mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
## Problem HyperFrames already had `--gpu`, but that flag only controlled FFmpeg hardware encoding. The browser capture path still forced Chrome/WebGL through SwiftShader software GL via `--use-angle=swiftshader`, so WebGL-heavy local renders could leave the biggest bottleneck on the CPU path. That made the existing flag naming easy to misread: `--gpu` sounded like it accelerated the whole render, but it did not change the browser frame-capture backend. ## What this fixes - Enables host browser GPU acceleration automatically for local CLI renders. - Adds `--no-browser-gpu` as the local opt-out for software Chrome/WebGL capture. - Keeps `--browser-gpu` as an explicit local browser-GPU request. - Adds `browserGpuMode: "software" | "hardware"` to engine config, with `PRODUCER_BROWSER_GPU_MODE` env support for lower-level producer users. - Keeps Docker browser capture on the deterministic software path. - Maps hardware browser GPU mode to platform-native Chrome backends: - macOS: Metal-backed ANGLE - Windows: D3D11-backed ANGLE - Linux: EGL - Blocks explicit `--browser-gpu --docker` with a clear error because Docker browser GPU passthrough is not cross-platform. - Clarifies docs so `--gpu` means FFmpeg encoder GPU and browser GPU means Chrome/WebGL capture GPU. - Keeps encoder backend selection auto-detected from FFmpeg capabilities: - NVIDIA: NVENC - macOS: VideoToolbox - Linux: VAAPI - Intel: QSV ## Why two flags There are two separate GPU surfaces in the render pipeline: 1. Browser GPU controls Chrome frame capture. - Affects WebGL, canvas, CSS rendering, compositing, and screenshot capture inside the browser. - This is enabled automatically for local CLI renders. - Use `--no-browser-gpu` when you want the software browser baseline. 2. `--gpu` controls FFmpeg video encoding. - Affects the final encode step after frames have already been captured. - The concrete encoder is auto-detected from the host FFmpeg build and hardware. - It can be faster for some machines/codecs, but it is not equivalent to browser rendering acceleration. The controls stay independent because users may want: - `hyperframes render` for the fast local default with browser GPU capture. - `hyperframes render --no-browser-gpu` for the software-browser local baseline. - `hyperframes render --gpu` for browser GPU capture plus hardware FFmpeg encoding. - `hyperframes render --no-browser-gpu --gpu` for software browser capture plus hardware FFmpeg encoding. - `hyperframes render --docker` for deterministic browser capture. ## Why `--gpu` does not imply browser GPU Keeping `--gpu` scoped to FFmpeg encoding avoids a semantic break and keeps the risk profile explicit: - `--gpu` already means encoder acceleration. Expanding it to also change Chrome capture would silently alter behavior for users who only wanted hardware encoding. - Browser GPU and encoder GPU have different portability. Encoder GPU can work in Docker when the host exposes the right devices; browser GPU passthrough is not cross-platform, so this PR intentionally blocks explicit `--browser-gpu --docker`. - The Apple presentation benchmark shows why the controls should stay separate: browser GPU capture was the useful improvement, while macOS VideoToolbox via `--gpu` was slower and produced larger output for this `standard` H.264 run. If HyperFrames later wants a single umbrella acceleration control, it should be explicit, for example `--acceleration browser|encoder|all` or `--gpu=browser|encoder|all`, rather than changing the meaning of the existing boolean `--gpu`. ## Root cause `buildChromeArgs()` always injected `--use-gl=angle --use-angle=swiftshader`. `disableGpu` only appended `--disable-gpu`; it did not provide a hardware-GPU mode. That made the public `--gpu` flag look broader than it was, because render capture stayed software-backed even when encoder GPU was requested. ## Verification ### Local checks - `bun install` - `bun run build:hyperframes-runtime` - `bun run --filter @hyperframes/engine test src/config.test.ts src/services/browserManager.test.ts` - `bun run --filter @hyperframes/cli test src/utils/dockerRunArgs.test.ts src/commands/render.test.ts` - `bun run --filter @hyperframes/cli typecheck` - `bun run --filter @hyperframes/engine typecheck` - `bun run --filter @hyperframes/producer typecheck` - `cd packages/producer && bunx vitest run src/services/renderOrchestrator.test.ts` - `bunx oxlint packages/cli/src/commands/render.ts packages/cli/src/commands/render.test.ts packages/cli/src/utils/dockerRunArgs.ts packages/cli/src/utils/dockerRunArgs.test.ts packages/engine/src/config.ts packages/engine/src/config.test.ts packages/engine/src/services/browserManager.ts packages/engine/src/services/browserManager.test.ts packages/producer/src/services/renderOrchestrator.test.ts` - `bunx oxfmt --check ...` on changed source/docs files - `git diff --check` - `bun packages/cli/src/cli.ts render --help | rg -n "browser-gpu|no-browser-gpu|GPU"` - `bun packages/cli/src/cli.ts render packages/producer/tests/css-spinner-render-compat/src --output /tmp/hf-auto-browser-gpu-smoke.mp4 --workers 1 --quality draft --fps 24 --strict` - Render plan prints `GPU: browser GPU (auto)`. - `bun packages/cli/src/cli.ts render packages/producer/tests/css-spinner-render-compat/src --no-browser-gpu --output /tmp/hf-software-browser-gpu-smoke.mp4 --workers 1 --quality draft --fps 24 --strict` - Render plan does not print browser GPU. - `bun packages/cli/src/cli.ts render packages/producer/tests/css-spinner-render-compat/src --docker --browser-gpu --output /tmp/should-not-render.mp4` - Exits 1 with `Browser GPU is local-only`. - `buildDockerRunArgs()` regression coverage asserts Docker container args include `--no-browser-gpu`, preventing nested container renders from re-enabling browser GPU through the local CLI default. - `resolveBrowserGpuForCli()` regression coverage asserts `PRODUCER_BROWSER_GPU_MODE=software` opts out when no CLI browser-GPU flag is supplied, while explicit `--browser-gpu` / `--no-browser-gpu` still win. - `ffmpeg -v error -i /tmp/hf-auto-browser-gpu-smoke.mp4 -f null -` - `ffmpeg -v error -i /tmp/hf-software-browser-gpu-smoke.mp4 -f null -` - `ffprobe -v error -show_entries format=duration:stream=codec_name,width,height,r_frame_rate -of json /tmp/hf-browser-gpu-smoke.mp4` -> H.264, 1920x1080, 24fps, 5.0s ### Apple presentation benchmark Rendered `/Users/miguel07code/Downloads/apple-presentation.zip` as supplied after extracting to `/tmp/hf-apple-profile/apple-presentation`. Fixed settings: - 1920x1080 - 30fps - `standard` quality - 4240 frames - 141.32s duration - 8-worker cap; render auto-calibration used 6 capture workers - macOS host detected FFmpeg GPU encoder: `videotoolbox` | Mode | Equivalent flags after this PR | Wall time | vs software-browser baseline | Speed | Capture | Encode | Output | | --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | | Software browser + CPU encode | `--no-browser-gpu` | 120.77s | baseline | 1.17x | 97.87s | 10.04s | 8.38MB | | Browser GPU + CPU encode | default local render | 70.10s | 42.0% faster | 2.02x | 50.72s | 9.91s | 8.39MB | | Software browser + encoder GPU | `--no-browser-gpu --gpu` | 133.16s | 10.3% slower | 1.06x | 103.58s | 18.31s | 25.43MB | | Browser GPU + encoder GPU | `--gpu` | 74.12s | 38.6% faster | 1.91x | 46.69s | 17.93s | 25.45MB | Result: browser GPU capture is the meaningful improvement for this WebGL/browser-capture-heavy presentation. VideoToolbox encoding was slower and produced larger files for this current `standard` H.264 path, so `--gpu` should stay separate and opt-in. Why `--gpu` plus browser GPU was slower than browser GPU alone: the combined run captured about 4.0s faster than browser GPU alone, but VideoToolbox encoding was about 8.0s slower than CPU x264 encoding, so the encode loss outweighed the capture gain. ### VideoToolbox flag check I also isolated the encode stage against the already-captured Apple frames to check whether macOS GPU encoding only needed special flags. `ffmpeg -h encoder=h264_videotoolbox` does not expose a CRF/CQ-style quality option like x264. It exposes bitrate-oriented and VideoToolbox-specific options such as `-b:v`, `-realtime`, `-profile`, `-coder`, `-prio_speed`, `-power_efficient`, and `-allow_sw`. That means our current `-q:v` mapping is not equivalent to x264 CRF and can produce very different bitrate/size behavior. Measured full-frame encode variants on this host: | VideoToolbox variant | Encode wall time | Output size | Bitrate | | --- | ---: | ---: | ---: | | Current `-q:v 64 -allow_sw 1` | 18.76s | 25.31MB | 1.43 Mbps | | Current without `-allow_sw 1` | 18.21s | 25.31MB | 1.43 Mbps | | `-b:v 500k -maxrate 750k -bufsize 1000k -profile high -coder cabac -realtime 1 -prio_speed 1 -power_efficient 0` | 20.58s | 7.42MB | 0.42 Mbps | | Same with `-b:v 1500k` | 20.84s | 16.70MB | 0.95 Mbps | | `-b:v 500k -profile baseline -coder cavlc -realtime 1 -prio_speed 1 -power_efficient 0` | 18.11s | 8.94MB | 0.51 Mbps | Conclusion: VideoToolbox can be made size/bitrate-predictable with explicit `--video-bitrate`, but the tested speed-oriented flags did not make it faster than CPU x264 wall time for this render. That reinforces keeping `--gpu` encoder acceleration explicit and separate from browser GPU capture. Artifacts from the local benchmark: - `/tmp/hf-apple-profile/results/cpu.mp4` - `/tmp/hf-apple-profile/results/browser-gpu.mp4` - `/tmp/hf-apple-profile/results/encoder-gpu.mp4` - `/tmp/hf-apple-profile/results/full-gpu.mp4` - `/tmp/hf-apple-profile/results/summary.json` All four benchmark MP4s completed `ffprobe` and full `ffmpeg -f null` decode checks. ### Pixel comparison Compared decoded MP4 output between software-browser and browser-GPU renders: - Apple presentation: - 4240 frames compared - 636 exact matching decoded frame hashes - 3604 different decoded frame hashes - Average PSNR: 57.79 dB - `css-spinner-render-compat` clean fixture: - 120 frames compared - 0 exact matching decoded frame hashes - Average PSNR: 61.57 dB Interpretation: browser GPU output is not strict hash/pixel-identical to the software-browser path after lossy H.264 encode, but the measured deltas are visually tiny. Above 50 dB PSNR is typically visually indistinguishable for normal video review. Use `--no-browser-gpu` or Docker when strict cross-run/cross-machine reproducibility matters more than local speed. ### Browser verification - Started HyperFrames Studio preview for `packages/producer/tests/css-spinner-render-compat/src`. - Used `agent-browser` to open `http://localhost:5191#project/src` and verify the composition loaded in Studio. - Screenshots: - `/tmp/hf-gpu-browser-proof/preview-loaded.png` - `/tmp/hf-gpu-browser-proof/preview-playing.png` - `/tmp/hf-gpu-browser-proof/preview-frame-60.png` - Agent-browser recordings: - `/tmp/hf-gpu-browser-proof/preview-playback.webm` - `/tmp/hf-gpu-browser-proof/preview-seek.webm` ## Notes - Browser GPU is enabled automatically for local CLI renders and disabled in Docker. - `--no-browser-gpu` is the opt-out for software Chrome/WebGL capture. - `--gpu` remains encoder-only and opt-in. - The Apple presentation zip has existing lint errors around unmanaged nested videos and imperative media `play()` calls. The benchmark still compares the same supplied source across modes, but it should not be treated as a clean deterministic-composition fixture.
371 lines
14 KiB
Plaintext
371 lines
14 KiB
Plaintext
---
|
||
title: Rendering
|
||
description: "Render compositions to MP4, MOV, or WebM locally or in Docker."
|
||
---
|
||
|
||
Render your Hyperframes [compositions](/concepts/compositions) to MP4, MOV, or WebM with the [CLI](/packages/cli). The rendering pipeline is frame-by-frame and seek-driven — see [Deterministic Rendering](/concepts/determinism) for how this works under the hood.
|
||
|
||
## Getting Started
|
||
|
||
<Steps>
|
||
<Step title="Verify your environment">
|
||
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
|
||
```
|
||
</Step>
|
||
<Step title="Preview your composition">
|
||
Before rendering, preview your composition in the browser to verify it looks correct:
|
||
|
||
```bash Terminal
|
||
npx hyperframes preview
|
||
```
|
||
</Step>
|
||
<Step title="Render to MP4">
|
||
Run the render command from your project directory:
|
||
|
||
```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)
|
||
```
|
||
</Step>
|
||
</Steps>
|
||
|
||
## Rendering Modes
|
||
|
||
<Tabs>
|
||
<Tab title="Local Mode">
|
||
### Local Mode (default)
|
||
|
||
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
|
||
</Tab>
|
||
<Tab title="Docker Mode">
|
||
### Docker Mode
|
||
|
||
[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>
|
||
</Tab>
|
||
</Tabs>
|
||
|
||
## When to Use Each Mode
|
||
|
||
| 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 |
|
||
|
||
## Options
|
||
|
||
| Flag | Values | Default | Description |
|
||
|------|--------|---------|-------------|
|
||
| `--output` | path | `renders/<name>.mp4` | Output file path |
|
||
| `--format` | mp4, mov, webm | mp4 | Output format (see [Transparent Video](#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` |
|
||
| `--workers` | 1-8 or `auto` | auto | Parallel render workers (see [Workers](#workers) below) |
|
||
| `--max-concurrent-renders` | 1-10 | 2 | Max simultaneous renders via the producer server (see [Concurrent Renders](#concurrent-renders) below) |
|
||
| `--gpu` | — | off | GPU encoding (NVENC, VideoToolbox, 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](/guides/hdr) |
|
||
| `--sdr` | — | off | Force SDR output even if HDR sources are detected |
|
||
| `--docker` | — | off | Use Docker for [deterministic rendering](/concepts/determinism) |
|
||
| `--quiet` | — | off | Suppress verbose output |
|
||
|
||
## Quality and Encoding
|
||
|
||
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:
|
||
|
||
```bash
|
||
# 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.mp4
|
||
```
|
||
|
||
**Tip**: 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.
|
||
|
||
## GPU Acceleration
|
||
|
||
Hyperframes has two separate GPU acceleration surfaces:
|
||
|
||
- `--gpu` uses a hardware video encoder in FFmpeg when one is available. Supported backends include VideoToolbox on macOS, NVENC on NVIDIA systems, 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-gpu` to opt out.
|
||
|
||
```bash Terminal
|
||
# 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.mp4
|
||
```
|
||
|
||
Browser 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.
|
||
|
||
## Workers
|
||
|
||
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.
|
||
|
||
### Default behavior
|
||
|
||
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.
|
||
|
||
### Choosing a worker count
|
||
|
||
```bash Terminal
|
||
# 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
|
||
```
|
||
|
||
<Tip>
|
||
Start with the default. If renders feel slow and your system has headroom (check Activity Monitor / `htop`), try increasing `--workers`. If you see high memory pressure or fan noise, reduce it.
|
||
</Tip>
|
||
|
||
### When to use 1 worker
|
||
|
||
- 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)
|
||
|
||
### When to increase workers
|
||
|
||
- 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
|
||
|
||
## Concurrent Renders
|
||
|
||
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.
|
||
|
||
### Configuration
|
||
|
||
```bash Terminal
|
||
# CLI flag
|
||
npx hyperframes render --max-concurrent-renders 2 --output output.mp4
|
||
|
||
# Environment variable (for the producer server)
|
||
PRODUCER_MAX_CONCURRENT_RENDERS=2
|
||
```
|
||
|
||
The default is **2** concurrent renders, which works well on 8-core machines where each render uses 2-3 workers.
|
||
|
||
### Queue status
|
||
|
||
The producer server exposes a `GET /render/queue` endpoint that returns the current state:
|
||
|
||
```json
|
||
{
|
||
"maxConcurrentRenders": 2,
|
||
"activeRenders": 1,
|
||
"queuedRenders": 3
|
||
}
|
||
```
|
||
|
||
AI agents can poll this endpoint to decide whether to submit a render or wait.
|
||
|
||
### SSE queue events
|
||
|
||
When using the streaming endpoint (`POST /render/stream`), queued requests receive a `queued` event before rendering begins:
|
||
|
||
```json
|
||
{"type": "queued", "requestId": "...", "position": 2}
|
||
```
|
||
|
||
This lets agents report "waiting in queue" to users rather than appearing stuck.
|
||
|
||
### Choosing a concurrency limit
|
||
|
||
| 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 |
|
||
|
||
<Tip>
|
||
When in doubt, use 1. Renders will queue up and execute sequentially, but each one gets full CPU and finishes as fast as possible. This is better than 3 renders fighting for CPU and all finishing slowly — or failing.
|
||
</Tip>
|
||
|
||
## Transparent Video
|
||
|
||
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.
|
||
|
||
### Recommended format: MOV (ProRes 4444)
|
||
|
||
```bash Terminal
|
||
npx hyperframes render --format mov --output overlay.mov
|
||
```
|
||
|
||
**MOV 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
|
||
|
||
<Warning>
|
||
ProRes MOV files are large (typically 5-40 MB for short clips) because ProRes is a high-quality intermediate codec optimized for editing, not delivery. This is expected — the same tradeoff Remotion and professional pipelines make.
|
||
</Warning>
|
||
|
||
### Format comparison
|
||
|
||
| 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 |
|
||
| **MP4** | H.264 | No | All | All | Small |
|
||
|
||
<Note>
|
||
**WebM VP9 alpha** is technically supported but all major video editors ignore the alpha channel and render transparent areas as black. Only Chromium-based browsers (Chrome, Arc, Brave, Edge) decode VP9 alpha correctly. Safari does not support it. Use MOV for editor workflows and WebM only for browser-based playback.
|
||
</Note>
|
||
|
||
### How it works
|
||
|
||
When you render with `--format mov` or `--format webm`, Hyperframes:
|
||
|
||
1. Captures each frame as a **PNG with alpha channel** (instead of JPEG for MP4)
|
||
2. Sets Chrome's page background to transparent via `Emulation.setDefaultBackgroundColorOverride`
|
||
3. Encodes with an alpha-capable codec (ProRes 4444 for MOV, VP9 for WebM)
|
||
|
||
Your composition's HTML should **not** set a `background` on `html` or `body` — leave it unset so the transparent background comes through.
|
||
|
||
### Authoring transparent compositions
|
||
|
||
```html
|
||
<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.
|
||
|
||
### Verifying transparency
|
||
|
||
- **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](https://rotato.app/tools/transparent-video) to verify your MOV or WebM has working transparency.
|
||
|
||
## Tips
|
||
|
||
<Tip>
|
||
Use `draft` quality during development for fast previews. Switch to `standard` or `high` for final output.
|
||
</Tip>
|
||
|
||
- Use `npx hyperframes benchmark` to find optimal settings for your system
|
||
- Docker mode is slower but guarantees [identical output](/concepts/determinism) across platforms
|
||
- For compositions with many frames, `--gpu` can significantly speed up local encoding
|
||
|
||
## Next Steps
|
||
|
||
<CardGroup cols={2}>
|
||
<Card title="Deterministic Rendering" icon="lock" href="/concepts/determinism">
|
||
Understand the determinism guarantees
|
||
</Card>
|
||
<Card title="HDR Rendering" icon="sun" href="/guides/hdr">
|
||
Render HDR10 MP4 from HDR video and image sources
|
||
</Card>
|
||
<Card title="CLI Reference" icon="terminal" href="/packages/cli">
|
||
Full list of CLI commands and flags
|
||
</Card>
|
||
<Card title="Troubleshooting" icon="wrench" href="/guides/troubleshooting">
|
||
Fix common rendering issues
|
||
</Card>
|
||
</CardGroup>
|