<video> tag and the subject floats over whatever you put behind them.
The CLI ships a built-in remove-background command that runs locally — no API keys, no cloud upload, no green screen.
Quick Start
1
Verify ffmpeg is installed
The pipeline needs Confirm with
ffmpeg and ffprobe for decode + encode. Most systems already have them; if not:Terminal
npx hyperframes doctor — both should be green.2
Remove the background from your video
Terminal
~/.cache/hyperframes/background-removal/models/. Subsequent runs reuse the cache.Output:3
Drop it into a composition
The output is a standard VP9-with-alpha WebM. Chrome’s Render the composition with the usual
<video> element decodes the alpha plane natively — no special player needed:composition.html
hyperframes render.How it works
The pipeline runs four stages, all locally:onnxruntime-node with the best-available execution provider on your machine: CoreML on Apple Silicon, CUDA on NVIDIA, CPU otherwise.
The output is encoded with the exact ffmpeg flags Chrome’s <video> element needs to decode alpha — -pix_fmt yuva420p plus the alpha_mode=1 metadata tag. Get those wrong and the alpha plane is silently discarded by browsers.
Output formats
Terminal
Layer separation: emit the cutout and the background plate together
Pass--background-output (alias -b) to write a second transparent video alongside the cutout. Same source RGB, alpha is the inverse mask — opaque where the surroundings were, transparent where the subject is. The result is a clean two-layer separation in a single inference pass:
Terminal
Both encoders share the source W/H/fps and your
--quality preset, so the layers are pixel-aligned. Encode cost roughly doubles; segmentation cost is unchanged.
Hole-cut vs. clean plate — when does the difference matter?
A hole-cut plate keeps the original surroundings and makes the subject region transparent. A clean plate fills the subject region with reconstructed background — produced by a separate inpainting model. Display each alone over black:
The line is: does anything ever need to be visible through the subject’s silhouette where the subject used to be?
If something opaque always covers the silhouette, hole-cut is sufficient and ~1000× cheaper than running an inpainter.
The two-layer composition pattern
The two-layer pattern is functionally a drop-in for text-behind-subject without needing the originalpresenter.mp4 in the project — the plate replaces it as the bottom layer:
.webm or .mov for both outputs. It’s not valid for image inputs (no temporal pairing to do) and won’t accept .png for the plate.
Performance
Real-world numbers from the matting eval, running u²-net_human_seg on a 4-second 1080p clip:
Matting is offline preprocessing — you run it once per asset and reuse the output. CPU-only is slow but always works; if you reuse the same subject clip repeatedly, run it once on a faster machine and check the transparent output into your project.
Picking a device explicitly
--device auto is the default and right for almost everyone. The flag exists for two cases:
-
Force CPU on a GPU box when you want to keep the GPU free for other work, or are debugging an EP-specific issue:
Terminal
-
Opt into CUDA by setting
HYPERFRAMES_CUDA=1and providing a GPU-enabledonnxruntime-nodebuild (the bundled build is CPU + CoreML only, to keep the install small for the 99% of users who don’t have a GPU):Terminal
npx hyperframes remove-background --info to see what providers are detected on your machine and which one auto would pick.
Using the transparent video in a composition
The transparent WebM behaves like any other video element. The two patterns you’ll use most: Subject over a background image:loop handles it.
Compositing patterns and pitfalls
The cutout webm is a re-encoded copy of the source mp4’s RGB — the matter pipeline decodes the source to raw RGB, runs segmentation, and re-encodes to VP9 with alpha. That choice has consequences depending on what you put behind it.The three patterns
Text-behind-subject: the recommended layout
Putting a headline behind a presenter so their silhouette occludes the text:Two non-obvious rules
1. Wrap the cutout video in a non-timed<div> and animate the wrapper, not the video.
The framework forces opacity: 1 on any element with data-start/data-duration while it’s “active” — that’s how it controls clip visibility. CSS opacity: 0 on the video element is silently overwritten by the framework’s clip lifecycle, so an opacity tween on the video element won’t do anything. Wrap the video in a <div> that has no data-* attributes; the wrapper is owned entirely by your CSS/GSAP.
2. Both videos start at data-start="0" and decode in sync from t=0.
It’s tempting to “late-mount” the cutout (data-start="3.3" to match the cut). Don’t — Chrome does a seek + decoder warm-up at mount, which can land one frame off the base mp4 at the cut moment. With both videos mounted from t=0 and the cutout’s wrapper opacity-animated, both decoders advance the same way and stay frame-accurate.
Quality preset and color match
When the cutout is overlaid on its own source mp4, the encoder’s CRF directly affects how visible the doubling is at edges:
The encoder also writes BT.709 + limited-range color metadata so Chrome’s YUV→RGB pipeline matches the source mp4’s. Without those tags, the cutout would render slightly differently from the underlying mp4 even at lossless quality (visible red/skin shift).
What u²-net_human_seg is and isn’t good for
The model is purpose-built for portrait / human matting. It excels when:- ✅ The subject is a person, head-and-shoulders or full-body
- ✅ The framing is reasonably stable (not a wide handheld shot)
- ✅ The background contrasts with the subject
- ❌ Non-human subjects (products, animals, objects). The model will return a mostly-empty mask.
- ❌ Very fine hair detail on a busy background. The 320×320 inference resolution means hair tips get softened — fine for most use cases, but compositors notice.
- ❌ Frame-to-frame temporal consistency. Each frame is processed independently, so static backgrounds with moving subjects can show subtle edge flicker. For most web playback this is invisible; for high-end VFX it may matter.
- ❌ Live streams or real-time capture. The pipeline is batch-only.
Alternatives — when the built-in command isn’t the right tool
The CLI ships one model on purpose — the one that’s MIT-licensed, runs everywhere, and produces production-quality output for person/portrait video. The list below leads with free, open-source tools that pair naturally with HyperFrames. Each entry calls out the actual catch — license, install effort, hardware needs — so you can pick the right one for your situation. Full benchmarks are in the matting eval.Free, open-source CLIs and libraries
These all run locally with no account, no upload, no watermark.
After running any of these externally, encode the output as a HyperFrames-compatible transparent WebM with:
Terminal
Free desktop / GUI tools
Web SaaS tools (free tiers, with strings)
How to choose
- Person / portrait video, web playback, MIT-clean → use the built-in
hyperframes remove-background(this is what it’s tuned for). - Non-human subject (product, animal, object) →
rembgwithisnet-general-use. - Maximum portrait quality, especially hair →
BiRefNetvia Python. - Long video where edge flicker would be visible, GPL is OK →
RVM. - One-off marketing clip, no install → DaVinci Resolve (free) for video, Backgroundremover.app for a still image.
- Specialty case the off-the-shelf models can’t handle → ComfyUI with a custom graph.
Troubleshooting
Model download fails or hangs
The weights live on GitHub Releases (rembg’sv0.0.0 release, ~168 MB). If your network blocks GitHub or the download is interrupted:
Terminal
remove-background runs skip the download and use your local copy.
”ffmpeg and ffprobe are required”
The pipeline shells out to ffmpeg for decode + encode. Install viabrew install ffmpeg on macOS or sudo apt install ffmpeg on Debian/Ubuntu. Verify with npx hyperframes doctor.
The output WebM looks fully opaque in the browser
Chrome only reads the alpha plane when the WebM is encoded asyuva420p with the alpha_mode=1 metadata tag. The CLI sets both. If you re-encode the output yourself (e.g. with another ffmpeg invocation), preserve those flags:
Terminal
Terminal
frame0.png should be RGBA and have non-trivial alpha values.
CoreML is “available” but inference fails to start
The pipeline auto-falls-back to CPU if CoreML fails to bind, with a warning. If you want to skip the CoreML attempt entirely, force CPU:Terminal
The alpha mask has rough or jagged edges
That usually means the source frame is high-contrast against a similar-toned background and the model’s 320×320 inference resolution is showing through. Two paths forward:- Re-frame or re-shoot to give the subject a more contrasting background.
- Try
birefnet-portraitviarembg(see Other open-source models) — it’s higher quality at hair edges but slower and heavier.
Reference
- CLI:
hyperframes remove-background - Eval: Matting eval — v7
- Source model: danielgatis/rembg
- ONNX runtime:
onnxruntime-node