KQ-1536 · BEACH/SCRAWL

Beach Scrawl

A shoreline seen from above and drawn in characters — swell glyphs for the sea, sparse dots for dry sand, denser marks for wet — where an unseen hand writes a message in single-stroke capitals, hopping pen-up between strokes on the springs.flick constants, and waves run up on a decelerating ballistic curve to a reach that varies along the shore, spread a foam lace at the top of their run and drain away. Every water front that crosses a groove scours it and spreads it into its neighbours, so each wave leaves the writing softer until the tide has taken it and the hand writes again. Drag to press your own grooves into the sand; focus the beach and type, and the hand writes your letters at a caret (Backspace rakes the last one smooth, Enter smooths the line).

KQ-1536 · BEACH/SCRAWL

Waylight Inn · Coldbrook Bay

Low tide at 16:40. Rooms from 140.

Type to write in the sand, or drag to draw. Waves wash it away.

writing in the sand · “LOW TIDE”

Install

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

$ pnpm dlx shadcn@latest add @kinetiq/beach-scrawl

Props

message
string
default "Low tide"
What the unseen hand writes, in single-stroke capitals (A–Z, 0–9, . - ' & +). Empty for none.
loop
boolean
default true
Write the message again once the tide has taken it.
waveRate
number
default 7
How often the tide comes, in waves a minute (1 to 30).
charSet
"shore" | "dots" | "hatch"
default "shore"
The glyph vocabulary for sand, water and foam.
wetness
number
default 0.5
How far the swash runs up the beach and how long the sand stays dark, 0 to 1.
erosion
number
default 0.5
How much each wave takes from the writing, 0 (nothing) to 1 (about two waves).
writeSpeed
number
default 2.5
How fast the hand writes, in letters a second.
cell
number | "auto"
default "auto"
Glyph size in px (6 to 18), or "auto": 10 on a wide beach down to 7 on a phone, so a long word still has enough cells to read.
brush
number
default 7
Width of a pressed stroke in px; the hand's stick narrows for small letters.
sandColor
string
default "color-mix(in oklab, var(--warn) 45%, var(--ink-3))"
Sand ink, any CSS colour. Wet sand mixes it toward the sea; grooves toward the foreground.
seaColor
string
default "var(--accent-bright)"
Sea ink, any CSS colour.
foamColor
string
default "color-mix(in oklab, var(--accent-bright) 30%, var(--foreground))"
Foam ink, any CSS colour.
interactive
boolean
default true
Let the visitor write: drag to draw, type to write.
trigger
"mount" | "inView"
default "inView"
When the hand starts writing: at mount, or the first time the beach is on screen.
paused
boolean
default false
Hold the tide and the hand where they are.
seed
number
default 1
Seeds the sand, the waves and their reach.
dpr
number
default 2
Cap on the canvas's device pixel ratio, at most 2.
onWritten
(text: string) => void
default —
Fires when the hand finishes writing the message, with the text it wrote.
onWashed
() => void
default —
Fires when the tide has taken every mark from the sand.
onWave
(count: number) => void
default —
Fires as each wave starts up the beach, with the count so far.
label
string
default "Beach"
The beach's accessible name; what the sand reads is added to it.
className
string
default "h-56 w-full"
Sizes and shapes the box.