KQ-1612 · SAND/FURROW
Sand Furrow
A picture buried under fine sand, the sand a heightfield simulated on a coarse grid and shaded per pixel in WebGL2 — directional light, grain, glints, short shadows cast into the furrow and damp edges where it thins. Drag to plough: a bowl-shaped plough cuts down toward the picture and heaps what it cuts onto a bank on each side, the banks round off at their angle of repose, and about a second later they slump back in at the settle pace until the sand lies smooth and the loop stops. Double-click, Enter or Space sweeps the tray clear with a broom front that carries a ridge off the edge; Backspace pours the sand back in; arrow keys plough from a marker.
Basinworks dig · trench 4
A floor under the dunes
buried · drag to plough a furrow
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/sand-furrow
Props
- src
- string
- default —
- The buried picture (same origin or CORS-enabled); defaults to a drawn tile mosaic.
- alt
- string
- default "A tile mosaic of a fish among waves"
- Describes the picture.
- grainSize
- number
- default 1.5
- Size of a grain of sand, 0.5 (silt) to 3 px (coarse).
- settle
- number
- default 0.45
- How quickly the banks slump back over the furrow, 0 (never) to 1.
- plough
- number
- default 24
- Width of the plough, 8 to 40 px.
- depth
- number
- default 0.9
- How much sand one pass moves, 0 to 1; 1 cuts down to the picture.
- light
- number
- default 135
- The direction the light comes from, in degrees (0 is from the right, 90 from above).
- sandColor
- string
- default "oklch(from var(--warn) 0.82 0.055 calc(h - 8))"
- The sand's colour; a pigment, the same in both themes.
- cleared / defaultCleared
- boolean
- default false
- The sand swept clear; controlled or initial.
- onClearedChange
- (cleared: boolean) => void
- default —
- Fires from the double-click or key that swept the sand clear or poured it back.
- onPlough
- (uncovered: number) => void
- default —
- Fires after each stroke, and again once the banks have settled, with the share of the picture uncovered, 0 to 1.
- sound
- boolean
- default false
- A swish as the plough moves and as the sand is swept or poured.
- interactive
- boolean
- default true
- Drag, double-click and the keys plough and sweep.
- seed
- number
- default 5
- Seeds the dunes and the grain.
- dpr
- number
- default 2
- Cap on the canvas's device pixel ratio, at most 2.
- label
- string
- default "Sand over a picture"
- The tray's accessible name; the picture and how much of it shows are added.
- className
- string
- default "h-56 w-full"
- Sizes the tray.
| Prop | Type | Default | Description |
|---|---|---|---|
| src | string | — | The buried picture (same origin or CORS-enabled); defaults to a drawn tile mosaic. |
| alt | string | "A tile mosaic of a fish among waves" | Describes the picture. |
| grainSize | number | 1.5 | Size of a grain of sand, 0.5 (silt) to 3 px (coarse). |
| settle | number | 0.45 | How quickly the banks slump back over the furrow, 0 (never) to 1. |
| plough | number | 24 | Width of the plough, 8 to 40 px. |
| depth | number | 0.9 | How much sand one pass moves, 0 to 1; 1 cuts down to the picture. |
| light | number | 135 | The direction the light comes from, in degrees (0 is from the right, 90 from above). |
| sandColor | string | "oklch(from var(--warn) 0.82 0.055 calc(h - 8))" | The sand's colour; a pigment, the same in both themes. |
| cleared / defaultCleared | boolean | false | The sand swept clear; controlled or initial. |
| onClearedChange | (cleared: boolean) => void | — | Fires from the double-click or key that swept the sand clear or poured it back. |
| onPlough | (uncovered: number) => void | — | Fires after each stroke, and again once the banks have settled, with the share of the picture uncovered, 0 to 1. |
| sound | boolean | false | A swish as the plough moves and as the sand is swept or poured. |
| interactive | boolean | true | Drag, double-click and the keys plough and sweep. |
| seed | number | 5 | Seeds the dunes and the grain. |
| dpr | number | 2 | Cap on the canvas's device pixel ratio, at most 2. |
| label | string | "Sand over a picture" | The tray's accessible name; the picture and how much of it shows are added. |
| className | string | "h-56 w-full" | Sizes the tray. |