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