KQ-1526 · MOIRE/BAND

Moire Band

Two ribbons of fine parallel wires drift and turn over across the box, drawn per pixel in one WebGL2 pass with optional content above, and wherever they overlap broad interference fringes bloom and slide far faster than the wires move. The fringes are computed, not aliased: each grating is a short Fourier series of its wire and their product is expanded into sum and difference terms, each filtered by its own frequency, so the moire survives at any scale. The pointer sets the twist on a curve that is fine near the middle (calm wide bands) and coarse at the ends (racing fringes), followed on springs.glide; the first sighting brings the twist in from wider on springs.drift. It is a slider: arrows, Page Up and Down, Home and End.

KQ-1526 · MOIRE/BAND

Waylight Optics

Light, measured twice

twist 6.0° · steady fringes

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/moire-band

Props

children
React.ReactNode
default —
The content above the ribbons: a hero line, a heading, a call to action.
wireCount
number
default 40
Wires in each ribbon, 8 to 120.
spacing
number
default 3.5
Distance between neighbouring wires, in px; ribbons wider than 80% of the box tighten to fit.
twist
number
default 6
The twist between the two ribbons, in degrees, where the band starts; changing it moves the band there.
value
number
default —
The twist when you control it, in degrees; pair with `onValueChange`.
onValueChange
(degrees: number) => void
default —
Fires from the pointer or the keys that turned the twist, with the new twist in degrees.
maxTwist
number
default 24
The furthest the pointer or keys can turn the twist either way, in degrees.
drift
number
default 0.35
How restless the ribbons are, 0 (still) to 1.
roll
number
default 0.5
How far each ribbon turns over as it runs, 0 (flat) to 1 (edge-on at its narrowest).
wireWidth
number
default 0.4
A wire's width as a share of the spacing, 0.15 to 0.7.
tint
string | readonly [string, string]
default ["var(--foreground)", "var(--accent-bright)"]
The wires' colour, or one colour for each ribbon, as CSS colours.
interactive
boolean
default true
The pointer and keys turn the twist.
intro
boolean
default true
The first time the band comes into view, the twist arrives from wider so the fringes spread into calm bands.
paused
boolean
default false
Hold the ribbons still; the twist still turns.
label
string
default "Ribbon twist"
Names the twist control for assistive technology.
seed
number
default 2
Seeds the ribbons' curves and turns, so two bands never match.
dpr
number
default 2
Cap on the canvas's device pixel ratio, at most 2.
disabled
boolean
default false
Shows the band but takes no input.
className
string
default "h-full w-full"
Sizes and shapes the box.