KQ-1584 · SERPENTINE/POP

Serpentine Pop

Paper streamers for any box: a moving pointer throws tight coils of coloured paper off the cursor, each popping out on the springs.recoil constants and flying on its throw while the strip pays out behind it, so the roll visibly unwinds in the air before the free paper flutters down, springing into ringlets from its curl memory and turning over to show its paler back. A click or tap fires a popper — a ring flashes, a dozen coils leave in an upward cone with a puff of tumbling flecks — and everything settles along the bottom edge in fixed 120 Hz Verlet physics. The box is one tab stop: Enter or Space fires a popper from an aim point on the bottom edge, and Left and Right move the aim.

KQ-1584 · SERPENTINE/POP

Fieldline · release 2.0

Fieldline 2.0 is live

Shared boards, offline sync and a calmer inbox.

Move the pointer to throw streamers. Click, tap, or press Enter to fire a popper; the Left and Right arrows aim it along the bottom edge.

move to throw streamers · click to pop

Install

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

$ pnpm dlx shadcn@latest add @kinetiq/serpentine-pop

Props

children
React.ReactNode
default —
The content the paper falls over (or behind, with layer="back"). It stays fully interactive.
palette
"party" | "sorbet" | "gilt" | "mono"
default "party"
The paper's colours: pigments at a fixed lightness, so a streamer looks the same on any page; mono follows the ink tokens.
colors
string[]
default —
Your own paper colours, any CSS colours (tokens included); replaces palette.
rate
number
default 5
How many streamers a moving pointer throws, at most, per second (1 to 12).
curl
number
default 0.6
How tightly the paper remembers its roll, 0 (straight ribbons) to 1 (tight ringlets).
stripWidth
number
default 6
Width of the paper strip, px.
streamerLength
number
default 130
Length of one streamer unrolled, px.
burst
number
default 12
Streamers a popper fires.
flecks
number
default 36
Paper flecks a popper puffs out with them.
gravity
number
default 1
How fast paper falls, 0.5 (floating) to 2 (heavy).
linger
number
default 10
Seconds settled paper lies on the floor before it fades; 0 keeps it until maxPaper is reached. The resting garland never lingers out.
maxPaper
number
default 40
Most streamers lying on the floor at once; past it the oldest fade.
scatter
number
default 6
Curled streamers already lying along the bottom edge at rest (fewer on a narrow box).
layer
"front" | "back"
default "front"
Draw the paper in front of the content or behind it.
paused
boolean
default false
Hold the paper where it is and stop throwing.
seed
number
default 7
Seeds the colours, curls and launch of every streamer.
dpr
number
default 2
Cap on the canvas's device pixel ratio, at most 2.
label
string
default "Party popper"
The popper's accessible name.
onPop
(count: number) => void
default —
Fires on every popper, with how many have been fired so far.
sound
boolean
default false
A pop and a paper crinkle when a popper fires (never on hover).
disabled
boolean
default false
No streamers, no poppers; the box is plain content and leaves the tab order.
className
string
default —
Sizes and shapes the box; it clips the paper (rounded corners included).