| title | Rendering |
|---|---|
| description | Render compositions to MP4, MOV, or WebM locally or in Docker. |
Render your Hyperframes compositions to MP4, MOV, or WebM with the CLI. The rendering pipeline is frame-by-frame and seek-driven — see Deterministic Rendering for how this works under the hood.
Run the diagnostics command to check for required dependencies:```bash Terminal
npx hyperframes doctor
```
Expected output:
```
✓ Node.js v22.x
✓ FFmpeg 7.x
✓ FFprobe 7.x
✓ Chrome (bundled)
✓ Docker available
```
```bash Terminal
npx hyperframes preview
```
```bash Terminal
npx hyperframes render --output output.mp4
```
Expected output:
```
⠋ Rendering composition "root" (30fps, standard quality)
✓ Captured 240 frames in 8.2s
✓ Encoded to output.mp4 (8.0s, 1920x1080, 4.2MB)
```
Uses Puppeteer (bundled Chromium) and your system's FFmpeg. Fast for iteration during development.
**Requires:** FFmpeg installed on your system. See [Troubleshooting](/guides/troubleshooting) if FFmpeg is not found.
```bash Terminal
npx hyperframes render --output output.mp4
```
**Pros:**
- Fast startup, no container overhead
- Can use your system GPU for Chrome/WebGL capture by default
- Can use your system GPU for hardware-accelerated encoding (with `--gpu`)
- Best for iterative development
**Cons:**
- Output may vary across platforms due to font and Chrome version differences
- Not suitable for CI/CD pipelines that require reproducibility
[Deterministic](/concepts/determinism) output with an exact Chrome version and font set. Use this for production renders and CI pipelines.
**Requires:** Docker installed and running.
```bash Terminal
npx hyperframes render --docker --output output.mp4
```
**Pros:**
- Identical output on every platform — same Chrome, same fonts, same FFmpeg
- The same pipeline used in production
- Ideal for CI/CD and automated workflows
**Cons:**
- Slower startup due to container initialization
- Browser capture stays on the deterministic software-GL path
- GPU encoding requires Docker host GPU passthrough and is not cross-platform on Docker Desktop
<Note>
Docker mode uses `chrome-headless-shell` with [BeginFrame](/concepts/determinism#how-it-works) control for frame-perfect, deterministic capture.
</Note>
| Scenario | Recommended Mode |
|---|---|
| Local development and iteration | Local |
| CI/CD pipeline | Docker |
| Sharing renders with a team | Docker |
| Quick preview export | Local |
| AI agent-driven rendering | Docker |
| Benchmarking performance | Local |
| Flag | Values | Default | Description |
|---|---|---|---|
--output |
path | renders/<name>.mp4 |
Output file path |
--format |
mp4, mov, webm, png-sequence | mp4 | Output format (see Transparent Video below) |
--fps |
24, 30, 60 | 30 | Frames per second |
--quality |
draft, standard, high | standard | Encoding quality preset |
--crf |
0–51 | — | Override CRF (lower = higher quality). Cannot combine with --video-bitrate |
--video-bitrate |
e.g. 10M, 5000k |
— | Target bitrate encoding. Cannot combine with --crf |
--video-frame-format |
auto, jpg, png | auto | Source video frame extraction format. Use png for UI recordings, screen captures, and color-sensitive source videos |
--workers |
1-8 or auto |
auto | Parallel render workers (see Workers below) |
--max-concurrent-renders |
1-10 | 2 | Max simultaneous renders via the producer server (see Concurrent Renders below) |
--gpu |
— | off | GPU encoding (NVENC, VideoToolbox, AMF, VAAPI, QSV) |
--browser-gpu / --no-browser-gpu |
— | on locally, off in Docker | Use or opt out of host GPU acceleration for local Chrome/WebGL capture |
--hdr |
— | off | Force HDR output even if no HDR sources are detected (MP4 only). See HDR Rendering |
--sdr |
— | off | Force SDR output even if HDR sources are detected |
--docker |
— | off | Use Docker for deterministic rendering |
--quiet |
— | off | Suppress verbose output |
The --quality flag selects a preset that controls the H.264 CRF (Constant Rate Factor) and encoder speed:
| Preset | CRF | x264 Preset | Best For |
|---|---|---|---|
draft |
28 | ultrafast | Quick previews, iteration |
standard |
18 | medium | General use — visually lossless at 1080p |
high |
15 | slow | Final delivery, near-lossless quality |
For finer control, use --crf or --video-bitrate to override the preset:
# Near-lossless quality (CRF 15 = very high quality, large file)
npx hyperframes render --crf 15 --output pristine.mp4
# Target a specific bitrate (useful for size-constrained delivery)
npx hyperframes render --video-bitrate 10M --output controlled.mp4Tip: The default standard preset (CRF 18) is visually lossless at 1080p — most people cannot distinguish it from the source. Use --quality draft for faster iteration, or --quality high / --crf 10 when file size is no concern.
For UI recordings, screen captures, or other source videos where saturated interface colors matter, pass --video-frame-format png to extract source video layers as PNG before browser capture. The default auto mode preserves the historical behavior: alpha-capable sources use PNG, opaque sources use JPG.
Hyperframes has two separate GPU acceleration surfaces:
--gpuuses a hardware video encoder in FFmpeg when one is available. Supported backends include VideoToolbox on macOS, NVENC on NVIDIA systems, AMD AMF on Windows, VAAPI on Linux, and Intel QSV on supported Windows/Linux hosts.- Browser GPU uses the host GPU for local Chrome/WebGL capture. It is enabled automatically for local renders and disabled in Docker. Use
--no-browser-gputo opt out.
# Add hardware FFmpeg encoding to the default local browser-GPU render
npx hyperframes render --gpu --output encoded-fast.mp4
# Opt out of hardware Chrome/WebGL capture
npx hyperframes render --no-browser-gpu --output software-browser.mp4
# Use browser GPU plus hardware FFmpeg encoding
npx hyperframes render --gpu --output gpu.mp4Browser GPU capture is local-mode only. It maps to platform-native Chrome GPU backends: Metal on macOS, D3D11 on Windows, and EGL on Linux. Use --no-browser-gpu or Docker mode when exact cross-machine reproducibility matters more than local render speed.
Each render worker launches a separate Chrome browser process to capture frames in parallel. More workers can speed up rendering, but each one consumes ~256 MB of RAM and significant CPU.
By default, Hyperframes uses half of your CPU cores, capped at 4:
| Machine | CPU cores | Default workers |
|---|---|---|
| MacBook Air (M1) | 8 | 4 |
| MacBook Pro (M3) | 12 | 4 (capped) |
| 4-core laptop | 4 | 2 |
| 2-core VM | 2 | 1 |
This is intentionally conservative. Each worker spawns its own Chrome process, so the per-worker overhead is significant. Fewer workers avoids resource contention with FFmpeg encoding and your other applications.
# Explicit worker count
npx hyperframes render --workers 1 --output output.mp4
# Let Hyperframes pick based on your CPU
npx hyperframes render --workers auto --output output.mp4
# Maximum parallelism (use with caution on laptops)
npx hyperframes render --workers 8 --output output.mp4- Short compositions (under 2 seconds / 60 frames) — parallelism overhead exceeds the benefit
- Low-memory machines (4 GB or less)
- Running renders alongside other heavy processes (video editing, large builds)
- Long compositions (30+ seconds) on a machine with 8+ cores and 16+ GB RAM
- Dedicated render machines or CI runners
- Docker mode on a well-provisioned host
When multiple render requests hit the producer server simultaneously (common with AI agents), each render spawns its own set of Chrome worker processes. Too many concurrent renders can exhaust CPU and cause failures.
The producer server uses a request-level semaphore to queue renders. Only maxConcurrentRenders renders execute at a time — additional requests wait in a FIFO queue until a slot opens.
# CLI flag
npx hyperframes render --max-concurrent-renders 2 --output output.mp4
# Environment variable (for the producer server)
PRODUCER_MAX_CONCURRENT_RENDERS=2The default is 2 concurrent renders, which works well on 8-core machines where each render uses 2-3 workers.
The producer server exposes a GET /render/queue endpoint that returns the current state:
{
"maxConcurrentRenders": 2,
"activeRenders": 1,
"queuedRenders": 3
}AI agents can poll this endpoint to decide whether to submit a render or wait.
When using the streaming endpoint (POST /render/stream), queued requests receive a queued event before rendering begins:
{"type": "queued", "requestId": "...", "position": 2}This lets agents report "waiting in queue" to users rather than appearing stuck.
| Machine | CPU cores | Recommended limit |
|---|---|---|
| 4-core VM | 4 | 1 |
| 8-core workstation | 8 | 2 |
| 16-core server | 16 | 3-4 |
| 32-core render box | 32 | 5-6 |
Hyperframes supports rendering with a transparent background — useful for overlays, lower thirds, subscribe cards, and any element you want to composite over other footage in a video editor.
npx hyperframes render --format mov --output overlay.movMOV with ProRes 4444 is the industry standard for transparent video. It works in all major video editors:
- CapCut
- Final Cut Pro
- Adobe Premiere Pro
- DaVinci Resolve
- After Effects
| Format | Codec | Transparency | Video editors | Browsers | File size |
|---|---|---|---|---|---|
| MOV | ProRes 4444 | Yes | CapCut, Final Cut, Premiere, DaVinci, After Effects | No | Large |
| WebM | VP9 | Yes | None (shows black background) | Chrome, Firefox | Small |
| PNG sequence | RGBA PNGs (no encoding) | Yes (lossless) | After Effects, Nuke, Fusion (image-sequence import) | No | Largest |
| MP4 | H.264 | No | All | All | Small |
npx hyperframes render --format png-sequence --output frames/--format png-sequence skips the encoder entirely. The captured RGBA frames are copied to <output>/frame_NNNNNN.png (zero-padded) and, if the composition has audio, an audio.aac sidecar is written alongside. Use this when you want lossless frames — for compositing in After Effects / Nuke / Fusion, or as the input to a custom encode pipeline. --output is treated as a directory and is created if it doesn't exist.
When you render with --format mov, --format webm, or --format png-sequence, Hyperframes:
- Captures each frame as a PNG with alpha channel (instead of JPEG for MP4)
- Sets Chrome's page background to transparent via
Emulation.setDefaultBackgroundColorOverride - Encodes with an alpha-capable codec (ProRes 4444 for MOV, VP9 for WebM);
png-sequenceskips encoding and writes the captured frames directly
Your composition's HTML should not set a background on html or body — leave it unset so the transparent background comes through.
<style>
/* Do NOT set background on html/body — leave them transparent */
* { margin: 0; padding: 0; box-sizing: border-box; }
[data-composition-id="my-overlay"] {
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
/* No background here either */
}
</style>Only the visible elements (cards, text, images) will appear in the final video. Everything else will be transparent.
- In a browser: Open the MOV file — it won't play (ProRes is not a browser codec). Instead, render a WebM copy and open it in Chrome on a checkerboard background page.
- In a video editor: Import the MOV file and place it on a track above other footage. Transparent areas should show the footage below.
- Online tool: Use rotato.app/tools/transparent-video to verify your MOV or WebM has working transparency.
- Use
npx hyperframes benchmarkto find optimal settings for your system - Docker mode is slower but guarantees identical output across platforms
- For compositions with many frames,
--gpucan significantly speed up local encoding