Choosing Between WebGL and WebGPU for a Browser 3D Application
2026-08-11 · 10 min read
Somewhere near the start of any browser-based 3D project, someone asks whether it should be built on WebGPU. The question usually arrives with an assumption attached: that WebGL is the old way, WebGPU is the new one, and picking the new one is the safer long-term bet. That framing produces bad decisions, because the two APIs differ along four axes that matter to a delivery schedule, and only one of them is about age.
The four are browser reach, download size, which shader language the team is committed to, and whether the application needs the GPU to compute things rather than only draw them. Three.js supports both APIs, which takes the question of feasibility off the table entirely and leaves those four costs to weigh. Here is what each one is worth in numbers you can check.
Browser reach is the constraint most projects discover last
WebGL 2 reaches 95.73% of tracked browsers.[1] WebGPU reaches 85.56%.[2] Ten percentage points would be tolerable if the gap were spread thinly across old devices. It is not. Chrome and Edge have shipped WebGPU since version 113, Opera since 99, Samsung Internet since 24, and Safari carries partial support from 26.0 on both desktop and iOS. Firefox ships it disabled by default, and the compatibility data still records that through version 156.[2]
Firefox is the whole problem. A browser with a small share is a rounding error in a support matrix; Firefox is the browser a reviewer, a procurement officer, or an internal IT department is running when they open the link you sent. When the canvas fails there, the report says the application is broken, and the explanation that a browser vendor has not finished shipping a graphics API does not travel well through three layers of management.
The practical consequence is that WebGPU-only is not a deployable choice today for anything with a general audience. A WebGL path has to exist. That changes the question from which API to build on into which one is the default and which one is the fallback, which is a much smaller question.
Both renderers ship in the same package, and one falls back on its own
Three.js publishes separate renderer entry points that share a common core module. Reading the r182 source, WebGPURenderer sets up its own fallback in the constructor: unless you pass forceWebGL: true, it selects the WebGPU backend and registers a fallback function that builds a WebGL 2 backend instead, logging WebGPURenderer: WebGPU is not available, running under WebGL2 backend. when it fires.[6]
One renderer class therefore covers Chrome and Firefox from the same code, with no capability detection written by hand. For a team that expected to maintain two rendering paths, that is a significant amount of work that does not need doing.
The fallback has a boundary worth understanding before relying on it. It swaps the backend underneath the renderer, not the material system above it. WebGPURenderer is built on Three.js node materials and TSL, the library's own shader-composition layer, whose r182 entry point exports 629 symbols. Shaders written directly in GLSL against ShaderMaterial do not travel across that boundary, because the obstacle was never the backend. WGSL and GLSL are separate languages with separate type systems, and Three.js resolves the difference by asking you to write in TSL instead and emitting whichever language the active backend needs.[5]
That design is sound, and it still means a project with a GLSL shader library has a rewrite in front of it. Anyone estimating a WebGPU migration should count those files first, because that number will dominate every other cost discussed here.
Download size and asynchronous startup
The Three.js build files can be measured directly. Gzipping the r182 builds at level 9, counting the shared core plus the renderer entry point each path requires:
| Path | Entry point | With shared core | Relative |
|---|---|---|---|
| WebGL | 82 KB | 180 KB | 1.00× |
| WebGPU | 167 KB | 265 KB | 1.47× |
An additional 85 KB of compressed JavaScript matters for a marketing page with a decorative canvas and matters much less for an application whose entire purpose is the 3D view. The figure is useful mainly because it tells you which of those two things you are building.
A second cost never shows up in a bundle report. Acquiring a GPU device is asynchronous, through navigator.gpu.requestAdapter() and then adapter.requestDevice(), both of which return promises and either of which can come back with nothing.[4] Three.js surfaces this as await renderer.init(), and as of r181 the older renderAsync() approach is deprecated in favour of awaiting init() once when the renderer is created. Any wrapper, test harness, or screenshot pipeline built on the assumption that a renderer works the moment it is constructed needs revisiting, and in most codebases that assumption is buried somewhere unobvious.
Two smaller differences cause confusing bugs during a port. WebGLRenderer defaults alpha to false while the WebGPURenderer options default it to true, so a canvas that was opaque by accident becomes transparent. And WebXR does not run on the WebGPU backend at all: the XR manager throws and tells you to pass forceWebGL: true.[6] An application with an immersive mode has a WebGL requirement regardless of what the rest of the browser market does.
In a React codebase the decision is coupled to a React upgrade
This is the part that surprises technical leads, because it moves the work out of the graphics layer entirely. React Three Fiber version 8 types its gl prop to accept a factory function returning any object with a render method, so passing a WebGPURenderer through it works today without upgrading anything.
The supported path is fiber 9, and fiber 9 declares a peer dependency on react: ">=19 <19.3". Drei 10, the companion helper library almost every R3F project depends on, requires fiber 9 and react: ^19. A project still on React 18 therefore faces this ordering:
| Step | Requires | Blocked by |
|---|---|---|
Pass WebGPURenderer via the factory prop | fiber 8 | nothing, works now |
| Upgrade to fiber 9 | React 19 | a React major migration |
| Upgrade to drei 10 | fiber 9 and React 19 | the same migration |
| Move custom shaders to TSL | node materials | size of the shader library |
A renderer change that was scoped as graphics work turns out to depend on a React major upgrade that touches routing, suspense behaviour, and every third-party component in the tree. That is worth establishing during planning, while the estimate can still absorb it.
The capability that changes the answer
For drawing a scene that WebGL 2 already draws, WebGPU's advantage is CPU overhead. Its per-draw and per-object validation cost is substantially lower, because validation is front-loaded into pipeline objects created once rather than repeated on every call.[5] A scene bottlenecked on thousands of individually transformed draw calls will show that difference in its frame budget.
Most scenes are not bottlenecked there, and it is worth being blunt about the alternative explanations. Scenes usually feel slow because of device pixel ratio, overdraw, object count, or a canvas that mounts eagerly on page load. All four are cheaper to fix than a renderer migration and should be ruled out first. On our own site, five separate Three.js canvases run behind page content, the busiest spawning fifty meshes on a desktop viewport and fifteen on a phone with pixel ratio clamped at 1.5, and none of them would render measurably differently under WebGPU. That is the normal case, not an unusual one.
Compute is the exception, and it is the one argument for WebGPU that holds up under pressure. WebGPU exposes compute pipelines and storage buffers as first-class objects, so particle integration, GPU-side culling, mesh processing, and physics can run on the GPU without the texture-encoding workarounds WebGL forces.[5] A roadmap containing GPU-driven simulation, culling across large instanced geometry, or geometry preprocessing that needs to leave the main thread has a concrete reason to pay the costs described above, and for that kind of project the costs are modest against what the capability enables.
Two implementation details save time on a first attempt. WebGPU requires a secure context, so HTTPS or localhost with no exceptions.[4] And requestAdapter() accepts featureLevel: "compatibility", which asks for a restricted subset aimed at OpenGL ES 3.1 and Direct3D 11 rather than the full feature set.[4] On older or driver-constrained machines that option is sometimes the difference between getting an adapter and getting null, and it is worth trying before concluding the hardware cannot run WebGPU.
A decision procedure
Five questions, in this order, settle it for most projects.
Does the browser matrix treat Firefox as a first-class target? If it does, both paths ship. Three.js already handles this through the automatic fallback, so the remaining choice is only which backend leads.
Is there an immersive or WebXR mode, now or planned? That mode runs on WebGL, by library constraint rather than preference.
How many hand-written GLSL shaders exist? Count the files. TSL is where they end up, and this number sets the size of the project.
Has the performance problem been profiled? Pixel ratio, overdraw, object count, and eager mounting account for most complaints about slow 3D on the web, and none of them are fixed by changing renderers.
Does the roadmap require GPU compute? A yes here outweighs the other four answers, because nothing in WebGL supplies that capability at any level of effort.
For a new project with a general audience and no compute requirement, WebGLRenderer remains the reasonable default in 2026, with the shader layer kept clear of hand-written GLSL so the door to WebGPU stays open. For a project rendering large instanced geometry with culling that belongs on the GPU, WebGPU is worth the bundle, the async initialisation, and the React version coupling, and the browser gap gets covered by the fallback that already exists.
The framing that treats one of these as modern and the other as legacy misreads the situation. WebGL 2 is a finished specification, at near-universal support, receiving no new features. WebGPU is an unfinished specification with a real gap in one major browser and a capability ceiling far above it. Both of those descriptions will stay true for several years, which is long enough that the choice deserves to be made per project rather than once as a matter of policy.
---
References
- Can I use, WebGL 2.0 support table, 2026. Reports 95.73% global support; Chrome 56+, Firefox 51+, Safari 15+, Edge 79+.
- Can I use, WebGPU support table, 2026. Reports 85.56% global support; Chrome and Edge 113+, Opera 99+, Samsung Internet 24+, Safari partial from 26.0, Firefox disabled by default through version 156.
- W3C, WebGPU, Candidate Recommendation Draft, 10 August 2026. States that WebGPU is not related to WebGL and does not explicitly target OpenGL ES, and is designed to map to post-2014 native GPU APIs.
- MDN Web Docs, GPU.requestAdapter(), Mozilla, 2026. Adapter and device acquisition, the secure-context requirement, and the
featureLevelandpowerPreferenceoptions. - MDN Web Docs, WebGPU API, Mozilla, 2026. WGSL compared with GLSL, compute pipelines and storage buffers, and reduced per-object CPU overhead relative to WebGL.
- Three.js r182 source,
src/renderers/webgpu/WebGPURenderer.jsandsrc/renderers/common/XRManager.js. Backend selection, theforceWebGLoption, the automatic WebGL 2 fallback, and the WebXR restriction.
Bundle figures were measured against the [email protected] build files, gzipped at level 9. Dependency constraints were read from the published peerDependencies of @react-three/[email protected] and @react-three/[email protected]. The current npm release at the time of writing is [email protected].
EFFOMA builds real-time 3D and spatial visualization systems for the web. See our technology portfolio or contact us.
Last updated: August 11, 2026