Over the past month I built and shipped remove.ac, an image background remover split into two explicit execution paths: a free in-browser WebAssembly studio where image bytes stay inside the tab, and an optional stateless Cloud GPU API (POST /v1.0/removebg) compatible with remove.bg payloads.
Running a 1024px segmentation model (onnxruntime-web WASM + guided-filter hair refinement) inside real users' browsers surfaced a few bugs that never showed up on an M-series MacBook during development. Here is what broke, how we fixed it, and the raw telemetry we collected from 2,000+ production runs.
1. WebKit holds onto offscreen <canvas> memory after unmount
Dropping references to temporary <canvas> elements in React was enough in Chromium, but iOS and macOS Safari kept hundreds of megabytes of backing-store memory pinned across batch runs. Explicitly zeroing dimensions (canvas.width = 0; canvas.height = 0) in teardown routines reclaimed memory immediately and stopped mobile tab reloads during 50-image ZIP batches on /batch.
2. Bounding manual brush undo history to 64 MB
On a 12 MP product photo, storing 10 undo snapshots as raw RGBA ImageData eats ~480 MB on its own. For the local Erase / Restore / Smart Brush and point/box subject selector, we switched to replaying stroke vectors on top of a rolling checkpoint canvas, capping undo history at 12 steps within a 64 MB ceiling.
3. Never silently fall back from local WASM to a backend server
A subtle trust bug in "hybrid" web tools is catching an out-of-memory WASM error and quietly uploading the file to a cloud worker so the task succeeds. If someone picked on-device processing for an ID scan or unreleased product shot, silent fallback violates the whole point. On remove.ac, local failures stay local; Cloud GPU runs only when a signed-in user clicks it or calls the API with an X-Api-Key (rac_live_...).
4. Bypassing serverless ~4 MB gateway caps without storing user files on S3
Standard serverless HTTP gateways choke around 4.5 MB, which fails as soon as a studio photographer outputs a 15 MB lossless PNG. Instead of staging uploads in an object bucket, our zero-dependency SDKs and CLI request a single-use HMAC ticket from POST /v1.0/removebg/ticket (placing a Postgres FEFO credit hold) and stream up to 25 MB / 40 MP directly into volatile GPU worker RAM. If inference errors out or times out, the credit hold releases automatically at zero cost.
Open SDKs (MIT) and Real-Device Latency Dataset (CC BY 4.0)
- Zero-dependency Node.js & Python SDKs +
--watchfolder CLI: github.com/szp2005/remove-ac-sdk (includes a 1-linefrom removebg import RemoveBgdrop-in shim). - Rolling 90-day real-device WASM benchmark dataset: remove.ac/benchmarks — publishes p50/p95 download, session-init, and inference timings across desktop/mobile CPU buckets (raw JSON and CSV under CC BY 4.0, no cherry-picked single-machine numbers).
If you want to poke at the browser WASM studio (no login wall) or test /v1.0/removebg with the free signup credit, everything is live at https://remove.ac. Happy to share details on the guided-filter worker pipeline or the Postgres FEFO ledger SQL!


