KQ-1596 · RING/SHEPHERD
Ring Shepherd
A close look down onto a planetary ring system in Canvas 2D: bands of fine dust bowed round a planet below the frame, dark gaps and thin ringlets between them, shearing past slowly as rings do seen from a moon's orbit. The pointer is a small moon on springs.snap that pushes every point of dust it passes out of a lane, and because each point then rings about its new orbit at the epicyclic frequency while the shear carries it along, both edges of the lane raise scalloped waves and bright crests that trail opposite ways and smooth out behind it as the lane closes. It is a slider for where the moon is: arrow keys steer it, Escape gives it back its own orbit, and with no pointer it circles the field on its own.
Arrow keys steer the moon through the rings, Shift for bigger steps; Escape gives it back its own orbit.
Basinworks · ring survey
The Coldbrook rings
on its own orbit · moon in a gap
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/ring-shepherd
Props
- ringDensity
- number
- default 0.6
- How thickly the dust lies in the bands, 0.1 (a sparse scatter) to 1 (dense, opaque rings).
- gapWidth
- number
- default 0.4
- Width of the dark gaps between the bands, 0 (bands touching) to 1 (wide divisions).
- wake
- number
- default 0.6
- How hard the moon pushes and how long its wake rings, 0 (a light touch that heals at once) to 1 (a wide lane with long scallops).
- bands
- number
- default 6
- How many bands of dust cross the frame, 3 to 9.
- color
- string
- default "oklch(from var(--warn) 0.88 0.03 h)"
- The dust's colour: pale ice from the warn token's hue. Drawn as light on a dark page and as a darkened ink on a light one.
- moonColor
- string
- default "oklch(from var(--warn) 0.8 0.05 h)"
- The moon's colour, a pigment that reads on both themes; it is lit from the upper left.
- moonSize
- number
- default 6
- The moon's radius, px, 3 to 14. The lane it clears scales with it.
- shear
- number
- default 1
- How fast the rings shear past, 0 (held) to 2. The middle of the frame rides with the moon; dust inside and outside it drifts opposite ways.
- curvature
- number
- default 0.5
- How far the bands bow, as if the planet sat below the frame, 0 (straight) to 1.
- autopilot
- boolean
- default true
- With no pointer and no keys, the moon circles the field on its own.
- step
- number
- default 18
- Pixels per arrow key; Shift moves three times as far.
- interactive
- boolean
- default true
- The pointer and keys steer the moon.
- disabled
- boolean
- default false
- No steering by pointer or key; the control leaves the tab order.
- paused
- boolean
- default false
- Hold the shear and the moon's own orbit; steering still parts the dust.
- seed
- number
- default 1
- Seeds the band layout and the dust's grain, so two ring systems never match.
- dpr
- number
- default 2
- Cap on the canvas's device pixel ratio, at most 2.
- label
- string
- default "Steer the moon"
- The accessible name of the moon's control.
- onBandChange
- (band: number | null) => void
- default —
- The band under the moon (0 is the outermost), or null while it crosses a gap. Fires on change, and once when it first reads.
- onSteerChange
- (steering: boolean) => void
- default —
- The visitor took the moon (true) or gave it back to its own orbit (false).
- className
- string
- default —
- Sizes the box; it fills its parent by default (h-full w-full).
| Prop | Type | Default | Description |
|---|---|---|---|
| ringDensity | number | 0.6 | How thickly the dust lies in the bands, 0.1 (a sparse scatter) to 1 (dense, opaque rings). |
| gapWidth | number | 0.4 | Width of the dark gaps between the bands, 0 (bands touching) to 1 (wide divisions). |
| wake | number | 0.6 | How hard the moon pushes and how long its wake rings, 0 (a light touch that heals at once) to 1 (a wide lane with long scallops). |
| bands | number | 6 | How many bands of dust cross the frame, 3 to 9. |
| color | string | "oklch(from var(--warn) 0.88 0.03 h)" | The dust's colour: pale ice from the warn token's hue. Drawn as light on a dark page and as a darkened ink on a light one. |
| moonColor | string | "oklch(from var(--warn) 0.8 0.05 h)" | The moon's colour, a pigment that reads on both themes; it is lit from the upper left. |
| moonSize | number | 6 | The moon's radius, px, 3 to 14. The lane it clears scales with it. |
| shear | number | 1 | How fast the rings shear past, 0 (held) to 2. The middle of the frame rides with the moon; dust inside and outside it drifts opposite ways. |
| curvature | number | 0.5 | How far the bands bow, as if the planet sat below the frame, 0 (straight) to 1. |
| autopilot | boolean | true | With no pointer and no keys, the moon circles the field on its own. |
| step | number | 18 | Pixels per arrow key; Shift moves three times as far. |
| interactive | boolean | true | The pointer and keys steer the moon. |
| disabled | boolean | false | No steering by pointer or key; the control leaves the tab order. |
| paused | boolean | false | Hold the shear and the moon's own orbit; steering still parts the dust. |
| seed | number | 1 | Seeds the band layout and the dust's grain, so two ring systems never match. |
| dpr | number | 2 | Cap on the canvas's device pixel ratio, at most 2. |
| label | string | "Steer the moon" | The accessible name of the moon's control. |
| onBandChange | (band: number | null) => void | — | The band under the moon (0 is the outermost), or null while it crosses a gap. Fires on change, and once when it first reads. |
| onSteerChange | (steering: boolean) => void | — | The visitor took the moon (true) or gave it back to its own orbit (false). |
| className | string | — | Sizes the box; it fills its parent by default (h-full w-full). |