Sine Wave Checkerboard
This script creates an animated retro “checker wave” background on any canvas — a glowing, rippling checkerboard with customizable colors, scroll direction, scanlines, glow, and other retro effects, all GPU-accelerated via WebGL for smooth performance. It also respects the user’s `prefers-reduced-motion` setting, automatically pausing the animation for those who prefer less movement.
Congratulations, You Found Us. Unfortunately.
Congratulations, You Found Us. Unfortunately.
Congratulations, You Found Us. Unfortunately.
Congratulations, You Found Us. Unfortunately.
Instructions
Pattern & Wave Shape
pattern (default: 'checker') Chooses the base layout style. 'checker' renders the classic alternating checkerboard where both the horizontal and vertical square phases are combined. 'vertical' renders only vertical stripes/columns — the vertical checker phase is ignored, so you get bar-like columns that still stretch with the wave.
squaresX (default: 16) How many squares fit across the width of the pattern box. Internally this determines the pixel size of a single square (squarePx = patternWidth / squaresX), and one full checker tile is two squares wide. Changing it triggers a re-layout. The example uses 19.
amplitude (default: 0.80) How strongly the wave distorts the grid. A wave value (0–1) computed from layered sines scales the horizontal spacing of the squares vertically, so rows of squares appear to swell and compress as the wave passes. 0 keeps the grid perfectly uniform; higher values create more dramatic swelling.
frequency (default: 3.2) The spatial frequency of the wave — how many wave cycles fit vertically across the pattern. Higher values produce more, tighter ripple bands; lower values produce one broad swell.
speed (default: 1.0) A global time multiplier applied to the animation clock. It affects the wave phase, hue cycling, and the glow pulse together. 2 makes everything run twice as fast; 0 effectively freezes the wave (though motion scrolling still advances on its own clock).
Motion & Scrolling
motion (default: 'left') The direction the pattern scrolls. Linear options: 'left', 'right', 'up', 'down'. Oscillating options: 'left-right' (swings horizontally), 'up-down' (swings vertically), and 'circle' (moves in an ellipse).
motionSpeed (default: 1.0) A multiplier on how fast the scroll/oscillation happens. For linear motions it scales the pixels-per-second; for oscillating motions it scales the angular speed of the sine/cosine phase.
scroll (default: 0.25) The base scroll rate, expressed as a fraction of one full checker tile period per second (before motionSpeed is applied). It's the "fine tune" of drift speed — smaller values slide the pattern more slowly.
oscRange (default: 1.5) Only meaningful for oscillating motions (left-right, up-down, circle). It's the oscillation amplitude measured in tile periods — how far the pattern swings back and forth before reversing. Clamped at 0 minimum.
scrollSync (default: false) When true, the pattern's motion is driven by the page's scroll position instead of time. Scrolling the page moves the checker wave; when you stop scrolling, the pattern holds still. Useful for parallax-style backgrounds.
scrollAmount (default: 0.25) The sensitivity of scroll-sync mode: a multiplier applied to the raw page scroll offset in pixels. Higher values make the pattern move further per pixel of page scroll.
scrollInvert (default: false) Flips the direction of the scroll-sync coupling, so scrolling down moves the pattern the opposite way.
Retro / CRT Effects
scanlines (default: 0.34) Intensity of CRT-style horizontal scanlines. Every Nth row of pixels is darkened by this fraction (0 disables). The example uses 1.00, which makes the scanline rows fully black.
scanlineSpacing (default: 3) The gap, in CSS pixels, between scanline rows. It's scaled by the canvas resolution and has a minimum of 2 device pixels so lines never merge into a solid darkening.
vignette (default: 0.85) Strength of the vignette — a darkening toward the edges/corners of the canvas, like an old tube screen. 0 disables it. (A negative value would invert it into a brightening white vignette, per the shader logic.)
Color & Glow
hueA (default: 320) The hue (0–360) of color A — the bright squares — when you rely on the built-in HSL colors. It's rendered with high saturation (~0.95) and lightness 0.60, so the default is a hot magenta/pink.
hueB (default: 215) The hue of color B — the dark squares — rendered with saturation 0.72 and very low lightness (0.08), so the default is a near-black blue.
hueCycle (default: 14) How many degrees per second both hues rotate over time. This makes colors slowly drift through the spectrum as the animation runs. Set 0 to lock colors in place.
colorA (default: null) An optional override for color A as any CSS color string (e.g. '#ff00aa', 'rgb(255,0,170)', 'orange'). It's parsed via a hidden 1×1 canvas. When set, it replaces the HSL-computed color entirely (and hue cycling no longer affects it).
colorB (default: null) Same as colorA, but for the dark squares.
glow (default: 0.22) Intensity of a pulsing glow on the squares, driven by the wave value squared — so the glow peaks where the wave crests and fades in the troughs. 0 disables it.
glowHue (default: 190) The hue of the glow color itself, independent of the square colors.
glowMode (default: 'bright') 'bright' adds a light glow color (lightness ~0.78) on top of the pattern, producing a luminous bloom-like sheen. 'dark' instead mixes toward a very dark color (lightness ~0.06), which makes crests read as dark pulses rather than bright ones.
Deformation & Distortion
deform (default: 'none') A geometric distortion applied to the pattern box. 'ripple' pushes pixels in and out radially with a traveling sine ring (an underwater/wobble effect). 'twist' rotates pixels around the center by an angle that fades with distance from center, creating a swirl.
deformAmount (default: 0.5) Strength of whichever deform mode is active. 0 neutralizes it.
warp (default: 0.0) A domain-warp: the grid coordinates themselves are bent by layered sine fields before sampling the checker pattern, making the whole grid wobble organically like a heat haze or liquid. 0 disables it.
Chromatic Aberration & Post-Processing
chroma (default: 0.0) Amount of chromatic aberration — the red and blue channels are sampled slightly offset from green, producing color fringing at edges. Requires re-evaluating the pattern three times per pixel, so it has a perf cost. 0 disables it.
chromaMode (default: 'radial') 'radial' offsets the channels outward from the pattern's center, with fringing growing quadratically toward the edges (lens-like). 'normal' (any other value) applies a uniform horizontal offset proportional to the pattern width.
noise (default: 0.0) Animated film-grain/static, added per pixel with a hash function and a per-frame random offset. Only available when the post-processing pipeline is functional (framebuffers supported). 0 disables it.
bloom (default: 0.0) Intensity of the bloom effect: bright areas are extracted, blurred (via a two-pass separable Gaussian on a quarter-resolution buffer, run twice at increasing radius), and added back over the scene so highlights bleed softly. 0 disables the whole bloom pipeline.
bloomThreshold (default: 0.55) The luminance cutoff that decides which pixels feed into the bloom. Only brightness above this threshold contributes. Lower values make more of the image bloom; higher values restrict it to the very brightest spots.
Runtime / Behavior
pixelRatio (default: 0) The device pixel ratio used for the canvas backing store. 0 means auto: use window.devicePixelRatio, capped at 2. Any positive value forces that ratio — e.g. 1 renders at CSS-pixel resolution (sharper workload halved on retina screens), 0.5 renders even smaller and upscales (a performance lever at the cost of softness). Changing it triggers a re-layout.
autostart (default: true) Whether the animation loop starts immediately when the effect is created. If false, the effect renders one static frame and waits for .start().
respectReducedMotion (default: true) Honors the user's OS/browser "reduce motion" preference (prefers-reduced-motion: reduce). When it matches, the animation auto-pauses and renders one static frame instead; when the preference changes back, it automatically resumes — unless the user manually called .stop(), in which case the manual pause wins.
onStateChange (default: null) An optional callback fired whenever the running state changes (start, stop, or auto-pause/resume from reduced motion). It receives { running: boolean, reducedMotion: boolean }. Exceptions thrown inside it are swallowed so they can't break the render loop.
For reference, the usage example at the bottom overrides several of these: a gentler wave (amplitude: 0.30, frequency: 1.6), upward motion with a faster cycle (motion: 'up', motionSpeed: 2.50), fully black scanlines (scanlines: 1.00), a strong additive cyan-ish glow (glow: 0.63, glowHue: 190, glowMode: 'bright'), a fixed 19-squares-across grid, and no bloom/chroma/noise/warp — a clean, glowing, upward-drifting checker wave.