FallingLetters

A line of type comes apart and falls, each letter dropping and bouncing once, with every position a closed form in t.

press replay to drop it again

Paste into Claude Code, Cursor or any agent. Pinned to a tag, so it will not change under you.

Options

hold 400

0 to 4000 ms

Milliseconds before the first letter goes. Long enough that the line is read as a line first, because the whole gesture depends on it having been intact a moment ago.

stagger 42

0 to 300 ms, good: 25 to 80

Milliseconds between one letter leaving and the next. At 0 the whole line goes at once, which reads as the shelf giving way rather than as the type coming loose.

scatter 0.55

0 to 1

How scattered the order is. At 0 the letters drop strictly left to right, which reads as mechanical; at 1 the order is unrelated to position, which reads as random. The middle is what looks like something coming apart.

gravity 26

4 to 120

Fall acceleration, in line heights per second squared. In line heights rather than pixels so the same value behaves at any type size.

bounce 0.28

0 to 0.8

How much of its speed a letter keeps on the bounce. At 0 it stops dead, which reads as a dropped frame. Past about 0.5 it is a rubber ball rather than a piece of lead type.

drift 0.35

0 to 2

How far a letter wanders sideways as it falls, in line heights. A little is the difference between type coming loose and a row of counters dropping down a slot.

spin 150

0 to 900 deg/s

Degrees a letter turns per second while falling, in whichever direction its hash gives it. At 0 the letters stay upright, which is quieter and is the right answer for a line that has to stay readable as it goes.

floor 3.2

0.5 to 20

How far below the baseline the letters come to rest, in line heights. Make sure there is room: the element keeps its original height, so anything past the bottom of its container needs the container to allow overflow.

seed 7

0 to 9999

Seed for the scatter and the spin directions. The same seed gives the same fall, so a recording and a live page agree letter for letter.

What it needs

Category
Type, an effect
Dependencies
None at all
Browser
No WebGL
Frame budget
~0.4 ms · 0 context
Pinned to
v0.14.0
  • Every letter's position is a closed form in t rather than a simulation. A solver steps from the last frame to the next, so frame 200 can only be reached by computing the 199 before it: you cannot seek, you cannot record a loop, and two runs drift apart as soon as one misses a frame. Here the fall is the parabola itself and the bounce is the same parabola reflected and damped, so any t in any order gives the same frame
  • The letters are measured once and taken out of flow, so nothing reflows. Split a line into spans and animate them in place and every letter that moves changes the line box, so the letters that have not fallen yet shuffle along underneath
  • The measuring has to finish before any letter is taken out of flow. Do it letter by letter and each removal reflows the rest, so every measurement after the first is of a line that no longer exists
  • One damped bounce, not none and not five. A letter that falls and stops dead reads as a dropped frame rather than as a thing with weight; five is a rubber ball. One is a piece of lead type landing on a stone
  • Distances are in line heights rather than pixels, so the same numbers behave at any type size
  • The original text stays in the accessibility tree as a label on the element, and the per-letter spans are hidden from it. A word split into letters is otherwise read out one letter at a time
  • destroy() puts the original text back exactly as it was found, including the spaces
  • One transform per letter per frame while the line is falling, composited, and nothing at all before the first letter goes: a letter that has not started writes no style
  • No per-frame allocation and no layout reads during the fall. Everything was measured once at build, and the arithmetic is a square root and two multiplications per letter