KQ-1896 · MONOSPACE/POND

Monospace Pond

A paragraph of monospaced text that is also the surface of a pond: the words stay real, selectable DOM text, and a canvas redraws every character from a glyph atlas where the page laid it out, riding a damped wave equation simulated one character wide with walls that reflect. Swells shoulder the letters toward their crests and lift them, the crests turn them into denser glyphs from the ramp in the crest's ink while troughs thin the ink, and when the energy is gone the original text is drawn exactly again. The pointer drags a trough and its wake through the water as hard as it is quick; a click, a tap, Enter or keyboard focus drops a stone that falls, dents the surface and rings outward under a hairline ring while a crown of droplet glyphs hops out, and the arrow keys move a fingertip that glides between characters on springs.snap while Escape smooths the water away on springs.glide.

KQ-1896 · MONOSPACE/POND

Coldbrook Reservoir · 06:10

No wind yet. The water holds the far bank so exactly that the pines grow down as well as up, and a heron stands on its own reflection like a pin through a page. Touch it, and the page remembers it is water.

Arrow keys move a fingertip through the text and stir the water as it goes; Enter drops a stone; Escape calms the water.

still water · move to stir, click to drop a stone

Install

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

$ pnpm dlx shadcn@latest add @kinetiq/monospace-pond

Props

text
string
default a short reservoir note (MONOSPACE_POND_TEXT)
The paragraph. Line breaks in it are kept; spaces wrap as usual.
as
"p" | "div" | "blockquote"
default "p"
The element the paragraph renders as.
damping
number
default 0.35
How quickly the water settles, 0 (rings for seconds and rebounds off the edges) to 1 (settles like syrup).
ramp
"ascii" | "blocks" | "dots" | "marks" | string
default "ascii"
The glyphs a crest turns letters into, light to dense: a named ramp or your own string.
depth
number
default 0.5
How deep the pond is, 0 to 1: deeper water carries waves faster and bends the letters further.
stir
number
default 0.6
How hard the pointer drags waves through the text, 0 to 1.
radius
number
default 2.5
The pointer's footprint in characters, 1 to 6.
stone
number
default 0.7
How big a dropped stone is, 0 to 1.
crest
number
default 0.5
How readily crests swap letters for ramp glyphs, 0 (only the highest) to 1 (any swell).
foam
boolean
default true
Word gaps froth with the lightest glyph on strong crests, so a ring reads across them.
ring
boolean
default true
A hairline ring rides out from every stone with the wavefront.
color
string
default "var(--foreground)"
The ink of the text at rest.
crestColor
string
default "var(--accent-bright)"
The ink the crests, the foam and the ring mix toward.
interactive
boolean
default true
The pointer, touch and keyboard disturb the water.
paused
boolean
default false
Freeze the water where it is; it ignores the hand until unpaused.
intro
boolean
default true
One stone drops by itself the first time the paragraph is seen.
seed
number
default 1
Where the intro stone falls and how a stone's droplets fly.
dpr
number
default 2
Cap on the canvas's device pixel ratio, at most 2.
label
string
default "Stir the water"
The pond's accessible name.
onStone
(at: { line: number; column: number }) => void
default —
Fires for every stone a click, a tap, Enter or keyboard focus drops, with its line and column (from 0).
onCalmChange
(calm: boolean) => void
default —
Fires when the water starts to move (false) and when it is still again (true).
className
string
default —
Sizes the box and sets its type: the font size is inherited.