KQ-1483 · PAINT/VORTEX

Paint Vortex

A background of colour bands twisted into a slow spiral and rendered as felt, with your content on top: a WebGL2 shader paints square blocks from a logarithmic spiral about the eye, its arms bent by slow noise and flowing gently inward, and felts every block with its own lightness, a pillowed edge, fibres and interleaving where two colours meet. The hand pulls the eye toward it, its place on springs.glide and the pull's presence on springs.drift; a press stirs the paint through a twist spring with springs.recoil's constants, so the arms wind tight, slosh back loose past rest and settle in two beats. Keyboard focus on a control inside pulls the eye there with a softer stir.

KQ-1483 · PAINT/VORTEX

Fernworks Felt

Dyed by hand, wound slow

Five colourways this season

dusk · twist 0.50 · turning

Install

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

$ pnpm dlx shadcn@latest add @kinetiq/paint-vortex

Props

children
React.ReactNode
default —
The content on top of the paint: a hero line, a heading, a call to action.
twist
number
default 0.5
How tightly the bands wind round the eye, 0 (loose arms) to 1 (a tight coil).
speed
number
default 0.5
How fast the bands flow in along the arms, 0 (still) to 2.
palette
"dusk" | "sage" | "clay" | "ink" | readonly string[]
default "dusk"
A named palette, or 3 to 6 CSS colours of your own, darkest first.
bands
number
default 7
Colour bands round a full turn, 3 to 12.
pixel
number
default 6
The felt's block size, px (2 to 16).
felt
number
default 0.5
How much the blocks felt: fibres, soft edges and mixing where colours meet, 0 to 1.
warp
number
default 0.35
How far the arms bend like paint rather than ruled curves, 0 to 1.
origin
readonly [number, number]
default [0.68, 0.5]
Where the eye rests, as fractions of the box's width and height.
interactive
boolean
default true
The pointer and keyboard focus pull the eye, and a press stirs the paint.
pointerStrength
number
default 0.6
How far the hand pulls the eye and how hard a press stirs, 0 to 1.
paused
boolean
default false
Hold the paint still; the hand still pulls and stirs it.
seed
number
default 1
Seeds the felt and the bend of the arms.
dpr
number
default 2
Cap on the canvas's device pixel ratio, at most 2.
onActiveChange
(active: boolean) => void
default —
Fires when the pointer or keyboard focus starts (true) or stops (false) pulling the eye.
className
string
default "h-full w-full"
Sizes and shapes the box.