Files
heygen-com--hyperframes/docs/concepts/determinism.mdx
T
wehub-resource-sync 85453da49f
regression / regression-shards (style-16-prod style-9-prod style-17-prod iframe-render-compat variables-prod mp4-h265-sdr, shard-4) (push) Has been cancelled
regression / regression-shards (style-4-prod style-11-prod style-2-prod animejs-adapter typegpu-adapter parallel-capture-regression, shard-5) (push) Has been cancelled
regression / regression-shards (style-7-prod style-8-prod style-10-prod css-spinner-render-compat webm-transparency mp4-h264-sdr webm-vp9, shard-3) (push) Has been cancelled
regression / regression-shards (sub-composition-video style-18-prod raf-ball-render-compat font-variant-numeric sub-comp-t0 sub-comp-id-selector, shard-7) (push) Has been cancelled
Windows render verification / Detect changes (push) Has been cancelled
Windows render verification / Preflight (lint + format) (push) Has been cancelled
Windows render verification / Render on windows-latest (push) Has been cancelled
Windows render verification / Tests on windows-latest (push) Has been cancelled
CI / Detect changes (push) Has been cancelled
CI / Build (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Fallow audit (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Producer: integration tests (push) Has been cancelled
CI / Producer: unit tests (push) Has been cancelled
CI / File size check (push) Has been cancelled
CI / Test: skills (push) Has been cancelled
CI / Skills: manifest in sync (push) Has been cancelled
CI / CLI: npx shim (macos-latest) (push) Has been cancelled
CI / CLI: npx shim (ubuntu-latest) (push) Has been cancelled
CI / CLI: npx shim (windows-latest) (push) Has been cancelled
CI / SDK: unit + contract + smoke (push) Has been cancelled
CI / Test: runtime contract (push) Has been cancelled
CI / Studio: load smoke (push) Has been cancelled
CI / Smoke: global install (push) Has been cancelled
CI / CLI smoke (required) (push) Has been cancelled
CI / Semantic PR title (push) Has been cancelled
Player perf / Detect changes (push) Has been cancelled
Player perf / Preflight (lint + format) (push) Has been cancelled
Player perf / player-perf (push) Has been cancelled
Player perf / Perf: drift (push) Has been cancelled
Player perf / Perf: fps (push) Has been cancelled
Player perf / Perf: parity (push) Has been cancelled
Player perf / Perf: scrub (push) Has been cancelled
Player perf / Perf: load (push) Has been cancelled
preview-regression / Detect changes (push) Has been cancelled
preview-regression / Preflight (lint + format) (push) Has been cancelled
preview-regression / Preview parity (push) Has been cancelled
preview-regression / preview-regression (push) Has been cancelled
regression / regression (push) Has been cancelled
regression / Detect changes (push) Has been cancelled
regression / Preflight (lint + format) (push) Has been cancelled
regression / regression-shards (hdr-regression style-5-prod style-3-prod mov-prores, shard-1) (push) Has been cancelled
regression / regression-shards (overlay-montage-prod style-12-prod chat missing-host-comp-id png-sequence portrait-edge-bleed, shard-6) (push) Has been cancelled
regression / regression-shards (style-13-prod style-6-prod vignelli-stacking gsap-letters-render-compat audio-mux-parity, shard-8) (push) Has been cancelled
regression / regression-shards (style-15-prod hdr-hlg-regression style-1-prod many-cuts vfr-screen-recording render-symlinked-assets, shard-2) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docs / Validate docs (push) Has been cancelled
Sync skills to ClawHub / Publish changed skills (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:58:35 +08:00

104 lines
4.9 KiB
Plaintext

---
title: Deterministic Rendering
description: "Same input, identical output. Every time."
---
Hyperframes is built around a core guarantee: **the same [composition](/concepts/compositions) always produces the same video**. This is what makes automated pipelines, CI testing, and AI-driven workflows reliable.
## How It Works
The rendering pipeline is frame-by-frame and seek-driven. No realtime playback is involved -- every frame is independently seeked and captured.
<Steps>
<Step title="Frame clock">
The [engine](/packages/engine) computes the time for each frame using integer math: `time = floor(frame) / fps`. There is no wall-clock dependency -- rendering is entirely decoupled from real time.
</Step>
<Step title="Seek">
The [frame adapter](/concepts/frame-adapters) receives a `seekFrame(frame)` call and deterministically positions all animations, DOM state, and canvas content to the exact frame. The adapter pauses all [GSAP](/guides/gsap-animation) timelines and seeks them to the computed time.
</Step>
<Step title="Capture">
Chrome's `HeadlessExperimental.beginFrame` API captures the pixel buffer for the current frame. This is a single, atomic operation -- no partial paints or race conditions.
</Step>
<Step title="Encode">
FFmpeg encodes the captured frames into the final MP4 video. Audio tracks from `<audio>` and `<video>` elements are mixed in during this stage.
</Step>
</Steps>
```mermaid
graph LR
A["Frame Clock<br/>t = frame / fps"] --> B["Seek<br/>adapter.seekFrame(frame)"]
B --> C["Capture<br/>beginFrame API"]
C --> D["Encode<br/>FFmpeg"]
D --> E["MP4"]
style A fill:#00C4FF,color:#fff
style B fill:#00C4FF,color:#fff
style C fill:#00C4FF,color:#fff
style D fill:#00C4FF,color:#fff
style E fill:#00A8E1,color:#fff
```
## What Makes It Deterministic
- **No wall-clock dependencies** -- rendering does not use `Date.now()`, `requestAnimationFrame`, or system timers
- **No unseeded randomness** -- `Math.random()` without a seed breaks determinism
- **No render-time network fetches** -- all assets must be loaded before rendering starts
- **Fixed output parameters** -- `fps`, `width`, and `height` are locked before the first frame
- **Finite duration** -- every [composition](/concepts/compositions) has a known, finite length
These same rules apply to every [frame adapter](/concepts/frame-adapters). If you are building a custom adapter, you must follow the [determinism contract](/concepts/frame-adapters#determinism-contract).
## Docker Mode
For maximum reproducibility, render in Docker:
```bash
npx hyperframes render --docker --output output.mp4
```
Docker mode uses an exact Chrome version and font set, ensuring:
- Same Chromium rendering engine across all platforms
- Same system fonts (no platform-specific font substitution)
- Same FFmpeg encoder version
See the [Rendering guide](/guides/rendering) for all rendering options.
## Preview vs. Render Parity
The browser preview and the rendered MP4 should match. Hyperframes achieves this through:
- **One runtime** -- the same `hyperframe.runtime` drives both preview and render
- **Producer-canonical behavior** -- the [producer's](/packages/producer) seek semantics are the source of truth
- **Readiness gates** -- `__playerReady` and `__renderReady` ensure the [composition](/concepts/compositions) is fully loaded before any frame is captured
Parity here means **visual fidelity** — every frame looks the same. It does *not* mean performance parity. Preview plays in real time in a browser, so frame-rate limits are bound by your hardware. Render is seek-driven and frame-at-a-time, so it never drops frames regardless of per-frame cost. A composition can stutter in preview and render perfectly. See [Performance](/guides/performance) for why.
<Note>
Local rendering (without Docker) may show slight differences due to platform-specific font rendering and Chrome version. Use Docker mode when exact reproducibility matters.
</Note>
## For Adapter Authors
If you are building a [frame adapter](/concepts/frame-adapters), your adapter must follow the determinism contract:
- `seekFrame(frame)` must be idempotent -- same frame, same result
- No side effects that depend on call order (must handle random access)
- No async operations that resolve after the frame is "committed"
- Clean lifecycle: `init` -> `seekFrame` (N times) -> `destroy`
## Next Steps
<CardGroup cols={2}>
<Card title="Frame Adapters" icon="plug" href="/concepts/frame-adapters">
Build adapters that uphold the determinism contract
</Card>
<Card title="Rendering" icon="film" href="/guides/rendering">
Render to MP4 locally or in Docker
</Card>
<Card title="@hyperframes/producer" icon="clapperboard" href="/packages/producer">
The full rendering pipeline that orchestrates deterministic output
</Card>
<Card title="Common Mistakes" icon="triangle-exclamation" href="/guides/common-mistakes">
Pitfalls that break determinism and how to avoid them
</Card>
</CardGroup>