KQ-1530 · SATIN/RAKE
Satin Rake
A length of satin lying in soft folds, shaded per pixel in one WebGL2 pass: meandering folds crisp in the crease and rounded on the ridge, a wrapped diffuse that keeps the dye rich in the troughs, and an anisotropic sheen whose lobe runs long along each fold, so the highlights streak down the ridges. The light is a lamp a little above the cloth that sweeps slowly back and forth on its own; the pointer takes it (where on springs.glide, how much on springs.drift) and raking it over a trough turns the trough's walls to the light, while a click sends one slow wave rolling out through the cloth, lifting and kinking every fold it crosses. One slider under the content steers the lamp from the keyboard and Enter sends a wave.
Arrow keys move the lamp across the satin; Enter sends a wave through it.
Fernworks Atelier
Duchess satin, cut to order
Fourteen dyes, finished by hand
lamp sweeping · 7 folds, sheen 0.70
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/satin-rake
Props
- children
- React.ReactNode
- default —
- The content on top of the satin: a headline, a product plate, a call to action.
- foldCount
- number
- default 7
- How many long folds lie across the cloth, 3 to 14.
- sheen
- number
- default 0.7
- How strongly the ridges streak with sheen, 0 (matte silk) to 1 (liquid satin).
- tint
- "cobalt" | "ivory" | "rose" | "jade" | string
- default "cobalt"
- The dye: a named satin or any CSS colour. Named dyes hold their lightness, so the cloth is the same on a light and a dark page.
- angle
- number
- default -28
- The direction the folds run, in degrees from horizontal (negative rises to the right).
- softness
- number
- default 0.55
- The folds' profile, 0 (pressed creases) to 1 (soft rolls).
- depth
- number
- default 0.6
- How deep the folds are, 0 (nearly flat) to 1 (deep drape).
- lampHeight
- number
- default 0.3
- How high the lamp hangs over the cloth, 0 (raking, deep shadows) to 1 (overhead, even light).
- reach
- number
- default 0.6
- The size of the lamp's pool of light, as a share of the box's diagonal, 0.2 to 1.
- sweep
- number
- default 9000
- One slow return trip of the unattended lamp, in ms.
- waveStrength
- number
- default 0.6
- How high a click's wave lifts the cloth, 0 (no waves) to 1.
- waveSpeed
- number
- default 180
- How fast a wave rolls out through the cloth, px per second.
- interactive
- boolean
- default true
- The pointer and keys steer the lamp, and a click sends a wave.
- paused
- boolean
- default false
- Hold the unattended lamp still; the hand and waves still move.
- seed
- number
- default 1
- Seeds the drape, so two lengths of satin never fold alike.
- dpr
- number
- default 2
- Cap on the canvas's device pixel ratio, at most 2.
- label
- string
- default "Steer the lamp"
- The accessible name of the lamp control.
- onWave
- (origin: { x: number; y: number }) => void
- default —
- Fires when a click or Enter sends a wave, with its origin as shares of the box (0 to 1).
- onActiveChange
- (active: boolean) => void
- default —
- Fires when the pointer or the keyboard takes the lamp (true) or hands it back to its sweep (false).
- className
- string
- default —
- Sizes and shapes the box; it fills its parent by default (h-full w-full).
| Prop | Type | Default | Description |
|---|---|---|---|
| children | React.ReactNode | — | The content on top of the satin: a headline, a product plate, a call to action. |
| foldCount | number | 7 | How many long folds lie across the cloth, 3 to 14. |
| sheen | number | 0.7 | How strongly the ridges streak with sheen, 0 (matte silk) to 1 (liquid satin). |
| tint | "cobalt" | "ivory" | "rose" | "jade" | string | "cobalt" | The dye: a named satin or any CSS colour. Named dyes hold their lightness, so the cloth is the same on a light and a dark page. |
| angle | number | -28 | The direction the folds run, in degrees from horizontal (negative rises to the right). |
| softness | number | 0.55 | The folds' profile, 0 (pressed creases) to 1 (soft rolls). |
| depth | number | 0.6 | How deep the folds are, 0 (nearly flat) to 1 (deep drape). |
| lampHeight | number | 0.3 | How high the lamp hangs over the cloth, 0 (raking, deep shadows) to 1 (overhead, even light). |
| reach | number | 0.6 | The size of the lamp's pool of light, as a share of the box's diagonal, 0.2 to 1. |
| sweep | number | 9000 | One slow return trip of the unattended lamp, in ms. |
| waveStrength | number | 0.6 | How high a click's wave lifts the cloth, 0 (no waves) to 1. |
| waveSpeed | number | 180 | How fast a wave rolls out through the cloth, px per second. |
| interactive | boolean | true | The pointer and keys steer the lamp, and a click sends a wave. |
| paused | boolean | false | Hold the unattended lamp still; the hand and waves still move. |
| seed | number | 1 | Seeds the drape, so two lengths of satin never fold alike. |
| dpr | number | 2 | Cap on the canvas's device pixel ratio, at most 2. |
| label | string | "Steer the lamp" | The accessible name of the lamp control. |
| onWave | (origin: { x: number; y: number }) => void | — | Fires when a click or Enter sends a wave, with its origin as shares of the box (0 to 1). |
| onActiveChange | (active: boolean) => void | — | Fires when the pointer or the keyboard takes the lamp (true) or hands it back to its sweep (false). |
| className | string | — | Sizes and shapes the box; it fills its parent by default (h-full w-full). |