> ## Documentation Index
> Fetch the complete documentation index at: https://hyperframes.heygen.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Motion Blur

> Smear fast-moving elements along their path, in Studio preview and in renders. Off until you turn it on.

Capture motion blur gives fast-moving elements a real shutter smear. For each frame, HyperFrames reads where every mover was across the shutter interval and redraws it along that path. A box sliding across the frame leaves a streak instead of a sharp copy at one position.

It is **opt-in**. Nothing blurs until you add `data-motion-blur="on"` to the composition root.

## Turn it on

```html theme={null}
<div
  id="root"
  data-composition-id="main"
  data-width="1920"
  data-height="1080"
  data-duration="10"
  data-motion-blur="on"
>
  ...
</div>
```

That is all. The same attribute drives Studio preview and `hyperframes render`, so what you see is what you render.

| Attribute | Default | Meaning |
| - | - | - |
| `data-motion-blur` | none | `on` enables blur. `off` opts out, on the root or on any subtree. |
| `data-motion-blur-shutter` | `180` | Shutter angle in degrees, 0–720. Larger is a longer smear. |
| `data-motion-blur-samples` | `16` | Sub-frame samples per frame, 2–64. More is smoother and slower. |
| `data-motion-blur-events` | off | `on` also runs timeline callbacks (`onUpdate` and similar) at each sample. Use it when the motion is driven by callbacks, which blur cannot otherwise see. |

## 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 composition with nothing moving is never wrapped and does no per-frame blur work; the one-time discovery scan still runs on first play.

Blur is an image-space approximation. A mover's box and its contents move together, and glows or shadows outside its box stay sharp. 3D movers are skipped.

## Requirements

Blur draws through Chrome's [HTML in Canvas](/guides/html-in-canvas.md) API.

* **Renders** use the render browser, which HyperFrames launches with the API enabled.
* **Studio preview** uses your own browser. We saw Chrome 157 and 158 expose the API without a flag, but only in headless and Chrome-for-Testing builds; whether it ships in stable Chrome is unverified. Chrome 155 and earlier need `chrome://flags/#html-in-canvas`. Preview detects the API and plays sharp when it is missing, so check your own browser rather than relying on a version number.

If Studio's browser cannot blur, Studio tells the render, and the render also skips blur so the output matches what you saw.

## When blur turns itself off

html-in-canvas captures a few things wrongly. A wrongly blurred frame is worse than a sharp one, so when HyperFrames finds one of these it turns blur off **for the whole composition, from the first frame**, instead of switching mid-render. This covers every defect the timeline scan can see. A defect the scan misses (a WebGL canvas added late, a root shake between scan samples, a face that flips hidden briefly) is caught per frame instead: that one frame renders sharp, blur resumes on the next, and the count is reported as `failedFrames`.

| Found in the composition | Why |
| - | - |
| A WebGL or WebGPU canvas anywhere in the document | Captured frozen. |
| `backface-visibility: hidden` inside a 3D context (including from an animation) | Drawn mirrored. |
| A transform on the root element | Ignored by the capture. |
| Opacity below 1, or `mix-blend-mode`, on the root element | Not applied by the capture. |
| An `iframe`, `object`, `embed` or custom element in the root | Not captured faithfully. |
| A nested capture canvas, such as a VFX block | Hangs the renderer. |
| An HDR render | The HDR compositor's layer mask does not reach the blur canvases. |
| A distributed render | Chunks cannot share the single-page capture. |

Large see-through movers (a full-frame translucent sweep, say) are skipped too, because blurring them would drag the content underneath along. Their fast children still blur.

### Find out why it is off

```bash theme={null}
npx hyperframes check
```

| Finding | Severity | Meaning |
| - | - | - |
| `motion_blur_off` | warning | Blur is on but was switched off. The message names the cause and a fix. |
| `motion_blur_no_movers` | info | Blur is on but nothing moves fast enough to blur. |
| `motion_blur_unverified` | info | The check could not confirm blur in this browser. |

In the page, `window.__hfMotionBlur.report()` returns `state`, `reason`, `reasonCode` and `movers`. A render also logs one line per session saying whether blur ran and why not, plus a session-end line when any frames fell back to sharp (`failedFrames > 0`).

## Cost and limits

* **Render time.** About 1.7× for a blurred composition (median over 20 real compositions). Compositions where blur is off or nothing moves add only the one-time discovery scan (tens of milliseconds on a 25-mover composition).
* **Editing.** The root is wrapped in the capture canvas only while playing or rendering. Paused, it is plain DOM, so click-to-select in Studio works as usual.
* **Preview rate.** Blurred preview can run at half the frame rate on heavy compositions.
* **Video.** A `<video>` drawn through the capture looks about 2–3% darker in Studio preview than in normal playback. Renders are not affected.
* **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: fixed` and `foreignObject`; a very short Web Animations API animation that falls between scan samples; movers made see-through only by CSS backgrounds, masks or `clip-path`.


## Related topics

- [Motion Blur](/catalog/components/motion-blur.md)
- [Changelog](/changelog.md)
- [Gooey Split](/catalog/blocks/gooey-split.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.