v0.8.45
HyperFrames v0.8.45
Released on 2026-09-17.
Motion blur arrives twice in this release, as two separate mechanisms for two different jobs. The catalog component reproduces the After Effects look, while the render engine computes the real shutter integral.
Features
- Producer:
motionBluron a render configuration turns on sub-frame multi-sample blur. An empty object takes the defaults: 16 samples per frame, a 180 degree shutter angle and a shutter phase of -90.samplesPerFrameaccepts up to 64. Setting it forces PNG frame capture, because the samples are averaged pixel by pixel and JPEG samples would be averaged after a lossy step. Whether a render captures lossless frames now has one owner instead of being decided in three places from a different fact (b64a7a3a6, #4020). - Engine: Each output frame can be the average of several captures taken across the shutter window. Because each sample re-renders the page, this integrates whatever the composition actually does inside the window: rotation, scale, opacity, filters and child animation, not translation alone. It is off unless asked for, and a static frame still costs one capture (0a1c6f5a4, #4019).
- Producer and CLI: A render can output an HLS VOD package. It is the same H.264 and AAC encode as MP4, copied into MPEG-TS segments with playlists, and every segment starts on a keyframe (462f9db73, #4014, e4b1832fa, #4022).
- Telemetry:
render_completeandrender_errorcarry more about the render they describe: the requested output shape, which deterministic adapters the composition exercised, counts of audio, image, sub-composition, audio group and color grading elements, whether a LUT is present, and the browser and FFmpeg major versions. Every value comes from work the render already did, so nothing new runs. A Docker render reports no tool versions, because its preflight happens inside the container (71bb88719, #4017).
Fixes
- Engine: A frame that repeats its predecessor is reused instead of re-rendered, and with motion blur on that check ignored the shutter window. A still frame next to a moving one reused a buffer carrying none of that motion. The reuse now requires the whole window to be still. The guard's contract comes from #4013 by Dante-dan (9117b3647, #4025).
- Producer: Asking for motion blur on the layered compositor route returned frames with no blur and no error. It now fails with a named error that says which route refused and why, instead of a render that looks finished and is not. The route is taken for HDR content and for shader transitions, and the message names both rather than blaming HDR for a composition that has none. "Does this capture route support motion blur" has one owner, keyed to the capture plan's own kind, so a new route cannot inherit silence. The explicit unsupported-mode error is the design in #4013 by Dante-dan (77a45f608, #4026).
Catalog
- Registry: The
motion-blurcomponent now follows the After Effects shutter model instead of a row of hard copies. Measured against a real After Effects export over 72 frames, the match improved from 24.19 dB to 32.51 dB on average, and the worst frame from 17.07 dB to 27.57 dB (9aa60a844, #4008).
Choosing a motion blur, and what each one costs
The catalog component and the engine path do different things. Applying both blurs the same motion twice.
- The catalog component is cheap, blurs translation only, and deliberately reproduces the After Effects look, which composites a sharp copy over the smear.
- The engine path blurs any animated property and computes the shutter integral, which is what a real camera shutter does. Peak opacity therefore drops during fast motion rather than staying anchored to a sharp core. That is the correct answer for a shutter and a different picture from the component.
- The engine path costs one capture per sample. At 16 samples a 1080p frame spends roughly 600 ms in the blend on top of the 16 captures, so a slow render is expected rather than stuck.
Known limits of the engine path
- A video's own motion does not blur. Injected video frames are extracted once per output frame, so a sub-frame seek does not move them. A render that injects video frames refuses rather than pretending.
- Capture modes other than screenshot are refused with a named error, as is the layered compositor route.
- Frames are captured as PNG for the whole render.
- A shutter that is both wide and not centered, meaning
shutterPhase + shutterAngleabove 360, seeks past the end of the composition on the last frames. No default or documented configuration reaches it and the behaviour there is unproven.