Course outline
Feature Detection and Fallbacks
In one line
Never assume the visitor's browser can draw 3D. Ask it, in order: WebGPU if you want it, then WebGL 2, then show a still image with a text alternative. Each answer is a path, and every path must leave the page useful.
Think of it like this
Before a road trip you check the weather. If there is snow, you take the train: slower, but you arrive. A fallback is your train, decided before you leave. Feature detection is the check; the still image is the train.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- feature detection
- Asking the browser whether it supports something before using it, instead of guessing from its name or version.
- fallback
- What the page does when the better option is not available. Here: WebGL 2 instead of WebGPU, or a still image instead of 3D.
- adapter
- WebGPU's handle for one physical or software GPU.
requestAdapter()returns one, ornullif there is none to use. - poster
- A still picture shown instead of (or before) the live scene.
Step by step
01Pretend there is no WebGL
The first example replaces HTMLCanvasElement.prototype.getContext before anything else runs, so asking for webgl2 or webgl returns null. That is how a blocked GPU looks to your code. It prints null twice, so the page can be tested without a second machine.
02See three.js fail
new THREE.WebGLRenderer() then throws. The example catches it and prints the message. three.js also logs the same text itself, with THREE.WebGLRenderer: in front twice (once from three.js's logger, once from the error). That doubled prefix is the real output.
03Write the matrix as one function
The second example asks in order: WebGPU wanted and a GPU object present? then the adapter, with failures caught; then getContext("webgl2"); else the image. It returns a word the rest of the page switches on.
04Meet the six pretend browsers
It tests an adapter (asked or not), no navigator.gpu, a null adapter, a requestAdapter that throws, and no WebGL 2. Only the last ends at the still image (960 × 600 px, 65-character alt).
Try it yourself
- 1
Take the override away
In the first example, delete the
HTMLCanvasElement.prototype.getContext = ...block (the lines under the comment). The first two lines printWebGL2RenderingContext {}andWebGLRenderingContext {}: this browser has both. The three.js error never happens: thetryblock makes a renderer and prints nothing. - 2
Prove the class is not the test
With the override back, add
console.log("the class exists:", typeof WebGLRenderingContext);after the twogetContextlines. It printsthe class exists: functionwhile both contexts arenull. - 3
Let WebGPU work without WebGL 2
In the second example add the row
["asked, GPU fine, no WebGL 2", { wantsWebGPU: true, gpu: { requestAdapter: async () => ({}) }, canvas: none }]above the last. It printsasked, GPU fine, no WebGL 2 -> webgpu: once WebGPU works, WebGL 2 is never asked.
Code & diagrams
The override affects only this page.
Console
Run the scene to see its console.Expected console output
webgl2 context: null
webgl1 context: null
THREE.WebGLRenderer: THREE.WebGLRenderer: Error creating WebGL context.
three.js says: THREE.WebGLRenderer: Error creating WebGL context.The environment is passed in, so every row is testable. In the showroom, `wantsWebGPU` is `location.search.includes("webgpu")`.
Console
Run the scene to see its console.Expected console output
WebGPU adapter, ?webgpu asked -> webgpu
WebGPU adapter, not asked -> webgl2
no navigator.gpu -> webgl2
adapter is null (blocked) -> webgl2
requestAdapter throws -> webgl2
no WebGL 2 either -> image
poster: 960 x 600 | alt text: 65 charactersBreak 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 to catch requestAdapter
In chooseBackend, change await env.gpu.requestAdapter().catch(() => null) to await env.gpu.requestAdapter().
Myth vs fact
Myth
Checking window.WebGLRenderingContext is enough.
Fact
It only says the browser knows the word. A blocked or software-less machine still has the class and returns null from getContext. Only the context itself tells you.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Detection can lie in one direction: a context that is created can be lost a moment later (Topic 18.2), and a context created with
antialiasorpowerPreferencemay fail while a plain one works. three.js tells these apart:Error creating WebGL context with your selected attributes.means a plain request would have worked.
Remember this
- 1
The problem this solves: the GPU, the driver, the browser and the administrator's settings all have to agree before a canvas can draw. On some devices one of them says no. If your code does not ask first, the visitor sees a blank page, or a script error in the console that nobody reads.
- 2
WebGL 1 is gone from three.js. Since r163
WebGLRendererasks the browser only for a"webgl2"context (seegetContext( contextName, ... )inthree/src/renderers/WebGLRenderer.jsof r186). The three.js migration guide says for r163: "WebGLRendererno longer supports WebGL 1." There is no middle step between WebGL 2 and the still image. - 3
Ask, don't guess.
canvas.getContext("webgl2")returnsnullwhen the browser cannot or will not give one. For WebGPU,navigator.gpumay be missing, and even when it existsawait navigator.gpu.requestAdapter()can resolve tonull(no usable GPU) or reject. Check all of it, and treat every failure as "use the next path". - 4
The matrix. The showroom (Topic 13.1) runs on WebGL 2 and on WebGPU only when the address has
?webgpu. So: WebGPU asked for and an adapter exists, then WebGPU; else WebGL 2 if a context is available; else the still image. The second example runs that function against six pretend browsers. - 5
A blocked GPU is a real case.
chrome://gpulists each feature as hardware accelerated, software only, or disabled. Drivers on the browser's blocklist, remote desktops, virtual machines, a company policy and a setting that turns hardware acceleration off all lead to a missing or very slow WebGL. When three.js cannot get a context it throwsTHREE.WebGLRenderer: Error creating WebGL context.; the second part of the first example shows it.
Explain it without notes
Describe the order of checks for a page that prefers WebGPU, accepts WebGL 2 and has a still image. What can go wrong at each step?
Why does three.js r186 have no WebGL 1 fallback, and what does that change for your fallback design?
Practice
Write supportsWebGL2() so it returns false instead of throwing when getContext itself throws.
Write the three lines that show a #fallback element with a reason and an alt text when the start fails.
Done when you can
You can write the fallback order for your own page as one function.
You can make a page without WebGL, and see what three.js reports.
You know why checking
window.WebGLRenderingContextis not a test.You show a reason and a text alternative in the fallback, not a blank box.