The grammar

Thirty-six movements,
no cuts.

The vocabulary the scenes are built from. A transition is something you pick between two finished clips; a movement carries the film from one state to the next with no join to hide. Every entry here runs a live demo, states the numbers it starts from, and names the bench that can fail it. Twenty-six of them run on the slab; the last ten have no interface on screen at all, because in those the sentence is the film.

Thirty-one of the thirty-six name a bench that exists, runs in CI and can fail. The other five are printed in grey: four name a bench nobody has written yet, and one names a bench that exists but cannot run in CI. A measurement nobody runs is a proposal, and this page would be doing the thing it warns against if it let the two look alike.

Forty-five seconds, one shot

This is the film the vocabulary is for. Twelve of the thirteen beats render and join end to end. The one still in grey is the truck, and it is not grey for lack of time: any clipping — overflow: hidden, overflow: clip, clip-path — flattens preserve-3d, and the slab has to clip, so per-layer parallax costs a restructure of the DOM in every scene rather than a few translateZ. It stays printed the way the missing benches are printed: a plan and a thing are not allowed to look alike.

f0000
CAM-01 the slab enters from off-frame and straightens, from UI_MOCKUP_START_POSE to UI_MOCKUP_END_POSEUIMockup
f0120
CAM-02 truck left: sidebar, board and panel separate because they sit at different depthsno scene gives its layers a translateZ
f0240
CUR-01 the cursor arrives on an arc, overshoots the active card slightly and settles on itCardHandoff
f0330
CUR-04 it picks the card up and drags: three frames of lag, tilting into the direction of travelCardHandoff
f0450
CHR-01 the destination column opens the slot before the card arrives, the source closes behindCardHandoff
f0570
CHR-03 the card lands, then the count moves five frames later, then the card rewrites its own age. The delays are what read as causeCardHandoff
f0660
CAM-04 descent to macro onto the card just delivered. That is CardFocus, and it stays sharp because it is DOMCardFocus
f0780
TXT-01 the cursor reaches the composer and types with a person's pausesPromptInput
f0930
CUR-02 half a second of hesitation on the button, then CUR-03 presses and the UI answers five frames laterPromptInput
f0990
TXT-02 the answer arrives in chunks, and inside the visible area rather than under the composerPromptInput
f1140
CAM-05 everything that is not the answer drops to 0.62 opacityPromptInput
f1230
CAM-03 a short orbit shows the edge of the slab and stops at restBoardOrbit
f1350
End. At rest, so another scene can attach here without it showingBoardOrbit

What renders today is six scenes joined end to end, every join measured by seam.sh: UIMockup, CardHandoff, CardFocus, CardRelease, PromptInput, BoardOrbit. 1460 frames, 48.7 seconds, no cut anywhere in them, and the five joins read 557, 64, 55, 29 and 15 pixels. They get cleaner as they go, which is what happens when both sides take the same geometry from the same module instead of agreeing by hand. CardRelease is not a beat in this list because this list is the plan and not the inventory.

Speed is a number

Motion design is dense: the references behind this vocabulary run 13 to 42 seconds and fit more into them than there is in these 48. The tempo of a scene is now its duration — each scene writes its beats against a reference length and durationInFrames scales all of them, so halving it halves every beat inside rather than cutting the tail off. What does not scale is the perceptual thresholds: the four frames between a click and its consequence are the window in which the eye ties a gesture to its effect, not a rhythm decision, and at double speed they would be two. Same for the three frames the card lags behind the hand, which are the weight of the object, and for the caret's blink, which is a frequency. Scaling those along with the rest is how a faster scene becomes a broken one.

The benches, and what they fail

