← All posts

2 October 2026 · 14 min read

Scroll is time: how we rebuilt restless.run.

A field guide to the glow-up: one idea for the whole page, two motion languages that never mix, choreography that waits for the camera, micro-interactions that earn their place, and a performance budget that keeps all of it at sixty frames.

0 ms900 msrest
Two motion languages on one timeline: the pixel world moves in whole steps, the product interface glides on a spring.

The old restless.run explained the product. The new one lets you spend a day inside it. You arrive in the morning, the studio’s people and agents are already at their desks, and as you scroll the clock in the corner moves through the day: a conversation in a room, a brief with three cursors, an afternoon of chores that never reach you, a review on the company computer, one decision, and then dusk. The office stays lit.

None of the individual effects are new. Sticky scenes, line-masked headlines, cursor-following light, count-ups and spring easing are all well known. What made the difference was deciding, before writing a line of motion code, what the motion was for, and then holding every effect to that standard. This is the set of rules we ended up with, written down so we can keep to them and so you can borrow them.

Start from one idea, not a list of effects

The brief for the page was to make two bets legible: people and agents working together, and attention treated as a budget. A list of features cannot do that. A story can. We chose one organising idea, scroll is time, and let it decide everything else. The page is one working day. Each section sits at an hour. The clock in the corner is the progress indicator, the colour of the floor is the time of day, and the night arrives exactly once, at the end, as a payoff rather than a premise.

The idea then became a filter. An effect stays only if it moves the day forward or shows the product. The cursor torch in the hero stays, because it is how you meet the people on the floor. A parallax starfield behind the feature grid did not, however pretty it was, because it said nothing about the work.

Render the product, not screenshots

Every surface on the page is the real component. The work board, the decision folio and the hold-to-approve control come from @restless/ui; the office floor, with its engine and sprites, comes from @restless/office. Both are release artifacts packed from Restless Core at a recorded commit and checked by checksum when the site builds. The site never copies Core source.

That choice pays for itself in motion. A screenshot can only fade. A component can behave: the board’s cards move between columns with the same list animation the product uses, the approval fills under your pointer, the floor’s people walk to their desks. When the illustration is the product, there is nothing to keep in sync, and nothing that looks better on the page than it does in the app. Wherever the example business appears, the page says so.

Two motion languages, and they never mix

The office floor is pixel art. Pixel art reads as cheap the moment it is smoothly interpolated, so everything that belongs to the world moves in whole steps: CSS steps() timing, whole-pixel positions, sprite frames at the floor’s own cadence. Tags bob in two frames. The decision burst falls in nine. The quickstart office lights its windows one room at a time.

The product interface is the opposite. It glides on one spring, cubic-bezier(0.16, 1, 0.3, 1), at 700 to 900 milliseconds for anything that travels and 110 to 180 for presses and state. Panels grow, headlines rise through a mask, cards settle. The contrast is the brand: a toy-like world outside, serious software inside. Mixing them, a smoothly sliding sprite or a stepped dialog, breaks both.

  • World: stepped timing, whole pixels, 4 to 9 frames, never eased.
  • Interface: one spring curve, three durations (press, state, travel), never stepped.
  • Exits are faster than entrances: 220 milliseconds out, 700 in. Leaving should never hold up arriving.
0 ms900 msrest
The same 900 milliseconds, twice: a stepped path for the world and a damped spring for the interface.

Choreography waits for the camera

Each beat of the day pins the office and moves a camera to a room. The single most important timing rule on the page is that the interface waits for the camera to arrive. Scroll drives the camera through the first forty percent of a beat; only after that does the beat become active and its panel appear. If both move at once the eye has nowhere to land.

When the panel does appear, it grows out of the room the camera is looking at. A small pixel speech bubble pops first, in steps, at the centre of the stage; the product surface then scales from that point to its place on the right, on the spring. The visitor never sees a card arrive from nowhere: it comes out of the world.

Inside a panel, small staggers carry the reading order: 70 to 90 milliseconds between lines, never more than about 600 milliseconds for the whole group. Headlines rise through a line mask rather than fading, which keeps them crisp at large sizes. Everything else uses a short rise with a touch of blur, so it settles rather than pops.

Micro-interactions have to earn their place

A micro-interaction is worth building when it lets the visitor do the thing the product is about, in miniature. Hold to approve is the decision. Take control is the company computer. Invite an agent into a room shows that rooms have an explicit audience. Correcting a fact and watching three outputs get flagged is Company Identity in four seconds. None of them are there to be clever; each is a promise you can test with your own hand.

Feedback for those interactions is layered and optional. Every press has a physical response, a bevel that drops by three pixels. The copy button morphs its icon rather than swapping text. Approving fires a burst in the product’s four intent colours and makes everyone on the floor hop. Sound is synthesised in the browser, off by default, and remembered only on that device. Vibration is used once, for the approval, where the platform supports it.

Label who is who, everywhere

A page about people and agents working together has to be precise about which is which. Everyone on the floor wears a tag over their head: gold NI for Natural Intelligence, blue AI for an agent. The same tags appear in rooms, in the brief, in the decision, in the cast list and on the cursors of other visitors. The tags are pinned to the moving characters by reading their screen positions from the office engine every frame, so they follow people as they walk.

The live cursors follow the same rule. Other people looking at the page appear as anonymous keepers with an NI tag. If you are alone, the cursor that shows you around is Marlow, the studio’s lead agent, tagged AI. It would have been easy to fake a second visitor. It would also have undermined the one thing the page is careful about.

A performance budget the effects have to live inside

Motion that drops frames reads as broken, not as ambitious. The page animates only transform, opacity and filter. Scroll is read once per frame in one requestAnimationFrame loop per pinned scene, and everything downstream derives from that number. The office canvas paints at thirty frames a second, which pixel art cannot tell apart from sixty, and stops entirely when it scrolls out of view. Tag overlays run only while the floor is visible. Presence updates are sent at most eight times a second.

The page is prerendered to static HTML. The headline is readable before the canvas has loaded; the office arrives a moment later with its lights stepping on, which turns a loading state into part of the story.

  • Animate compositor-only properties; never layout.
  • One scroll reader per scene; derive, do not re-measure.
  • Pause what is not on screen: canvas, overlays, intervals.
  • Throttle anything that leaves the device.
  • Ship static HTML first; let motion enhance it.

Reduced motion is a designed version, not a broken one

Visitors who ask for reduced motion get the same story without the camera: the floor as a still, each beat as a normal section with its panel already complete, counters at their final values, no tour cursor and no sound. Without JavaScript, every section is still there and every link works. We review that version as carefully as the animated one, using a query parameter that forces it on any device.

Accessibility and motion pull in the same direction more often than people expect. Inactive beats are inert, so keyboard focus never lands on a panel you cannot see. The step rail is real buttons. The hold-to-approve control is a real button that submits without a pointer. A good motion system is mostly a good state system with timing attached.

What we would tell a team starting a glow-up

Write the one idea down first, and make it something the visitor can feel rather than read. Pick your two or three durations and one curve, and refuse to add a fourth. Decide which parts of the page belong to the world and which to the interface, and give each its own motion language. Make the interface wait for the camera. Build only the micro-interactions that let people try the product’s promise with their own hands. Label everything honestly. Then put the whole thing on a frame budget and review the reduced-motion version before you review the animated one.

The result should not feel like a page with a lot of animation. It should feel like a place where time passes.