KQ-1812 · ICE/CUBE
Ice Cube
A clear ice cube slides across a picture after the pointer on a per-frame glide spring, turning with the curve of its slide, and bends the picture per pixel in one WebGL2 pass: bevelled facets displace it, the top magnifies it through frosted cracks and a cloudy core, a cold highlight runs along the top edge and a pale shadow holds a bright caustic. Sliding melts it — it shrinks, rounds and softens and lays meltwater into a ping-pong field that bends the picture too before it dries — and a click drops a fresh cube that lands on springs.recoil with a splash. Keyboard focus on anything inside parks the cube on it, and a drop button that appears on focus takes Enter or Space.
Coldbrook · Iced
Cold brew, poured slow.
Steeped eighteen hours, served over clear ice.
cube 1 · slide it, click to drop another
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/ice-cube
Props
- children
- React.ReactNode
- default —
- Content laid over the picture: headlines, buttons, links. It stays fully interactive.
- image
- { src?: string; alt?: string }
- default —
- The picture under the cube, drawn cover-fit. Without a src, a cloth is drawn from the theme.
- pattern
- "gingham" | "tiles" | "linen"
- default "gingham"
- The drawn cloth when there is no src.
- melt
- number
- default 0.4
- How fast it melts as it slides, 0 (never) to 1 (a few seconds of sliding).
- facets
- number
- default 0.7
- Facet sharpness, 0 (soft, rounded sides) to 1 (flat facets with hard folds).
- trail
- number
- default 0.5
- How much meltwater it leaves and how long the trail takes to dry, 0 to 1.
- size
- number
- default 68
- The fresh cube's edge, px; it never exceeds a third of the box's height.
- refraction
- number
- default 0.6
- How hard the ice and the water bend the picture, 0 to 1.
- frost
- number
- default 0.5
- Frosted cracks and the cloudy core inside the ice, 0 to 1.
- tint
- string
- default "oklch(from var(--accent-bright) 0.9 0.05 calc(h - 40))"
- The colour of the ice, any CSS colour.
- rest
- { x: number; y: number }
- default { x: 0.74, y: 0.55 }
- Where the cube rests before anyone moves it, as fractions of the box.
- interactive
- boolean
- default true
- The pointer moves the cube and a click drops a fresh one.
- paused
- boolean
- default false
- Holds the cube and the water where they are.
- dpr
- number
- default 2
- Cap on the canvas's device pixel ratio, at most 2.
- sound
- boolean
- default false
- A clack when a fresh cube lands or the cube hits the frame. Off unless asked for.
- onDrop
- (count: number) => void
- default —
- Fires when a fresh cube is dropped, with how many cubes there have been.
- onMelt
- (left: number) => void
- default —
- Fires as the cube melts, with how much of it is left (1 to 0.2) in 5% steps.
- onMelted
- () => void
- default —
- Fires when the cube has melted into a puddle.
- dropLabel
- string
- default "Drop a fresh cube"
- The keyboard's drop button, shown on focus.
- disabled
- boolean
- default false
- Nothing moves the cube and the drop button is off.
- className
- string
- default "h-56 w-full"
- Sizes the box.
| Prop | Type | Default | Description |
|---|---|---|---|
| children | React.ReactNode | — | Content laid over the picture: headlines, buttons, links. It stays fully interactive. |
| image | { src?: string; alt?: string } | — | The picture under the cube, drawn cover-fit. Without a src, a cloth is drawn from the theme. |
| pattern | "gingham" | "tiles" | "linen" | "gingham" | The drawn cloth when there is no src. |
| melt | number | 0.4 | How fast it melts as it slides, 0 (never) to 1 (a few seconds of sliding). |
| facets | number | 0.7 | Facet sharpness, 0 (soft, rounded sides) to 1 (flat facets with hard folds). |
| trail | number | 0.5 | How much meltwater it leaves and how long the trail takes to dry, 0 to 1. |
| size | number | 68 | The fresh cube's edge, px; it never exceeds a third of the box's height. |
| refraction | number | 0.6 | How hard the ice and the water bend the picture, 0 to 1. |
| frost | number | 0.5 | Frosted cracks and the cloudy core inside the ice, 0 to 1. |
| tint | string | "oklch(from var(--accent-bright) 0.9 0.05 calc(h - 40))" | The colour of the ice, any CSS colour. |
| rest | { x: number; y: number } | { x: 0.74, y: 0.55 } | Where the cube rests before anyone moves it, as fractions of the box. |
| interactive | boolean | true | The pointer moves the cube and a click drops a fresh one. |
| paused | boolean | false | Holds the cube and the water where they are. |
| dpr | number | 2 | Cap on the canvas's device pixel ratio, at most 2. |
| sound | boolean | false | A clack when a fresh cube lands or the cube hits the frame. Off unless asked for. |
| onDrop | (count: number) => void | — | Fires when a fresh cube is dropped, with how many cubes there have been. |
| onMelt | (left: number) => void | — | Fires as the cube melts, with how much of it is left (1 to 0.2) in 5% steps. |
| onMelted | () => void | — | Fires when the cube has melted into a puddle. |
| dropLabel | string | "Drop a fresh cube" | The keyboard's drop button, shown on focus. |
| disabled | boolean | false | Nothing moves the cube and the drop button is off. |
| className | string | "h-56 w-full" | Sizes the box. |