Restore Lost WebGL Context After Hardware Reset

Handling WebGL context loss is an essential requirement for building resilient web graphics applications. When a GPU experiences a hardware reset, driver update, or resource exhaustion, the browser discards the WebGL context and wipes all stored GPU resources. Restoring the context involves capturing context-loss events, preventing the browser's default abort behavior, reinitializing rendering pipelines, and reloading all textures, buffers, and shaders once the hardware becomes available again.

1. Intercept the Context Lost Event

When the graphics card resets, the canvas fires the webglcontextlost event. By default, browsers treat this event as fatal and will not attempt to restore the context. To allow recovery, you must add an event listener and call event.preventDefault().

At this stage, you must also stop your render loop to prevent your application from issuing commands to an invalid context.

const canvas = document.getElementById("glcanvas");
let animationFrameId;

canvas.addEventListener("webglcontextlost", (event) => {
    event.preventDefault();
    cancelAnimationFrame(animationFrameId);
    console.warn("WebGL context lost. Pausing rendering...");
}, false);

2. Listen for Context Restoration

Once the operating system and GPU stabilize, the browser creates a new underlying graphics context and dispatches the webglcontextrestored event to the canvas.

Attach an event listener for webglcontextrestored to trigger your resource reconstruction logic:

canvas.addEventListener("webglcontextrestored", () => {
    console.log("WebGL context restored. Rebuilding resources...");
    reinitializeWebGL();
}, false);

3. Re-create All GPU Resources

A restored WebGL context is completely blank. The original WebGLRenderingContext or WebGL2RenderingContext instance remains the same JavaScript reference, but all previously allocated WebGL objects are invalid.

Your initialization routine must explicitly re-execute the following tasks:

  • Shaders and Programs: Recompile vertex and fragment shaders, attach them to new program objects, and re-link them.
  • Uniform Locations: Query and cache all uniform and attribute locations from the newly linked programs.
  • Buffers: Generate new vertex buffer objects (VBOs) and index buffers (IBOs), binding them and repopulating them with your geometry data.
  • Textures: Recreate texture objects, set texture parameters (filtering, wrapping), and re-upload image data to the GPU.
  • Framebuffers and Renderbuffers: Reallocate any off-screen render targets and depth/stencil buffers.
  • State Settings: Re-enable required states, such as depth testing (gl.DEPTH_TEST), blending modes, cull face settings, and clear colors.

4. Restart the Render Loop

After all assets and states are fully loaded back into the GPU, resume the application's render loop.

function reinitializeWebGL() {
    const gl = canvas.getContext("webgl2") || canvas.getContext("webgl");
    
    initShaders(gl);
    initBuffers(gl);
    initTextures(gl);
    
    // Restart animation loop
    render();
}

function render() {
    drawScene();
    animationFrameId = requestAnimationFrame(render);
}

Testing the Restoration Workflow

You can simulate hardware resets without crashing your system by using the WEBGL_lose_context extension. This extension allows you to programmatically trigger context loss and restoration to ensure your application recovers smoothly:

const ext = gl.getExtension("WEBGL_lose_context");

// Simulate context loss
ext.loseContext();

// Simulate recovery after 2 seconds
setTimeout(() => {
    ext.restoreContext();
}, 2000);