On-device background removal, verifiably local
"Runs in your browser" is a claim most tools ask you to take on trust. The
background remover and the cutout inside the
thumbnail maker are built so that you do not have to. The segmentation
models are files this site serves from its own /vendor/ directory; your browser downloads
them and executes them locally; and the only network traffic a photo ever causes is that download
— towards your device, never away from it. This guide explains what the three models are, what each
first-use download contains byte for byte, how to watch the whole thing happen in your browser's developer
tools, why the largest model analyses at 512×512 on the CPU provider, and what the tool does when
a model will not load. Every figure below is measured or computed at build time from the same vendored
files and tested code the tools use, so this page cannot disagree with them.
Three models, three tiers
The remover offers three on-device engines, and the picker lists them in this order. Each option carries a download label produced by the tested tier catalog from byte counts measured at build time — the label is what the tier will actually fetch the first time you use it.
| Tier | Model | License | Runtime | First use |
|---|---|---|---|---|
| Fast — people (auto) | Google MediaPipe selfie-segmentation (people) | Apache-2.0 | @mediapipe/tasks-vision 0.10.35, WASM | ~11.7 MB on first use |
| Click the subject | Google MediaPipe MagicTouch interactive segmentation (any subject you click) | Apache-2.0 | The same MediaPipe runtime | ~17.7 MB on first use |
| Best quality — any subject | BiRefNet_lite, 512×512 ONNX export with 4-bit-quantized weights | MIT | onnxruntime-web 1.29.0 (MIT), WASM CPU provider in a worker | ~86.9 MB on first use |
The first tier runs a person-segmentation model — the same one behind the maker's cutout toggle — so it is tuned for photos of people. The second runs an interactive model that segments whatever subject sits at the point you click. The third runs a dichotomous image segmentation model built for subject-versus-background cutouts of any kind, and it is the largest download by design. All three hand back the same thing: a per-pixel confidence mask at the photo's own resolution, which one shared refinement step turns into the alpha channel of a transparent PNG. The runtime versions in this table are read from the project's dependency manifest at build time, and the test suite pins them exact.
What each download actually contains
The tier labels are sums, not estimates. At build time the tool page — and this guide — measures each file the SIMD runtime path fetches with the file system and formats the totals with the same function the picker uses. The vendored provenance note records a second, no-SIMD copy of the MediaPipe runtime that the MediaPipe loader selects on browsers without WebAssembly SIMD; it is not part of these totals. These are the files, with their sizes as measured for this build:
File under /vendor/ | Role | Used by | Bytes | Size |
|---|---|---|---|---|
mediapipe/wasm/vision_wasm_internal.wasm | MediaPipe WASM runtime (binary) | Fast — people (auto); Click the subject | 11,153,617 | 11.2 MB |
mediapipe/wasm/vision_wasm_internal.js | MediaPipe runtime loader | Fast — people (auto); Click the subject | 322,044 | 0.3 MB |
mediapipe/models/selfie_segmenter.tflite | Selfie-segmentation model | Fast — people (auto) | 249,537 | 0.2 MB |
mediapipe/models/magic_touch.tflite | MagicTouch interactive model | Click the subject | 6,227,884 | 6.2 MB |
birefnet/model_q4f16.onnx | BiRefNet_lite 512×512 ONNX export, 4-bit weights | Best quality — any subject | 59,068,372 | 59.1 MB |
onnxruntime/ort-wasm-simd-threaded.jsep.wasm | onnxruntime-web WASM runtime (binary) | Best quality — any subject | 27,797,172 | 27.8 MB |
onnxruntime/ort-wasm-simd-threaded.jsep.mjs | onnxruntime-web runtime loader | Best quality — any subject | 46,676 | — |
A blank Size cell means the file is smaller than the formatter's one-decimal resolution — it would otherwise print as 0.0 MB — so that row's byte count stands alone; the bytes are still part of the tier total.
The two MediaPipe tiers share one runtime, so the arithmetic is short. Fast mode is the runtime binary and loader (11.5 MB) plus the selfie model (0.2 MB), which the catalog reports as ~11.7 MB on first use. "Click the subject" is the same runtime plus the MagicTouch model (6.2 MB), reported as ~17.7 MB on first use — the two tiers request the same runtime files, so the second tier adds only its model file to what the browser must fetch when the runtime is still cached. Best quality uses a different runtime entirely: the BiRefNet_lite export (59.1 MB) plus the onnxruntime-web binary and loader (27.8 MB), reported as ~86.9 MB on first use. The megabytes are SI megabytes of one million bytes, rounded to one decimal, which is why the raw byte counts sit beside them.
Self-hosted under /vendor/, with provenance
None of these files comes from a CDN or any third-party host. The MediaPipe runtime and both
.tflite models live under /vendor/mediapipe/; the BiRefNet_lite model
and the onnxruntime-web runtime live under /vendor/birefnet/ and
/vendor/onnxruntime/. Each directory ships a provenance note alongside the files —
source URLs, versions and repository revisions, checksums, the derivation and verification chain
for the ONNX export, and the full license text — and, for the BiRefNet_lite model and the
onnxruntime-web files, a test suite pins every vendored byte size and the runtime version, so
those notes cannot silently drift from what is served. Self-hosting is what makes the privacy
claim checkable rather than merely stated: if the only host your browser talks to is this one,
there is no third party to receive an image. Running WebAssembly required exactly one addition
to this site's Content-Security-Policy,
'wasm-unsafe-eval' in script-src, verified against a server sending
the production headers.
Verify it yourself in DevTools
The methodology page describes the check in one sentence; here it is as a procedure, with what you should expect to see at each step.
- Open the background remover (or the maker), then open your browser's developer tools and switch to the network panel. Clear it so only new requests show.
- Choose a photo. The file is read by local JavaScript and shown from memory; no request appears that carries it.
-
Pick a tier and run the cutout. You will see requests to this site's
/vendor/…paths — the files in the table above for that tier — and none to any other host. - Compare the decoded resource (content) size column with the byte counts above — the transfer column is smaller when the server compresses a file. They are the same files.
- Adjust the edge, paint with the restore or erase brush, and download the PNG. Each of those is canvas work in your tab; none of them produces a request.
What you will not see is any request that sends the image: no upload, no request to a third-party domain, no image bytes leaving the page. The "on first use" wording on the picker is literal — the model files are ordinary static assets, fetched the first time a tier runs.
The 512×512 constraint and the CPU-only decision
Best quality runs BiRefNet_lite as a 512×512 ONNX export with 4-bit-quantized weights. That input size is not a stylistic choice but a measured constraint: the full 1024×1024 export needs roughly 6 GB of working memory at inference, which exceeds the ~4 GB WebAssembly heap every browser gives a page — so the previous 1024 artifact failed for everyone, and the shipped one was verified to actually run and to reproduce the reference model's masks (the mask-parity measurements are in the provenance note). The consequence for you is simple: the model looks at a 512×512 version of the photo, and the confidence mask it returns is scaled back up to the photo's real dimensions and refined there, so the PNG you download has the same pixel size as the file you chose.
The model executes on onnxruntime-web's WASM CPU provider inside a worker thread, so the page stays responsive while it works. The WebGPU provider was tested and produced silently wrong output for this model on the pinned runtime version — inference resolved as if nothing had failed and returned a useless mask — so it is deliberately not used. A quality tier that quietly returns an empty mask would be worse than a slightly slower one that is right. That decision is revisited only when a runtime upgrade passes a known-answer check, never by assumption.
When a model will not load
A model file can fail to arrive (a blocked request, a proxy, an interrupted connection), a runtime can fail to start, or an inference can fail part-way. In the maker, if the model fails to load, the cutout toggle disables with a message and the rest of the editor keeps working — nothing else on the page depends on the model. The remover's engines follow the same doctrine: a failure is thrown honestly to the page rather than swallowed. The MediaPipe engines clear a failed load so the next attempt retries from scratch; the best-quality engine goes further — a failed download or session creation clears its singleton, and a failed inference disposes the session and clears the singleton before the error reaches the page, so a retry never reuses a dead session. It also tags each failure with the phase it happened in — download, session creation, or inference — so the status message can say what actually went wrong rather than blaming your connection for an out-of-memory error. On the remover the Remove button comes back after a failure, so you can try again.
Input rules, computed from the engine
Before any model runs, the file is checked by a pure function with its own tests. It accepts
image/png, image/jpeg, image/webp and refuses anything over 20 MiB (20,971,520 bytes — the "20 MB" the error message names). Rejections name the actual
problem: the real type of an unsupported file, the real limit for an oversized one, and a
distinct message for a zero-byte file, which is a common symptom of a failed download or cloud
sync. The verdicts below were produced by calling that function at build time:
| Input | Verdict |
|---|---|
| PNG, 4 MiB | Accepted |
| WebP, exactly 20 MiB | Accepted |
| JPG, one byte over 20 MiB | That file is over the 20 MB limit — export a smaller copy first. |
| GIF, 1 MiB | That file type (image/gif) is not supported — use a PNG, JPG, or WebP image. |
| File with no recognised type | That file type (unknown) is not supported — use a PNG, JPG, or WebP image. |
| PNG, 0 bytes | That file is empty (0 bytes) — re-export it and try again. |
The download is always a PNG, a lossless format with a real alpha channel, and its name is
derived from your file's name: extension stripped, anything that is not a letter or digit
collapsed to single hyphens, cut to 60 characters, with
-cutout.png appended. Letters outside ASCII survive; a name with nothing usable falls
back to image. An 80-character base name comes back as
60 characters plus the suffix.
| Uploaded name | Download name |
|---|---|
IMG_0042.jpeg | IMG-0042-cutout.png |
Product shot (final v2).PNG | Product-shot-final-v2-cutout.png |
café photo.webp | café-photo-cutout.png |
....jpg | image-cutout.png |
If you choose a solid background instead of transparency, the color is normalised to lowercase six-digit hex before it is drawn; anything the normaliser cannot read is rejected so the tool falls back explicitly rather than feeding garbage to the canvas:
| Color input | Normalised |
|---|---|
#FFF | #ffffff |
1e90ff | #1e90ff |
#ABCDEF | #abcdef |
white | rejected — falls back |
rgb(0, 0, 0) | rejected — falls back |
Where the cutout lands
Fast mode is the same engine that powers the cutout inside the thumbnail maker, where the result is placed over the maker's own gradients, outlined text, and shapes. The maker's canvases, described by the tested ratio engine, are:
| Canvas | Ratio |
|---|---|
| YouTube — 1280×720 | 16:9 |
| Shorts / Reels / TikTok — 1080×1920 | 9:16 |
| Instagram post — 1080×1080 | 1:1 |
Do the cutout on the remover page if you only want the PNG; do it in the maker if you are building the whole thumbnail on the 1280×720 canvas. The size rules for the finished image are on the size guide, and YouTube thumbnail sizes, explained covers why 1280×720 is the size that satisfies every upload path; the other mechanisms are under guides.
Common misreadings
- "Local means no download." Local means the model runs on your device, which is exactly why it must first reach your device. The download is the model, not your photo.
- "The 512×512 model produces a 512×512 cutout." The model analyses at that size; the mask is scaled back to the photo's real dimensions and refined there, and the PNG matches your input's pixel size.
- "The GPU would be faster, so it should be on." On the pinned runtime version it produced silently wrong output for this model. Correct output on the CPU provider wins over wrong output delivered quickly.
- "A big model download means the site is collecting something." Watch the network panel: the requests are static files from this site to you, and nothing travels the other way.
Frequently asked questions
Is my photo uploaded when I pick “Best quality”?
No. The direction of the bytes is the opposite: your browser downloads the BiRefNet_lite model and the onnxruntime-web runtime from this site (~86.9 MB on first use, measured from the served files at build time) and runs them in a worker thread in your tab. The photo becomes pixels in memory and never leaves the page. You can watch this in your browser's DevTools network panel: the only requests are model and runtime files from this site, and no image bytes are transmitted.
Why is the first run such a large download, and does it repeat every time?
Because the model has to reach your device to run there. Fast mode is ~11.7 MB on first use (the MediaPipe runtime plus the selfie-segmentation model), “Click the subject” is ~17.7 MB on first use (the same runtime plus the MagicTouch model), and Best quality is ~86.9 MB on first use. The labels say “on first use” because the files are ordinary static assets your browser fetches the first time a tier runs; every size is a sum of real byte counts, not an estimate.
Why does Best quality analyse the photo at 512×512?
Memory. The full 1024×1024 export of BiRefNet_lite needs roughly 6 GB of working memory at inference, which exceeds the ~4 GB WebAssembly heap every browser gives a page — so that artifact failed for everyone. The 512×512 export with 4-bit-quantized weights fits, and the mask it produces is scaled back up to your photo’s real dimensions and refined there, so the output PNG is the same size as your input.
Why is the GPU (WebGPU) not used?
Because it was tested and produced silently wrong output for this model on the pinned onnxruntime-web 1.29.0: inference completed as if nothing had failed and returned a useless mask. A quality tier that quietly returns an empty cutout is worse than one that is right, so the tool runs on the WASM CPU provider inside a worker thread and WebGPU stays off.
What happens if a model fails to load?
In the maker, the cutout toggle disables with a message and the rest of the editor keeps working. On the remover, the status message says what failed — for Best quality it names the phase: download, engine start, or inference — and the Remove button comes back so you can retry. Nothing is silently substituted, and a failed load is not cached: the engine clears its state so a later attempt starts clean instead of reusing a dead session.
Which files are accepted, and how is the output named?
image/png, image/jpeg, image/webp images up to 20 MiB (20,971,520 bytes — the “20 MB” the error message names). Unsupported formats are rejected with their real type named, empty files get a distinct message, and the download is always a PNG named after your file with “-cutout.png” appended — for example IMG_0042.jpeg becomes IMG-0042-cutout.png.
Every figure on this page is measured from the vendored files or computed by the tested engines at build time. The segmentation runs locally in your browser and photos are never transmitted; see the methodology page for how the engines are tested.