Qendrim Beka

Frame

Open studio

What should a component do when someone changes their mind halfway

Frame is a motion editor for working React components, built around that one question. You use your real component, interrupt it, and decide what it does at that moment. Export gives you a small runtime that behaves the same way in your app, with no dependency except React.

The stage above is the core of that editor on three small components. Press one, drag it or throw it, and everything else on the stage is worked out from what you did. The trace is the path of its moving edge at the component's own scale, and the plate behind it is the same take as a long exposure.

A hand is one more interruption

A press that lands mid flight and a hand that lets go mid flight ask the runtime for the same thing: start a new spring from wherever the component is, at whatever speed it has. So the hand needed no physics of its own. While it holds on, every channel follows its progress between the two measured layouts, and where one part leaves as another arrives the first fades out before the second fades in. When it lets go, the speed is the slope of its last 80 ms of pointer samples, and the ordinary interruption code takes over from there.

Nothing in between is eased or guessed, which is why a hard throw overshoots and a slow one does not. What is yours to decide is the rule. Below is one press and the press that undoes it, under each of the three rules Frame can export. Move the second press and all three are worked out again.

The take on the stage's component under each rule, worked out by the runtime in this page from the layout it measured, with the plate on the left of each and the trace of the edge on the right

What a plate records

A plate is a long exposure of a take. The shutter opens at the first event and closes when the motion rests, and the moving target leaves its outline wherever it is, so ink builds up where it lingers. A turning point prints as a dark line, a slow settle as a dense band, and a snap leaves almost nothing. A flash at 60 Hz adds one crisp outline for each display frame.

The lines are moments worked out from the take, they are not measured paints. Save plate gives you the print as an image, with the trace beside it at the same scale, so a dark line on the plate sits level with the turn in the curve that made it. Copy link gives an address that holds the take itself, every pointer sample of it, and replays it.

What CSS can hold, and where it stops

Tune also writes the opening spring as a CSS linear() easing, for when a stylesheet is all you want. From rest it follows the spring within 0.2% of the distance, which for the expense card is under half a pixel. A unit test reads the written string back and checks that bound for 13 springs at 3 distances.

What an easing cannot hold is a speed. A CSS transition that is reversed starts its easing again from rest, so the edge stops dead and sets off again. The runtime carries the speed through the turn. The right half of the figure works both out for the reversal above. It is computed here from the CSS rule for a reversed transition, and no browser run compares the two.

On the left the opening spring on the stage and its copied linear() easing, with the gap between them under it against the 0.2% bound drawn to scale, and on the right the same reversal by the runtime and by a CSS transition using that easing, whose speed moves in steps because a linear() easing is made of straight pieces

Two builds of one program

The studio preview and the exported app are two builds of the same evaluator, so comparing them says nothing about the physics. It tests everything around the physics: the build, the CSS and the replay path. Across 120 scripted runs in Chromium, WebKit and Firefox the two drew within 0.000023 px of each other, against a 0.5 px target.

Whether the springs are right is a separate question with a separate answer. Real browser geometry was read against an independent RK4 solution that shares no code with the runtime. The worst case was 0.094 px, against a 0.11 px target, and most of that is the 0.1 px snap when a spring settles.

The same code, built twice Studio against export, worst of 40,386 comparisons in three engines

0.000023 px 0.5 px target

Different math The exported app against RK4, worst of 5 runs

0.094 px 0.11 px target
One ruler in pixels, where each step is ten times the last, with agreement between two builds on the first row and the distance from independent physics on the second

A check that cannot fail proves nothing, so 9 faults were written into clean builds, from a dropped velocity to damping set 2 percent high. All 9 were caught, and neither of the 2 clean builds was flagged. The first full run caught 8 of 9, so it was thrown out, the fixture was changed so the missing fault could show, and everything was measured again.

What it costs

Added to an app

Frame export
7.1 KB
Motion 14.9.1
45.1 KB
Motion with LazyMotion
45.2 KB
Gzipped JavaScript over the same UI with no animation, in a Vite build of this one interaction, and Motion does far more than this runtime

No win claimed at runtime

Frame
12.8 ms
Motion 14.9.1
17.9 ms
Motion with LazyMotion
18.4 ms
Main thread per open and reverse cycle in Chromium on a scale from 0 to 22 ms, where the bar is the range of 5 runs around their median and the outline is the range from the same test three days earlier, when the ranges overlapped, so no win is claimed

Editing inside one frame

Edit to DOM, 30 targets
1.4 ms
One 60 Hz frame
16.7 ms
Median in a production build of the studio, with a p95 of 1.6 ms, against a target of one frame

Limits

  • No person has run the usability task, so there is no usability result.
  • All timings come from one Mac in headless browsers, with other work running. They are lab timings, not field data.
  • No physical phone was tested, and touch was emulated.
  • No one using a screen reader has tried it, and no human has reviewed the code.
  • Only registered components work. Elements outside the registered root do not animate.
  • Every measured result is for the expense card and for presses. The menu, the sheet and the hand run on the same runtime, covered by unit and browser tests, and were not part of those runs.
  • The example takes that use a hand are written as pointer samples. No person recorded them.
  • Scrubbing back across a viewport change reflows text to the current width.

Every number on this page comes from a recorded run, and the report has the tables, the raw data and the failures.