KQ-1601 · RIVER/SCRIPT

River Script

A sentence floats down a winding river on a pale map, letter by letter along the centreline and turned to its heading, as an accordion: the spacing opens in the wide pools, where the letters drift across the stream on a slow eddy, and closes up tight in the narrows, while the sentence's speed follows continuity — slow through the pools, quick through the narrows — so one sentence breathes as it travels and loops from the mouth back to the source. Hovering the text eases the current to a stop on springs.drift and the line lies straight to be read; grabbing the river by a bank drags the nearest bend 1:1, re-splining the course live with the letters flowing along it, and a released bend settles on springs.snap with the release speed. Space holds and frees the current, and each bend is a vertical slider for the keyboard.

KQ-1601 · RIVER/SCRIPT

Coldbrook · river walk

Fells to the sea, 9 km

Down from the fells the water idles through the pools and races the narrows to the sea.

Space holds and frees the current. Each bend is a slider: Up and Down move it north and south. Drag the river to re-route it.

flowing · quick through the Gauge

Install

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

$ pnpm dlx shadcn@latest add @kinetiq/river-script

Props

children
string
default —
The sentence, as a string. Wins over text.
text
string
default —
The sentence, when it is not given as children. A line about the river by default.
meander
number
default 0.6
How far the river winds, 0 (a straight canal) to 1 (deep bends).
flowSpeed
number
default 38
The current's speed through an average reach, px/s; quicker in the narrows, slower in the pools.
width
number
default 30
The river's width at an average reach, px.
narrows
number
default 0.6
How much the width swings between pools and narrows, 0 (even) to 1.
bends
number
default 5
How many bends the course passes through, 3 to 7; a narrow map draws fewer (about one per 130 px) so the text never kinks.
course / defaultCourse
number[]
default all 0
Offsets the visitor (or host) has added to each bend, -1 to 1 of the half height, negative north. Controlled or initial.
onCourseChange
(course: number[]) => void
default —
Fires from the drag or key that re-routed the river, with every bend's offset.
paused / defaultPaused
boolean
default false
Whether the current is held. Controlled or initial.
onPausedChange
(paused: boolean) => void
default —
Fires when Space holds or frees the current.
hoverToRead
boolean
default true
Hovering the text slows the current to a stop so it can be read.
waterColor
string
default "color-mix(in oklab, var(--accent-bright) 18%, transparent)"
The water's colour.
bankColor
string
default "var(--ink-3)"
The banks' and contour hints' ink.
places
readonly [string, string]
default ["Fern Pool", "the Gauge"]
Names for the widest pool and the tightest narrows, set beside the river where there is room (and left out where there is none).
seed
number
default 3
Seeds the course and its pools, so two rivers never match.
height
number
default 200
The map's height, px. It fills its parent's width.
disabled
boolean
default false
No dragging and no bend keys; the current still runs.
label
string
default "River map"
The map's accessible name.
onFlowChange
(state: "flowing" | "reading" | "paused") => void
default —
The current's state: flowing, held by a hand over the text, or paused.
className
string
default —
Classes on the map; the text's size and colour inherit from here.