Table of Contents
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Moiré Interference
Creates interference patterns through overlapping wave patterns, a common technique in retro psychedelic effects.
Mathematical Formula
I = \sin(k_1 x + \omega_1 t) \times \sin(k_2 y + \omega_2 t) \times \sin(k_3 (x+y) + \omega_3 t)
Where:
k_1, k_2, k_3are spatial frequencies\omega_1, \omega_2, \omega_3are temporal frequencies- Multiplication creates interference (not addition)
flowchart TD
A[Create coordinate grids] --> B[Generate wave 1]
A --> C[Generate wave 2]
A --> D[Generate wave 3]
B --> E[Multiply all waves]
C --> E
D --> E
E --> F[High contrast interference]
F --> G[Roll for color separation]
G --> H[RGB channels with phase shifts]
H --> I[Output animation]
How It Works
Moiré patterns occur when two or more regular patterns overlap. Unlike additive superposition (which averages waves), multiplicative interference creates:
- High contrast where waves align
- Dark regions where waves cancel
- Complex geometric patterns
Implementation
import torch
width, height = 512, 512
frames = []
for t in range(30):
x = torch.linspace(0, 10*torch.pi, width)
y = torch.linspace(0, 10*torch.pi, height)
X, Y = torch.meshgrid(x, y, indexing='ij')
# Create two overlapping wave patterns
wave1 = torch.sin(X * 2 + t/5)
wave2 = torch.sin(Y * 2 + t/4)
wave3 = torch.sin((X + Y) * 2 + t/6)
# Interference creates moiré pattern
interference = wave1 * wave2 * wave3
# High-contrast psychedelic palette
r = torch.abs(interference)
g = torch.abs(torch.roll(interference, 1, dims=0))
b = torch.abs(torch.roll(interference, 1, dims=1))
rgb = torch.stack([r, g, b], dim=-1).clamp(0, 1)
frames.append(rgb)
output_image = torch.stack(frames, dim=0)
Line-by-Line Explanation
x = torch.linspace(0, 10*torch.pi, width)
y = torch.linspace(0, 10*torch.pi, height)
Coordinates range from 0 to 10π, creating 5 full cycles of sine waves across the image.
wave1 = torch.sin(X * 2 + t/5)
Horizontal wave with frequency 2 (2 cycles per π units).
wave2 = torch.sin(Y * 2 + t/4)
Vertical wave with same frequency but different temporal speed.
wave3 = torch.sin((X + Y) * 2 + t/6)
Diagonal wave (sum of coordinates) with its own temporal speed.
interference = wave1 * wave2 * wave3
Multiplication creates interference. Unlike addition, this creates high-contrast patterns:
- Where all three are positive: bright
- Where any is negative: dark
- Complex geometric regions form
r = torch.abs(interference)
Red channel shows absolute interference (all positive).
g = torch.abs(torch.roll(interference, 1, dims=0))
Green channel shows interference shifted down by 1 pixel. This creates color separation.
b = torch.abs(torch.roll(interference, 1, dims=1))
Blue channel shows interference shifted right by 1 pixel. Different shift direction creates more color variation.
Mathematical Insight
Multiplication vs Addition
Additive superposition:
I = sin(x) + sin(y)
- Range: [-2, 2]
- Smooth transitions
- Averages values
Multiplicative interference:
I = sin(x) × sin(y)
- Range: [-1, 1]
- Sharp transitions
- High contrast regions
Multiplication creates moiré patterns because the product of oscillating functions has complex zero-crossings.
Why Roll for Color?
g = torch.abs(torch.roll(interference, 1, dims=0))
Shifting the interference pattern by 1 pixel before taking the absolute value creates phase differences between color channels. This:
- Separates colors spatially
- Creates rainbow-like effects
- Adds visual interest
Customization
More Waves
wave4 = torch.sin((X - Y) * 2 + t/7)
interference = wave1 * wave2 * wave3 * wave4
Different Frequencies
wave1 = torch.sin(X * 3 + t/5) # Tighter pattern
wave2 = torch.sin(Y * 2 + t/4)
Additive Instead of Multiplicative
interference = wave1 + wave2 + wave3 # Softer, less contrast
No Color Shifting
r = torch.abs(interference)
g = torch.abs(interference)
b = torch.abs(interference)
Performance Notes
- All operations are vectorized
torch.roll()is efficient- No expensive filtering