BenchThe failure it catchesReading on the repo
seam.shA join that is really a cut in costume29 px against 59,895, 2065×
handoff-travel.pyA card that does not actually cross. A freeze frame passes the other checks with full marks, and for months this one did not run at all on macOS, printing a diagnosis about the scene for a fault in itself170-195 px on the slab, three ratios
beats.pyA promised beat that is not on screen. The answer was assembling under the composer2 → 7 → 10 words, three ratios
fill-measure.shA frame that lets its edges die, which it cannot catch. The scenes sit on a background as bright as its threshold, so a slab shrunk to 60 per cent with the real background around it reads the same as a full frame. Kept as a readout; fill-geom.py does the job in CI, in geometry20 of 20, on the defect too
legibility.shText that does not survive being scaled down. It reads the reference films beside the render, and those are not in git, so it runs on a laptop and not in CIholds under 640 px wide
framelocked-verdict.shA scene that is not reproducible: it still renders, with different pixels. For months it printed its verdict and exited 0 either way, so it now has to fail a probe with a Math.random inside before its green countssame pixels every render, the probe fails
focus-sharpness.shA slab that stopped being DOM: the text falls apart once the camera is on top of it2.09× against 1.03× on the fixture
rest-point.shA scene still moving where the next one has to attach. Its thresholds were absolute to begin with, tuned on this machine, and every one of them failed the move to Linuxedges under 30% of mid-scene
click-gap.pyA press and its consequence fused onto one frame, or so far apart that it stops reading as a consequence and starts reading as lagf271 and f276, five frames, three ratios
loop-close.pyA demo whose loop tears every pass, or whose second half restarts from scratch instead of continuing the first. Hunting for the mid-loop cut did not work — a cut and a fade look alike to every metric tried — so each two-part demo declares the frame it changes on and the bench looks there, which is the bargain seamAfter already strikes for the scenes11 torn, now 0
type-check.pyType that covers other type or the readout, leaves the frame, snaps while it is visible, or goes away altogether. The first fix of the typography family made sure the frame never went black, and the sentences kept leaving and coming back anyway, in waves — in TYP-01 a fifth of the sentence was on screen for half a second, twice a loop. What reads as the sentence being gone is having nothing whole to read, so that is the rule: on every frame at least one word is on screen in full. And with reduced motion, where a demo shows a single frame, that frame has to be at rest10 of 10 failing on the previous page, 0 now, at four widths
tempo.pyA scene that was shortened rather than sped up. If the beats scale, frame f of the short render is frame f/k of the long one; compared that way the two differ by 287 px against 5379 without normalising. The residual is not slop — it is the signature of the thresholds that deliberately did not scale18.7× advantage, 4 required
fixture-trim.shMeasures nothing. Truncates a scene to the short duration instead of retiming it, which is what lowering the duration produced before any of this existed120 frames, beats left in place
contrast-floor.pyContent attenuated so far that it stops being a plane behind and becomes dirt on the background. Two wrong versions before this one: the first measured the composer placeholder, which is deliberately faint and sits at 2.93:1 before any attenuation at all; the second measured the right content in a place whose position depends on how the messages wrap, and on Linux the crop landed on empty background4.17:1, 3.84 on Linux, against 1.71
drift.pyA descent that slides past its subject, measured away from Topics: two slabs, one of them light and vertical, in three formats. Its second broken case starts exactly in place and only drifts while the camera pushes in, and the first version of the bench failed the correct case too, because the white of the probe slab ended up in the magenta mask2.03 px at worst of six, broken cases 39.5 to 99.8
chain-check.pyA camera that reverses while it is moving, which reads as a cut even when the two frames match. It reads the tracks the scenes render from, so it needs no render, and the version of the chain with the easing taken out is its broken case7 reversals, all at rest
fill-geom.pyA slab that leaves an edge of the frame uncovered, checked on every frame from the camera tracks and the kit projection instead of from pixels. Its first run said BoardOrbit fails, and it does, on purpose: the orbit shows the edge5.4 px of cover at worst, all five fail pushed back
project-check.pyA projection that gets a rotation sign wrong. The kit works out where a point of the slab lands at any pose, tilted included, and every bench that watches a moving camera will crop by it, so it is checked against the engine that draws: Chromium, two slabs, three stages, the six poses of the chain0.040 px at worst, 103.7 with the origin wrong
no-product-literals.shA bench that works out the geometry of one product by itself instead of asking the manifest. Four benches did, which is how a bench ends up measuring Topics and nothing else without ever failing4 on the previous commit, 0 now
demo-check.pyA demonstration on this page that has stopped demonstrating its own entry. The difference between showing a thesis and not showing it is almost always temporal, and a screenshot cannot see it, so this one scrubs25 entries, frame by frame
fixture-screenshot.shMeasures nothing. It builds the scene the bench above has to fail, and without it that bench is a promisefirst frame magnified 2.26×

The negative control gets written first. A bench that cannot fail measures nothing, so it is made to exit non-zero on a broken input before its green is worth anything.

What stays out

ExcludedWhy
The nine ways to reveal a logoOne is enough, and it goes at the end. Nine is a symptom of what the ecosystem thinks it is for
The twelve template transitionsThey ship with Remotion already. Rewriting them is wasted work, and they are cuts anyway
YouTube furnitureSubscribe reminders, end cards, credits rolls, countdowns. A different trade
Decorative backgroundsBokeh, matrix rain, particle explosions, starfields. Handsome and mute: they say nothing about the product
Anything that needs a cutIf it only works thanks to the edit, it does not belong in one unbroken shot