Motion
Motion tells you where something came from, where it went, what changed, what is active and what completed. Quiet, physical, continuous, purposeful. Nothing moves to decorate.
Five things motion is allowed to say.
If a movement does not answer one of these, it does not happen. Luxury here is restraint: the fewer things move, the more each movement means.
- Fromwhere something came from — a run drops out of its row, not from the sky
- Towhere it went — a dismissed ask leaves the way it came
- Changedwhat is different now — a state glyph turns, a count advances
- Activewhat is working — one pulse, on one glyph, at 1.6s
- Completedwhat finished — a check, a hold, then stillness
Forbidden
- Toy-like spring overshoot
- AI blobs and orbs
- Constant ambient movement in the interface
- Gratuitous parallax
- Cyberpunk glitches, VHS tearing, random distortion in the interface (the animated image layer has its own, stricter print-glitch rules on 05)
- Decorative loaders that hide nothing real
- Motion on hover that changes layout
- Animating a painting like cheap AI video
Seven durations. Four easings.
The bars below are the values: press Play and each dot travels its track in exactly its duration; each curve is drawn from its cubic-bezier.
Token tables
| Duration | Value | Use | Reduced motion |
|---|
| Easing | Curve | Used for |
|---|
CSS: --dur-* and --ease-* in tokens/milo.css. Under prefers-reduced-motion: reduce the same file collapses every duration to ≤120ms and dither/ambient to 0.
Nine named patterns, demo beside spec.
Every pattern in tokens/motion.json, live on the left, specified on the right. The names are the ones used in the CSS and the engine.
Render Day 1 voice trackRunning
- Load reference clip0.8s
- Synthesise 2,140 words1m 12s
- Normalise loudness—
01expand
The reverse of expand, faster, with the exit curve: what folds away should not linger.
- Reply to the FAM print proofQueued
02collapse
Done. Hero written at 2400×1600.
03emerge
Fixed top-centre while it runs. Watch the top of the viewport.
04voice-appear
05voice-resolve
06voice-dismiss
- Reprint the series artRunning
Colour tokens crossfade; imagery re-maps to the Nous recipe in dark.
07theme-switch
Half Dome, Carol M. Highsmith, Library of Congress. Public domain.
08dither-propagate

Carol M. Highsmith, Library of Congress. Watch for a minute; it should feel like weather, not a loop.
09ambient-nature
All nine, as a table
| Pattern | Duration | Easing | Note | Where |
|---|
What moves first.
A surface enters in the order you read it: the statement, its meta, the rows one after another, Milo's activity line, then the figure. Nothing overlaps enough to feel busy; nothing waits long enough to feel slow.
What matters now
Cut the Daily Bread Day 4 opener.
- Reply to the FAM print proofQueued
- Approve Euangelion render batch 7Needs you
- 3 more todaytucked
◉ Milo is rendering Euangelion batch 8 on the M4 · 3 of 12 · 2m 14s

scale: 1 unit = 12ms · total ≈ 960ms · each beat = emerge (slow · enter)
Rules
| Rule | Value | Why |
|---|---|---|
| Beat | emerge · slow (360ms) · enter | one pattern for everything that enters; the ear learns one rhythm |
| Row stagger | 40ms | reads as a list arriving, not as separate events |
| Maximum entry | ≤ 1s to last beat | a surface must be usable before it has finished arriving |
| Imagery last | +600ms | the figure is the least urgent thing on Today |
| Navigation | never animates on entry | the rail is furniture, not content |
| Reduced motion | all beats at 0ms, 120ms fade | order is preserved by layout, not by time |
Ten percent of images move.
The image engine has its own motion: reveal, drift, glitch and pixels, applied with data-fx. It is specified on 05 Imagery §animated; one example here so the catalogue is complete.

- mode
data-fx="reveal"— once, when scrolled into view- duration
- 1500ms, ease-out cubic (print easing approximation)
- stages
- original → pixels → dither → channels → interpretation → data
- after
- holds the recipe; nothing recomputes at rest
- reduced
- final frame drawn immediately
- others
drift(breathes every 4.2s) ·glitch(≤420ms print burst, seconds apart) ·pixels(24px → clean)
Nothing is lost except the movement.
Under prefers-reduced-motion: reduce every transition collapses to an opacity change of 120ms or less, ambient motion stops, and the dither transition draws its final frame at once.
This browser: checking…
Approve the Euangelion batch.
- Reprint the series artRunning
- Write the Thursday noteIdle
What changes, exactly
| Under reduce | Full | Reduced | Source |
|---|---|---|---|
| --dur-fast / base / slow / reveal | 140 / 220 / 360 / 600ms | 80 / 120 / 120 / 120ms | tokens/milo.css media query |
| --dur-dither | 1400ms | 0 — final frame | tokens/milo.css · engine/canvas.js |
| --dur-ambient | 24s | 0 — still | tokens/milo.css |
| State pulse (.state-active) | 1.6s loop | none | milo.css |
| Voice pill ring | breathe / spin / pulse | none; state word still changes | milo.css |
| data-fx images | reveal / drift / glitch / pixels | final frame only | engine/fx.js |
| Scroll behaviour | smooth | auto | milo.css |