KQ-1531 · THREAD/HOURGLASS

Thread Hourglass

A string sculpture in Canvas 2D: two glowing hoops, one above the other, strung with straight threads and seen from a little above with real perspective. Twist the top hoop and every thread leans the same way, so together they draw a hyperboloid — a curve made of nothing but straight lines — whose waist narrows into a band of light where the threads gather. A sideways drag twists it 1:1 with the hoop's front bead under the finger and rubber-bands past the limit; release and it unwinds on springs.snap from the release's speed, overshooting straight once. It is a slider: arrow keys twist it a step at a time on the same spring and Escape, Enter or Space let go.

KQ-1531 · THREAD/HOURGLASS

Drag sideways, or use the arrow keys, to twist the top hoop; Escape or Enter lets it go.

Waylight · breathing room

Twist in, let go out

at rest · 48 threads, twist up to 150°

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/thread-hourglass

Props

threadCount
number
default 48
How many threads string the two hoops, 8 to 120.
twist
number
default 150
How far the top hoop can be twisted either way, in degrees, 30 to 178; near 180 the waist closes to a point.
glow
number
default 0.6
The threads' bloom and the light at the waist, 0 (fine lines only) to 1.
color
string
default "var(--accent-bright)"
The threads' colour.
hoopColor
string
default —
The hoops' colour; by default the thread colour mixed toward the foreground.
tilt
number
default 20
How far we look down on the hoops, in degrees, 0 (side on) to 40.
spin
number
default 6
The sculpture's slow turn about its axis, degrees per second, 0 to 40.
step
number
default 15
Degrees per arrow key.
bounce
number
default 0.35
How far the hoop overshoots straight as it unwinds, 0 (none) to 1; the default is the house snap spring.
springBack
boolean
default true
Let go and the hoop unwinds to straight; off, it stays where it is left.
paused
boolean
default false
Hold the slow turn; twisting still works.
disabled
boolean
default false
No twisting by hand or key.
dpr
number
default 2
Cap on the canvas's device pixel ratio, at most 2.
label
string
default "Twist the top hoop"
The accessible name of the twist control.
onTwistChange
(degrees: number) => void
default —
Fires as a hand or key sets the twist, in whole degrees (positive to the right), and with the resting twist on release.
onHoldChange
(holding: boolean) => void
default —
Fires when a drag or the keyboard takes hold of the top hoop (true) and lets go (false).
className
string
default —
Sizes the box; it fills its parent by default (h-full w-full).