Handle WebGL Context Loss and Restore in Three.js
Operating systems or graphics drivers can occasionally reset the GPU, causing a WebGL context loss that crashes your Three.js application. This article explains why WebGL context loss occurs in Three.js and provides a step-by-step guide on how to detect this event, pause your application, and successfully restore your 3D scene without requiring a full page reload.
Understanding WebGL Context Loss
WebGL context loss occurs when the GPU resets or becomes unavailable. This can be triggered by hardware mode switches, driver updates, system sleep cycles, or when the browser decides the GPU is overloaded. When the context is lost, all WebGL resources—such as textures, geometries, shaders, and framebuffers—are cleared from the GPU memory.
To handle this gracefully, you must monitor the WebGL canvas for context events, pause your render loop, and rebuild your resources once the context is restored.
Step 1: Detect the Context Loss Event
To detect when a context is lost, add an event listener to the canvas
element associated with your Three.js WebGLRenderer. You
must call event.preventDefault() inside this listener;
otherwise, the browser will not attempt to restore the context.
const canvas = renderer.domElement;
canvas.addEventListener('webglcontextlost', (event) => {
event.preventDefault();
console.warn('WebGL Context Lost. Pausing application...');
// Stop the rendering loop to prevent console errors
stopAnimationLoop();
// Execute custom clean-up logic
onContextLost();
}, false);Step 2: Handle the Context Restoration Event
After the GPU recovers, the browser fires a
webglcontextrestored event. In this listener, you must
reinitialize your renderer state, recreate textures or materials if
necessary, and restart your animation loop.
canvas.addEventListener('webglcontextrestored', () => {
console.log('WebGL Context Restored. Reinitializing...');
// Reset the renderer's internal state
renderer.forceContextRestore();
// Rebuild lost assets (textures, shaders, render targets)
rebuildResources();
// Restart the rendering loop
startAnimationLoop();
}, false);Step 3: Rebuilding Three.js Resources
While Three.js automatically attempts to re-upload standard geometries and materials to the GPU upon context restoration, custom WebGL assets require manual intervention:
- Textures: Re-load or re-assign textures. Call
texture.needsUpdate = trueon all active textures to force Three.js to re-upload them to the new GPU context. - Custom Shaders: Recompile custom
ShaderMaterialinstances. Settingmaterial.needsUpdate = trueforces Three.js to recompile the shaders. - Render Targets: If your application uses
WebGLRenderTargetfor post-processing or shadows, dispose of the old targets using.dispose()and instantiate new ones.
function rebuildResources() {
// Traverse the scene to flag materials and textures for re-upload
scene.traverse((object) => {
if (object.isMesh) {
if (object.material) {
if (Array.isArray(object.material)) {
object.material.forEach(mat => resetMaterial(mat));
} else {
resetMaterial(object.material);
}
}
}
});
}
function resetMaterial(material) {
material.needsUpdate = true;
if (material.map) material.map.needsUpdate = true;
if (material.lightMap) material.lightMap.needsUpdate = true;
if (material.bumpMap) material.bumpMap.needsUpdate = true;
// Repeat for any other texture maps used in your materials
}Testing Context Loss
You can simulate a WebGL context loss to test your implementation.
Use the WebGL extension WEBGL_lose_context to manually
trigger the event in your development console:
const extension = renderer.getContext().getExtension('WEBGL_lose_context');
// Simulate context loss
extension.loseContext();
// Simulate context restoration after 3 seconds
setTimeout(() => {
extension.restoreContext();
}, 3000);