Technical Deep Dives6 min read

The "White Flash" Bug: What Running Out of RAM Looks Like in a Browser Tab

Process a long video, the tab flashes white and reloads, your work is gone. That's not a crash you can catch — it's the renderer being killed for OOM. Why it happens and how streaming frames fixes it.

A user processes a 30-second clip for background removal. Two-thirds through, the tab flashes white, reloads itself, and the job is gone. "I think I ran out of RAM," they reported. They were exactly right — and the fix isn't a try/catch, because by the time it happens your JavaScript is already dead.

Why you can't catch it

When a browser tab exceeds its memory budget, the browser doesn't throw a JavaScript exception you can handle. It kills the renderer process and reloads the tab. There is no catch block for "the process that was running your catch block no longer exists." The only fix is to never allocate that much in the first place.

The shape of the bug

The naive way to process video frames is intuitive and fatal:

decode ALL frames → array of N bitmaps → process each → encode

For a 30-second 30fps clip that's ~900 full-resolution ImageBitmaps held in memory simultaneously before processing even starts. At 1080p, each frame is ~8 MB of pixel data; 900 of them is ~7 GB. The tab dies long before frame 900.

The fix: stream, don't hoard

The bounded-memory version processes in a pipeline and releases as it goes:

for each frame:
  decode 1 frame → process → encode/append → release the bitmap

The discipline that makes this work:

  • bitmap.close() per frame. ImageBitmap holds memory outside the JS heap; it isn't freed by garbage collection alone. You must explicitly close each one the moment you're done drawing it.
  • Transfer, don't copy, to workers. When sending a frame to an inference worker, transfer the bitmap (move ownership) rather than clone it.
  • Worker isolation. Run the heavy work in a worker so that if it hits a memory wall, the worker dies — not the whole page. The page can then surface a real error instead of vanishing.
  • A bounded GIF decoder. Our GIF path keeps one running canvas buffer and composites each frame onto it, rather than allocating one full canvas per frame.

The honest limit

Streaming gets you a long way, but a browser tab still has a hard memory ceiling that a server doesn't. So we also cap input dimensions before the pipeline (a crisp 1024–1536px result beats a tab crash on a 12-megapixel phone photo), and we surface a clear message when a clip is genuinely too large for this device — instead of letting it flash white and pretend nothing happened.

The takeaway

The "white flash" isn't a bug in your code; it's the browser defending itself from your memory usage. You don't fix it with error handling — you fix it by never holding the whole clip at once, closing every bitmap you create, and isolating the heavy work so a failure is recoverable instead of catastrophic.