2 patterns-psychedelic-Scanline-Shift
Balazs Horvath edited this page 2026-04-18 11:13:10 +02:00

Scanline Shift

DMA-style horizontal scanline displacement with sine wave modulation, inspired by Earthbound's battle backgrounds.

Mathematical Formula


\text{shift} = \sin(y \cdot k + \omega t) \cdot \text{max}_{\text{shift}}

\text{shifted} = (x - \text{shift}) \bmod \text{width}

Where:

  • y is vertical position
  • k is spatial frequency
  • \omega is temporal frequency
  • \text{max}_{\text{shift}} is maximum pixel displacement

How It Works

This pattern simulates HDMA (Horizontal Direct Memory Access) effects by shifting each horizontal row based on its vertical position. The shift amount follows a sine wave pattern, creating a characteristic wavy distortion.

Implementation

import torch
import numpy as np

width, height = 512, 512
frames = []

for t in range(30):
    # Create base gradient
    x = torch.linspace(0, 2*torch.pi, width)
    y = torch.linspace(0, 2*torch.pi, height)
    X, Y = torch.meshgrid(x, y, indexing='ij')
    
    # DMA-style scanline shift
    # Each row is shifted based on its vertical position and time
    shift_amount = torch.sin(Y * 2 + t/5) * 20  # Shift up to 20 pixels
    
    # Apply shift using roll
    shifted = torch.roll(X, shift_amount.int(), dims=1)
    
    # Color with pleasing palette
    r = torch.sin(shifted + t/10)
    g = torch.sin(shifted + t/10 + 2*torch.pi/3)
    b = torch.sin(shifted + t/10 + 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)  # [30, H, W, 3]

Line-by-Line Explanation

shift_amount = torch.sin(Y * 2 + t/5) * 20

Calculates horizontal shift for each row based on vertical position and time.

shifted = torch.roll(X, shift_amount.int(), dims=1)

Applies the shift by rolling the X coordinate array horizontally.

r = torch.sin(shifted + t/10)
g = torch.sin(shifted + t/10 + 2*torch.pi/3)
b = torch.sin(shifted + t/10 + 4*torch.pi/3)

Color cycling with 120° phase shifts for smooth transitions.

Customization

Change Shift Amount

shift_amount = torch.sin(Y * 2 + t/5) * 40  # Larger shift

Change Speed

shift_amount = torch.sin(Y * 2 + t/10) * 20  # Slower

Change Spatial Frequency

shift_amount = torch.sin(Y * 4 + t/5) * 20  # More variation

References