Course outline
Context Loss and Recovery
In one line
The GPU can take a canvas's WebGL context away while the page is open. three.js rebuilds its own objects when it comes back, but anything you drew once, or made with your own WebGL code, is empty until you rebuild it.
Think of it like this
Someone wipes the whiteboard in your meeting room. The pens are still in your hand and the room is still yours, but everything you drew is gone, and a drawing you made only once must be drawn again. A lost context wipes the GPU's whiteboard.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- context loss
- The browser takes back a canvas's connection to the GPU. Everything stored on the GPU for that canvas is gone.
- preventDefault
- A call on the event that says "I handle this myself". In
webglcontextlostit asks the browser to bring the context back later. - render target
- A texture a scene is drawn into instead of the screen. Its pixels live only in GPU memory.
- WEBGL_lose_context
- A browser extension for tests that loses and restores a context on purpose.
Step by step
01Draw, lose, restore
The first example draws an orange box (centre pixel 255,107,44), calls loseContext() and waits for the event. isContextLost() becomes true. renderer.render() while lost returns at once and does not throw.
02See the box come back by itself
After restoreContext() the next draw() shows 255,107,44 again with no code of ours: the mesh, material and program were rebuilt from JavaScript data.
03Paint a target once
In the second example a 64 × 64 render target is painted orange once and shown on a plane every frame. Before the loss the centre is 255,106,42. After restore, with no repair, it is 0,0,0: the target was reallocated but not repainted.
04Repaint on restore
paintTarget() in the restored handler brings back 255,106,42. The rule: for every thing you create once on the GPU, write down how to make it again.
Try it yourself
- 1
Skip the repaint
In the second example set
REPAINTtofalse. The last line,after repainting: ..., disappears and the centre stays0,0,0: nothing else tells you the target is empty. - 2
Make only 16
In the third example change the
20to16andcontexts[19]tocontexts[15]. No context is lost: it printslost: 0and an empty list.
Code & diagrams
The loss is simulated with WEBGL_lose_context. A real loss fires the same two events.
Console
Run the scene to see its console.Expected console output
1. drawing: centre pixel 255,107,44 | lost: false
THREE.WebGLRenderer: Context Lost.
2. context lost: lost: true | render() while lost: no error
THREE.WebGLRenderer: Context Restored.
3. restored: lost: false | centre pixel 255,107,44The centre pixel is read from the canvas. Set REPAINT to false to see the blank result stay.
Console
Run the scene to see its console.Expected console output
before the loss: centre pixel 255,106,42
THREE.WebGLRenderer: Context Lost.
THREE.WebGLRenderer: Context Restored.
restored, no work: centre pixel 0,0,0
after repainting: centre pixel 255,106,42Chromium's limit; other browsers differ. The devtools console also shows the browser's warning.
Console
Run the scene to see its console.Expected console output
contexts made: 20 | lost: 4 | the oldest ones: 0, 1, 2, 3
the newest (19) is alive: trueBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Forget preventDefault
On a plain canvas, listen for webglcontextlost without calling event.preventDefault(), then call restoreContext() 100 ms later.
Break #2
Reuse a raw WebGL buffer after the restore
On a raw WebGL 2 canvas (like Phase 14's glow overlay) keep the buffer you made before the loss and call gl.bindBuffer(gl.ARRAY_BUFFER, buffer) after webglcontextrestored.
Myth vs fact
Myth
three.js handles context loss completely.
Fact
It handles its own objects. Pictures painted once into render targets, environment maps made from a scene by PMREM (an environment loaded from an HDR file is rebuilt from its data), and any raw WebGL canvas you created are yours to rebuild.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Restoring is lazy: after
initGLContext()nothing is uploaded until a mesh is drawn, so the first frame after a restore is slower. Expect a hitch. - ▸
WebGPU has no
webglcontextlost. A lost device reports throughdevice.lost, a promise. The showroom's WebGPU path does not recover from it and says so inmain.js; a production app on that path should show a message and restart.
Remember this
- 1
The problem this solves: a laptop waking from sleep, a restarting graphics driver, or a browser reclaiming memory can end a context. The page stays open but the picture freezes or goes black, with no message.
- 2
Two events. The browser fires
webglcontextloston the canvas, then, if it can,webglcontextrestored. In the lost handler you must callevent.preventDefault(): that is how you tell the browser you want the context back. three.js'sWebGLRendereralready does this and keeps a flag, sorender()returns at once while the context is lost (seeonContextLostandonContextRestoreinWebGLRenderer.js). - 3
What three.js restores. On
webglcontextrestoredit callsinitGLContext(), which makes new empty helper objects (properties, programs, textures, buffers). Meshes, geometries, materials and ordinary textures come back by themselves: the nextrender()uploads them again from the JavaScript objects that still hold the data. The first example draws, loses the context, and draws again. - 4
What you must restore. Anything whose contents exist only on the GPU. A render target (Topic 15.1) painted once, such as a baked reflection, a shadow or a PMREM environment made from a scene, has its memory back, empty. The second example paints a target once and shows the centre pixel go from
255,106,42to0,0,0after restore. Your own WebGL contexts, like Phase 14's glow overlay, are separate canvases: three.js knows nothing about them. - 5
How to test it. Browsers offer the
WEBGL_lose_contextextension for exactly this test:gl.getExtension("WEBGL_lose_context").loseContext()and.restoreContext()simulate the event pair;renderer.forceContextLoss()does the same through three.js. CallingrestoreContext()in the same instant as the lost event did nothing in our tests, so wait 100 ms first. - 6
The 16-context limit. Chromium keeps at most 16 live WebGL contexts per page. Making a 17th makes the browser lose the oldest, with a console warning:
WARNING: Too many active WebGL contexts. Oldest context will be lost.The third example makes 20 and shows which survive. A page with many small 3D widgets must share one renderer or create canvases only while visible.
Explain it without notes
Which things does three.js bring back after webglcontextrestored, and which do you have to rebuild? Give two examples of each.
Why is calling event.preventDefault() in the lost handler important?
Practice
Write the two handlers for a page whose render loop is loop: stop it when the context is lost, start it when restored.
A scene paints a static reflection into target once at load. Add the code that keeps it correct after a restore.
Done when you can
You can lose and restore a context on purpose, with
WEBGL_lose_contextorforceContextLoss().You can say what three.js rebuilds and what you must.
You always call
preventDefault()in a lost handler.You know the 16-context limit in Chromium and how to stay under it.