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.
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.
| Prop | Type | Default | Description |
|---|---|---|---|
| children | string | — | The sentence, as a string. Wins over text. |
| text | string | — | The sentence, when it is not given as children. A line about the river by default. |
| meander | number | 0.6 | How far the river winds, 0 (a straight canal) to 1 (deep bends). |
| flowSpeed | number | 38 | The current's speed through an average reach, px/s; quicker in the narrows, slower in the pools. |
| width | number | 30 | The river's width at an average reach, px. |
| narrows | number | 0.6 | How much the width swings between pools and narrows, 0 (even) to 1. |
| bends | number | 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[] | 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 | — | Fires from the drag or key that re-routed the river, with every bend's offset. |
| paused / defaultPaused | boolean | false | Whether the current is held. Controlled or initial. |
| onPausedChange | (paused: boolean) => void | — | Fires when Space holds or frees the current. |
| hoverToRead | boolean | true | Hovering the text slows the current to a stop so it can be read. |
| waterColor | string | "color-mix(in oklab, var(--accent-bright) 18%, transparent)" | The water's colour. |
| bankColor | string | "var(--ink-3)" | The banks' and contour hints' ink. |
| places | readonly [string, string] | ["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 | 3 | Seeds the course and its pools, so two rivers never match. |
| height | number | 200 | The map's height, px. It fills its parent's width. |
| disabled | boolean | false | No dragging and no bend keys; the current still runs. |
| label | string | "River map" | The map's accessible name. |
| onFlowChange | (state: "flowing" | "reading" | "paused") => void | — | The current's state: flowing, held by a hand over the text, or paused. |
| className | string | — | Classes on the map; the text's size and colour inherit from here. |