KQ-1864 · LOTUS/BEADS
Lotus Beads
Beads of water on a waxy leaf, drawn per pixel in one WebGL2 pass as one smooth field, so two that come close join with a neck; each is shaded as water on a leaf that repels it, with the veined leaf beneath magnified and turned over, a dark rim, a sharp highlight, light gathered on the far side and the bright caustic it focuses into its own shadow. Dragged, a bead follows the finger 1:1 and rolls on with little friction when let go; nearing another it stretches a neck toward it, and past the merge distance the two snap into one larger bead, volume and momentum kept, that jiggles with the springs.recoil damping ratio and settles round, while a throw faster than splitSpeed splits it into droplets that fan out and roll apart. Every bead is a real button: Tab moves between them, arrow keys roll the focused one, Enter or Space flicks it so it splits, and focus stays with the bead you rolled through a merge.
Gaugeworks Botanic · after rain
Arrow keys roll the bead; Enter or Space flicks it so it splits. Roll one into another and they merge.
7 beads · drag one into another
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/lotus-beads
Props
- beads
- number
- default 7
- How many beads sit on the leaf at the start, 3 to 14.
- tension
- number
- default 0.6
- Surface tension, 0 (soft, flat beads that reach for each other from far off) to 1 (tight, round beads that hold apart until they touch).
- tint
- number
- default 0.2
- How much colour the water carries, 0 (clear) to 1 (deeply tinted).
- sound
- boolean
- default false
- Play the beads' sounds: a blup as two merge, a higher one as a bead splits.
- friction
- number
- default 0.35
- How soon a rolling bead stops, 0 (it glides on and on) to 1 (it stops short).
- splitSpeed
- number
- default 1100
- How fast a throw must be to split a bead into droplets, in px a second, 500 to 3000.
- leafColor
- string
- default "oklch(from var(--success) 0.58 calc(c * 0.62) h)"
- The leaf's colour: a pigment at a fixed lightness, so it reads the same in both themes.
- waterColor
- string
- default "var(--accent-bright)"
- The colour the water takes on as tint rises.
- lightAngle
- number
- default 225
- Where the light comes from, in degrees: 0 from the right, 90 from below, 225 from the upper left.
- interactive
- boolean
- default true
- Beads can be dragged, thrown and rolled with the keys.
- disabled
- boolean
- default false
- Shows the leaf but takes no input.
- seed
- number
- default 7
- Seeds where the beads sit and how big they are.
- dpr
- number
- default 2
- Cap on the canvas's device pixel ratio, at most 2.
- onMerge
- (beads: number) => void
- default —
- Fires when two beads merge, with how many beads are left.
- onSplit
- (pieces: number) => void
- default —
- Fires when a bead splits, with how many droplets it made.
- onBeadsChange
- (beads: number) => void
- default —
- Fires whenever the number of beads changes, with the new count.
- label
- string
- default "Water beads"
- Names the group of beads for assistive technology.
- className
- string
- default "h-56 w-full"
- Sizes the box.
| Prop | Type | Default | Description |
|---|---|---|---|
| beads | number | 7 | How many beads sit on the leaf at the start, 3 to 14. |
| tension | number | 0.6 | Surface tension, 0 (soft, flat beads that reach for each other from far off) to 1 (tight, round beads that hold apart until they touch). |
| tint | number | 0.2 | How much colour the water carries, 0 (clear) to 1 (deeply tinted). |
| sound | boolean | false | Play the beads' sounds: a blup as two merge, a higher one as a bead splits. |
| friction | number | 0.35 | How soon a rolling bead stops, 0 (it glides on and on) to 1 (it stops short). |
| splitSpeed | number | 1100 | How fast a throw must be to split a bead into droplets, in px a second, 500 to 3000. |
| leafColor | string | "oklch(from var(--success) 0.58 calc(c * 0.62) h)" | The leaf's colour: a pigment at a fixed lightness, so it reads the same in both themes. |
| waterColor | string | "var(--accent-bright)" | The colour the water takes on as tint rises. |
| lightAngle | number | 225 | Where the light comes from, in degrees: 0 from the right, 90 from below, 225 from the upper left. |
| interactive | boolean | true | Beads can be dragged, thrown and rolled with the keys. |
| disabled | boolean | false | Shows the leaf but takes no input. |
| seed | number | 7 | Seeds where the beads sit and how big they are. |
| dpr | number | 2 | Cap on the canvas's device pixel ratio, at most 2. |
| onMerge | (beads: number) => void | — | Fires when two beads merge, with how many beads are left. |
| onSplit | (pieces: number) => void | — | Fires when a bead splits, with how many droplets it made. |
| onBeadsChange | (beads: number) => void | — | Fires whenever the number of beads changes, with the new count. |
| label | string | "Water beads" | Names the group of beads for assistive technology. |
| className | string | "h-56 w-full" | Sizes the box. |