data-motion-blur="on" to the composition root.
Turn it on
hyperframes render.
In the browser editor
The Blur tab in the browser editor’s right dock sets it with one control: Off, Subtle or Strong. Subtle is the default blur (240° shutter, samples sized to the smear). Strong opens the shutter to 720° and the samples to 64. The panel writes the attributes above to the composition root, so a render uses the same setting, and Undo works. Subtle and Strong also turn ondata-motion-blur-events, so motion set from a render(t) callback is found. Off removes data-motion-blur and data-motion-blur-events and leaves the other attributes where they are.
What it blurs
Elements that move quickly in 2D: position, scale and rotation changes from GSAP, CSS animations and transitions, and the Web Animations API. Slow movers and static content are left sharp. A mover’s glow and shadow travel with it, and the background behind it stays where it is.Two ways it is drawn
Capture copies each mover out of the page with Chrome’s HTML in Canvas API and redraws it along its path. It draws turns and zooms truthfully: a spinning tile smears along its arc and its centre stays sharp, and a zoom streaks outward. Filter puts an SVG filter on each mover: copies of the element along its path, averaged, and softened along the motion. It works in any Chrome, over a WebGL canvas, and costs about what a render without blur costs. Turns and zooms are only softened, not streaked. Withdata-motion-blur-tier="auto" (the default), blur uses the filter tier when every mover only translates and capture when anything turns or scales, because for translation the two look the same and the filter tier is much cheaper.
Capture needs a browser with HTML in Canvas. Chrome 157 and 158 builds exposed it without a flag in headless and Chrome-for-Testing builds; whether stable Chrome does is unverified. Chrome 155 and earlier need chrome://flags/#html-in-canvas.
Blur uses the filter tier instead of capture when capture cannot run:
This is decided once for the whole composition, from the first frame. A defect the scan misses (a WebGL canvas added late, say) is caught per frame instead: that one frame is rendered sharp and counted in
failedFrames.
Blur is off, and stays off, for HDR renders, distributed renders, and renders that use the engine’s own sub-frame blur.
Find out which one is drawing
In the page,
window.__hfMotionBlur.report() returns state, tier, reason, reasonCode and movers. A render logs one line per session with the tier, and a session-end line when any frames fell back to sharp (failedFrames > 0).
Cost and limits
- Render time. On an 8 s test composition with four whips, a render took 9.8 s with capture, 5.2–5.4 s with the filter tier and 5.3–5.9 s with no blur (two runs each, including startup). Capture adds a second paint per blurred frame; the filter tier adds almost nothing.
- Editing. With capture the root is wrapped only while playing or rendering; paused, it is plain DOM, so click-to-select works. The filter tier never wraps anything.
- Capture only. Blurred preview can run at half the frame rate on heavy compositions, and a
<video>drawn through the capture looks about 2–3% darker in the browser editor preview than in normal playback. Renders are not affected. - Preview and render. A browser without HTML in Canvas previews with the filter tier. A render then uses capture where the composition needs it, so a spin or zoom looks softer in that preview than in the render. Force one tier with
data-motion-blur-tierfor an exact match. - Worker count. Output is not bit-identical between worker counts (around 60 dB PSNR), about the size of the difference chunking already causes.
- Not handled. Pose errors under CSS
zoom,position: fixedandforeignObject; a very short Web Animations API animation that falls between scan samples; a large see-through mover (a full-frame translucent sweep), which is left sharp while its fast children still blur. With capture, a mover is drawn above the static content, so a static element that should cover a mover does not.