KQ-1420 · SILK/RIBBONS

Silk Ribbons

Silk ribbons stream from the pointer on one Canvas 2D layer: each is a chain of 32 links whose head rides the pointer on a spring blended between springs.drift and springs.snap, with every link following the one ahead at its length and held smooth by a bending spring, and the ribbons differ in length, stiffness and drag so a sweep fans them out. When a link catches up its overtaking motion turns sideways, so as the pointer stops the ribbons overshoot, wind into loops and unwind, and a press whips an S-wave down each chain; each ribbon tapers and twists, darkening edge-on and shifting colour between its two faces like iridescent silk. With nobody pointing they fly a slow figure-eight, keyboard focus sends them to circle the focused control, and Enter or Space on it whips them there.

KQ-1420 · SILK/RIBBONS

Fernworks · spring silks

Cut from the softest light.

in flight · move across or press Tab

Install

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

$ pnpm dlx shadcn@latest add @kinetiq/silk-ribbons

Props

children
React.ReactNode
default —
Content laid over the ribbons, such as a headline. It keeps every click, focus and key.
count
number
default 5
How many ribbons stream from the pointer, 1 to 8.
length
number
default 220
A ribbon's length in px; each varies around it by variance, and none outgrows 1.6× the box's shorter side.
colors
"opal" | "ember" | "tide" | "mono" | readonly string[]
default "opal"
The silk: a named palette from the theme tokens, or your own CSS colours the ribbons shift through along their length.
width
number
default 11
A ribbon's widest, just below the head, in px.
taper
number
default 0.85
How much a ribbon thins toward its tail, 0 (even) to 1 (a fine point).
stiffness
number
default 0.5
How tightly the heads follow and the silk holds its line, 0 (springs.drift, loose) to 1 (springs.snap, taut).
variance
number
default 0.5
How different the ribbons are from each other in length, stiffness and drag, 0 to 1.
curl
number
default 0.5
How much the ribbons wind into loops as they catch up, 0 to 1.
twist
number
default 1.5
Twists along each ribbon; where it turns edge-on it narrows and darkens.
sheen
number
default 0.6
A soft specular streak down faces turned to the light, 0 to 1.
spread
number
default 12
How wide the fan of heads is either side of the pointer, across its direction of travel, in px.
whip
boolean
default true
A press whips the ribbons, throwing a loop down each one.
ambient
boolean
default true
With nobody pointing, the ribbons fly a slow figure-eight through the box.
followFocus
boolean
default true
Keyboard focus on a control inside sends the ribbons to circle it.
interactive
boolean
default true
Follow the pointer.
paused
boolean
default false
Hold the ribbons still and stop the frame loop.
seed
number
default 5
Seed for every ribbon's length, spring, twist and colour.
dpr
number
default 2
The highest device pixel ratio the canvas draws at (never above 2).
onWhip
(point: { x: number; y: number }) => void
default —
Fires when a press (or Enter/Space on a control inside) whips the ribbons, with the point in the box.
className
string
default —
Classes for the box. Give it a height; children are laid out inside it.