There are four ways to get a morph onto a page. They differ in what they cost and in what they can do, and the browser matrix decides more than the size does.
| Tier | Size | Chromium 151 | Safari 26.3 |
|---|---|---|---|
| compiled CSS | 0 KB | animates | static icon |
| SMIL | 0 KB | animates | animates |
| morphrig/dom | 1.90 kB | animates | animates |
| morphrig/react | 2.89 kB | animates | animates |
Best when the morph is a toggle and you want nothing on the wire.
A two-keyframe morph compiles to a CSS transition, which runs both directions and
interrupts cleanly. Anything sampled compiles to @keyframes, and an animation plays one
direction only, so the way back cuts rather than animating. CSS cannot distinguish a state
flip from a first paint, so a reverse animation on the resting state would fire on load.
The same baked keyframes handed to element.animate(). Useful only where CSS d is
missing but WAAPI d is present. On the engines measured so far that set is empty, so
this exists for completeness rather than as a recommendation.
The one this book left out for too long, and the only other route to zero JavaScript.
<animate attributeName="d"> is part of the SVG spec rather than CSS, which changes three
things at once. It animates any attribute, not the handful CSS recognises, so points
and viewBox are reachable where CSS cannot touch them. It needs no stylesheet, so it
survives being loaded through <img> where an external SVG gets no CSS at all. And it is
not blocked by the missing CSS d property, which is exactly the hole tier 0 falls into.
This row used to carry a warning saying it had not been measured. It has now:
pnpm measure:smil pauses the SVG timeline, steps it to an exact moment and reads the
geometry back off the live element, in both engines. d animates and interpolates linearly
in each, and the midpoint lands on 12.00 where it should.
The run also found something the widely-reported version of this does not mention.
That disagreement matters more than it looks. Correspondence is still yours to decide, which is why Part 3's two answers survive intact, but you cannot lean on the browser to refuse a pairing you got wrong: one engine will refuse it and another will animate something. SMIL is a different way to deliver a morph, not a different way to plan one, and the rig this book compiles feeds it as readily as it feeds a stylesheet.
Chained beginEvent/endEvent sequencing is separately reported to be patchy in Safari,
and that part is still repeated rather than measured.
What to reach for by default if you need anything interactive.
It writes the d attribute directly, so it depends on neither CSS d nor WAAPI and works
everywhere. It adds interruption, real bidirectionality for sampled morphs, velocity
preserving interrupts, and seek for scrubbing.
A morph is a track from A to B, so progress 0 is A and 1 is B. Asking to go back to A mid-flight is not a new animation needing a re-plan, it is the same tween aiming at 0 with its velocity intact.
The same pair on tier 0 cannot do that:
9 keyframes, so it cannot be a CSS transition. It animates out and cuts back. The driver handles this pair in both directions for 1.90 kB.
Worth stating plainly, because "use CSS, it runs on the GPU" is the most repeated advice in web animation and none of it reaches a morph.
The browser renders in three phases, layout then paint then composite, and changing a
property re-runs its own phase and every phase after it. A small set of properties,
transform and opacity among them, land only in the last one. Those the compositor thread
can run on its own while the main thread is busy, which is the whole of what hardware
acceleration means and why a CSS transform survives a long task that a JavaScript one
stutters through.
d is not in that set and cannot join it. Changing geometry changes what has to be
rasterised, so every frame of every morph repaints, on the main thread, whichever tier drove
it. Tier 0 buys you nothing on the wire. It does not buy you a free thread.
The floor is at least paint rather than layout, which is a genuine mercy: SVG has no layout
algorithm, and one shape's coordinates cannot displace another's the way a wider div shoves
its siblings. viewBox is the only attribute that reaches back that far.
What this does change is what you put next to a morph. A rotation or a fade riding
alongside one should be a transform or an opacity rather than more sampled geometry,
because those two can leave the main thread even while the d underneath them cannot. That
is a second reason for the residual check in linear against polar:
sampling a rotation into keyframes does not just cost bytes, it moves motion that could have
been composited into the phase that cannot be.
Use tier 0 for a toggle that never needs to reverse mid-flight, and tier 2 for everything else. Mixing them on one page is fine, since they consume the same compiled output.
SMIL is the one to reach for when the constraint is no stylesheet, either because the SVG ships as a standalone file or because Safari has to animate and shipping a driver is not on the table. It is not the default here only because interruption and scrubbing are what most interactive morphs need, and those are the driver's.