Without preventDefault(), a lost WebGL context can never come back
Abbas
July 31, 2026
The report from a phone was: "random full-screen black flashes."
That description sent me looking for a rendering glitch β a shader, a camera cut, a z-fighting artifact. It was none of those. The GPU had dropped the WebGL context, and the page had no way back.
Why it read as a flash instead of a crash
Life Island draws its 3D world to a canvas and its interface β buttons, chat, labels β as ordinary DOM on top.
When the context is lost, the canvas goes black. The DOM HUD carries on painting perfectly, because it has nothing to do with WebGL. So the screen isn't obviously broken; it looks like the world blinked. On a phone, where the browser is aggressive about reclaiming GPU memory the moment you switch apps or the tab backgrounds, this can happen at any time.
Diagnosing it correctly starts with disbelieving the bug report. "Flash" implies something transient and visual. The truth was total and permanent.
The spec detail that is easy to miss
Here is the part that makes this a footgun rather than a mere inconvenience:
The browser fires
webglcontextrestoredonly if you calledpreventDefault()on thewebglcontextlostevent.
If you do nothing, the default behaviour is that the context is gone forever. Not retried, not restored on next paint β gone, until a full page reload. Doing nothing is not a neutral choice; it is opting out of recovery.
canvas.addEventListener('webglcontextlost', (e) => {
e.preventDefault(); // without this, restoration never fires
cancelAnimationFrame(rafId); // stop drawing into a dead context
showReconnectingOverlay();
});
canvas.addEventListener('webglcontextrestored', () => {
reinitRenderer(); // GPU resources are all gone β rebuild them
hideReconnectingOverlay();
rafId = requestAnimationFrame(loop);
});
Restoration is a rebuild, not a resume
The second half of the fix is understanding what you get back. On restore, every GPU-side resource β textures, buffers, compiled programs β is gone. Anything cached in JavaScript that holds a GPU handle is now pointing at nothing. You have to reinitialise the renderer and re-upload, not simply resume the loop.
It is also worth stopping your animation frame while the context is dead. Continuing to issue draw calls into a lost context does nothing useful and generates a stream of errors that will bury whatever you're trying to read in the console.
Reproduce it on purpose
You do not have to wait for a phone to misbehave. The WEBGL_lose_context extension lets you trigger both halves on demand:
const ext = renderer.getContext().getExtension('WEBGL_lose_context');
ext.loseContext(); // black screen
setTimeout(() => ext.restoreContext(), 1500);
If your app cannot survive those five lines, it cannot survive a user taking a phone call.
The general lesson
Any long-running canvas app on mobile should treat context loss as a normal event, not an exception β the same way a mobile network app treats losing signal. Handle it, show the user something honest, and rebuild when it comes back.
Commit 24a6899 in Life Island.