Table of Contents
Kaleidoscope Loop
Symmetrical kaleidoscope with rotation that loops perfectly over 600 frames.
Loop Guarantee
Pattern (50 frames), color (40 frames), and rotation (600 frames) ensure perfect loop.
Mathematical Formula
\theta' = |(\theta \cdot \frac{4}{\pi}) \bmod 2 - 1| \cdot \frac{\pi}{4}
X' = R \cdot \cos(\theta')
Y' = R \cdot \sin(\theta')
\text{pattern} = \sin(X' \cdot k + \frac{2\pi t}{50}) \cdot \cos(Y' \cdot k + \frac{2\pi t}{50})
\text{rotated} = \text{rotate}(\text{pattern}, \frac{2\pi t}{600})
Where:
- Pattern period: 50 frames
- Color period: 40 frames
- Rotation: completes
360^\circover 600 frames
How It Works
This pattern creates a kaleidoscope effect by:
- Convert to polar coordinates
- Apply symmetry transformation (8-fold)
- Generate pattern on symmetrical coordinates
- Apply rotation
- Map to color with time-based cycling
Implementation
import torch
width, height = 512, 512
fps = 60
duration = 10
total_frames = fps * duration
frames = []
for t in range(total_frames):
x = torch.linspace(-1, 1, width)
y = torch.linspace(-1, 1, height)
X, Y = torch.meshgrid(x, y, indexing='ij')
# Convert to polar
R = torch.sqrt(X**2 + Y**2)
theta = torch.atan2(Y, X)
# Create 8-fold symmetry
theta_sym = torch.abs((theta * 4 / torch.pi) % 2 - 1) * torch.pi / 4
# Convert back to cartesian for pattern
X_sym = R * torch.cos(theta_sym)
Y_sym = R * torch.sin(theta_sym)
# Rotating pattern with 50-frame period
pattern = torch.sin(X_sym * 10 + t * (2*torch.pi/50)) * torch.cos(Y_sym * 10 + t * (2*torch.pi/50))
# Complete rotation over 600 frames
rotation = t * (2*torch.pi / total_frames)
X_rot = X * torch.cos(rotation) - Y * torch.sin(rotation)
Y_rot = X * torch.sin(rotation) + Y * torch.cos(rotation)
# Apply pattern to rotated coordinates
final_pattern = torch.sin(X_rot * 5 + pattern) * torch.cos(Y_rot * 5 + pattern)
# Color mapping with 40-frame period
r = torch.sin(final_pattern + t * (2*torch.pi/40))
g = torch.sin(final_pattern + t * (2*torch.pi/40) + 2*torch.pi/3)
b = torch.sin(final_pattern + t * (2*torch.pi/40) + 4*torch.pi/3)
rgb = torch.stack([r, g, b], dim=-1)
rgb = ((rgb + 1) / 2).clamp(0, 1)
frames.append(rgb)
output_image = torch.stack(frames, dim=0)
Line-by-Line Explanation
theta_sym = torch.abs((theta * 4 / torch.pi) % 2 - 1) * torch.pi / 4
Creates 8-fold symmetry:
theta * 4/π: Scales anglemod 2: Wraps to [0, 2)- 1: Centers around 0abs(): Creates mirror symmetry* π/4: Rescales to [0, π/4]
This maps any angle to one of 8 symmetric segments.
X_sym = R * torch.cos(theta_sym)
Y_sym = R * torch.sin(theta_sym)
Converts symmetrical angle back to Cartesian coordinates for pattern generation.
pattern = torch.sin(X_sym * 10 + t * (2*torch.pi/50)) * torch.cos(Y_sym * 10 + t * (2*torch.pi/50))
Generates pattern on symmetrical coordinates with 50-frame period.
rotation = t * (2*torch.pi / total_frames)
Rotation angle increases from 0 to 2π over 600 frames.
X_rot = X * torch.cos(rotation) - Y * torch.sin(rotation)
Y_rot = X * torch.sin(rotation) + Y * torch.cos(rotation)
Standard 2D rotation matrix applied to coordinates.
final_pattern = torch.sin(X_rot * 5 + pattern) * torch.cos(Y_rot * 5 + pattern)
Applies pattern to rotated coordinates. The pattern itself becomes part of the coordinate transformation.
Mathematical Insight
8-Fold Symmetry
The symmetry transformation maps any angle θ to one of 8 segments:
- Segment 0: [0, π/4]
- Segment 1: [π/4, π/2]
- ...
- Segment 7: [7π/4, 2π]
All angles are mirrored into segment 0, creating 8-fold rotational symmetry.
Why This Creates Kaleidoscope
By mapping all angles to one segment and then generating a pattern, the pattern is automatically replicated 8 times around the circle. The rotation then animates the entire kaleidoscope.
Customization
Different Symmetry
# 6-fold symmetry
theta_sym = torch.abs((theta * 3 / torch.pi) % 2 - 1) * torch.pi / 3
No Pattern in Rotation
final_pattern = torch.sin(X_rot * 5) * torch.cos(Y_rot * 5)
Different Rotation Speed
rotation = t * (4*torch.pi / total_frames) # 2 full revolutions
Performance Notes
- Trigonometric functions are expensive
- Symmetry transformation is fast
- Rotation is vectorized
Loop Verification
# After generating frames
first_frame = output_image[0]
last_frame = output_image[-1]
diff = torch.abs(first_frame - last_frame).max()
print(f"Max difference: {diff.item()}")
# Should be close to 0