Skip to main content
Background removal — also called matting in VFX — separates a foreground subject (typically a person) from its background. The output is a video with an alpha channel: fully transparent where the background was, opaque where the subject is. Drop it into any HyperFrames composition as a <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 ffmpeg and ffprobe for decode + encode. Most systems already have them; if not:
Terminal
Confirm with npx hyperframes doctor — both should be green.
2

Remove the background from your video

Terminal
On the first run, the CLI downloads ~168 MB of model weights to ~/.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 <video> element decodes the alpha plane natively — no special player needed:
composition.html
Render the composition with the usual hyperframes render.

How it works

The pipeline runs four stages, all locally:
The model is u²-net_human_seg (MIT license, ~168 MB ONNX). It runs through 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.
This is a hole-cut plate, not an inpainted clean plate. The subject region in plate.webm is fully transparent — you have to composite something opaque under it (a graphic, a blurred copy, a different scene) to fill the hole. If you need an actual filled background where the subject was, use a video inpainter (LaMa, ProPainter, RunwayML Inpaint) — remove-background is not the right tool for that.

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 original presenter.mp4 in the project — the plate replaces it as the bottom layer:
Constraints: the flag requires a video input and .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=1 and providing a GPU-enabled onnxruntime-node build (the bundled build is CPU + CoreML only, to keep the install small for the 99% of users who don’t have a GPU):
    Terminal
Run 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:
Subject over a HyperFrames scene:
The cutout inherits the composition’s frame rate and timeline — it plays through once during the scene’s duration, so match the source clip length to the scene length when possible. If the scene is longer than the clip, loop handles it.
When rendering a composition that contains a <video> element, the renderer reads the source via ffmpeg internally. Transparent WebMs are decoded with the alpha plane preserved.

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

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
It struggles or fails on:
  • ❌ 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.
If your use case hits one of these, see the alternatives below.

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) → rembg with isnet-general-use.
  • Maximum portrait quality, especially hairBiRefNet via 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’s v0.0.0 release, ~168 MB). If your network blocks GitHub or the download is interrupted:
Terminal
Subsequent 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 via brew 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 as yuva420p 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
To verify a WebM has alpha, extract the first frame and inspect:
Terminal
The decoded 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:
  1. Re-frame or re-shoot to give the subject a more contrasting background.
  2. Try birefnet-portrait via rembg (see Other open-source models) — it’s higher quality at hair edges but slower and heavier.

Reference