WebGL: What It Is and Why It Still Runs the 3D Web
WebGL is the browser API that draws hardware-accelerated 3D graphics into a <canvas>. It has been in every major browser for over a decade, it needs no plugin and no install, and it is what almost every piece of 3D you have seen on the web is running on — including, most likely, the last product configurator, map, or scrolling animation that impressed you.
It is also frequently written off as legacy now that WebGPU exists. That is premature. WebGL 2 is available on effectively every browser in use, no vendor has announced any plan to remove it, and for the majority of commercial 3D work on the web it remains the correct default rather than the compromise.
Last checked 31 August 2026 · examples verified against three@0.181.1. Written by Peter Csipkay.
What WebGL is, precisely
WebGL is a JavaScript binding to OpenGL ES, the embedded variant of OpenGL that phones and consoles use.
- WebGL 1.0 maps to OpenGL ES 2.0, and shipped in browsers from 2011.
- WebGL 2.0 maps to OpenGL ES 3.0, and brings the things WebGL 1 conspicuously lacked: 3D textures, multiple render targets without an extension, transform feedback, instanced drawing in core, and integer support in shaders.
If you are starting today, target WebGL 2. WebGL 1 is a compatibility concern for genuinely ancient devices and the extension-juggling it requires is not worth carrying.
Crucially, WebGL is a drawing API, not a scene graph. It has no concept of a model, a light, or a camera. It knows about buffers of numbers, programs that transform them, and a framebuffer to write into. Everything you think of as 3D — a mesh with a material, positioned in a world, lit — is a convention built on top by a library.
That is why almost nobody writes raw WebGL. Three.js exists to supply exactly those missing concepts, and the practical skill for web 3D is Three.js plus enough WebGL to understand what it is doing underneath.
How the pipeline works
Understanding this is what turns performance debugging from guesswork into reasoning. A draw call in WebGL goes through these stages:
- You supply buffers. Vertex positions, normals, texture coordinates, indices — arrays of numbers uploaded to GPU memory.
- The vertex shader runs once per vertex. Its job is to output a clip-space position. This is where model, view and projection transforms happen.
- The rasteriser turns triangles into fragments. This is fixed-function; you do not control it. It decides which pixels a triangle covers, and interpolates the vertex outputs across them.
- The fragment shader runs once per fragment. Its job is to output a colour. This is where lighting, texturing and every visual effect happens.
- Per-fragment tests and blending. Depth test, stencil, alpha blending, then the write to the framebuffer.
Two facts from that list explain most performance problems I get called about:
The fragment shader runs an enormous number of times. A full-screen effect on a retina display at 2× density is roughly eight million invocations per frame. Anything you can move from the fragment shader to the vertex shader — or out of the shader entirely into a uniform computed once on the CPU — is a multiplier, not a saving.
Overdraw is invisible and expensive. If ten transparent surfaces stack up, every pixel behind them runs the fragment shader ten times. Transparency is the single most common reason a scene that "looks simple" runs at fifteen frames per second.
Where the limits actually bite
WebGL's real constraints are rarely the ones people worry about. In roughly the order I encounter them:
Draw calls, not triangles. Modern GPUs eat triangles. What they do not tolerate is being interrupted. Every draw call carries CPU-side validation and state-change cost, and in WebGL that cost is high. A scene of a thousand separate small objects will perform far worse than one merged mesh with the same triangle count. Merging and instancing are the fix, and InstancedMesh is the tool.
Texture memory, not texture files. A 2048×2048 texture is perhaps 1 MB as a JPEG. Uploaded to the GPU uncompressed it occupies about 16 MB, and around 22 MB once mipmaps exist. Six of those and a mid-range phone is out of memory and the tab dies. This is why GPU texture compression — KTX2 with Basis Universal — matters more than the download size everyone optimises for.
No compute shaders. WebGL has no general-purpose GPU compute. The workaround is encoding data into floating-point textures and ping-ponging framebuffers, which works and is unpleasant. If you need real compute, that is the one clean argument for WebGPU.
Single-threaded submission. All WebGL calls happen on the thread that owns the context. You can move work to a worker with OffscreenCanvas, but the API itself is not parallel in the way WebGPU's command buffers allow.
Context loss is real and you must handle it. The browser can take your GPU context away — on tab backgrounding, on a driver reset, under memory pressure, and routinely on mobile. If you do not listen for webglcontextlost and rebuild, your visitor gets a permanently black canvas with no error. This is skipped in almost every tutorial and it is one of the more common causes of "it works on my machine".
Why it is still the right default
I still start most client projects on WebGL, and here is the reasoning I give:
Availability is the feature. WebGL 2 is present on essentially every browser and device in current use. WebGPU availability additionally depends on the GPU, the driver and the platform backend, so it needs a fallback — and the fallback is WebGL. Building the universal path first means the thing works for everyone on day one.
The ecosystem is mature. A decade of Three.js examples, loaders, post-processing passes, Stack Overflow answers and battle-tested workarounds all target WebGL. The WebGPU side of that ecosystem is good and improving quickly, but it is younger, and you will hit more sharp edges.
The bottleneck usually is not the API. When a WebGL project runs badly the cause is almost always assets and scene structure, not the API ceiling: uncompressed textures, unmerged geometry, unnecessary transparency, a post-processing chain nobody has questioned. Those cost the same under WebGPU. Fixing them is cheaper than migrating, and you need to fix them either way.
It buys you optionality. A WebGL project written with the node material system can adopt WebGPU later by changing which renderer it constructs. Starting on WebGPU and needing to support everyone means building the WebGL path anyway — you just built it second, under pressure, and tested it less.
The honest exception is compute-driven work. If your project is a simulation, or needs GPU culling or sorting at scale, WebGL will fight you and you should start on WebGPU.
What you actually feed it
WebGL draws whatever you put in its buffers, but on a real website that content almost always arrives as a glTF file — the format designed for exactly this, sometimes described as "the JPEG of 3D".
The practical constraints have very little to do with the API and everything to do with the asset:
- A CAD export is not a web model. It has millions of triangles, no UVs worth using, and materials that mean nothing to a renderer. It has to be rebuilt, not converted.
- Geometry gets compressed with Draco or Meshopt. Meshopt decodes faster; Draco usually compresses smaller.
- Textures should be KTX2/Basis for GPU-resident compression, for the memory reason above — not merely resized JPEGs.
- The budget that matters is not one number, but total download, GPU memory, and draw calls together.
If you are at the stage of choosing or preparing assets rather than choosing an API, 3D models for websites covers formats, weight budgets and licensing, and 3D model optimisation covers turning a file that will not load into one that will.
Where to start
Do not start with raw WebGL. Writing the boilerplate for a textured, lit cube teaches you the API's ceremony rather than the concepts, and you will not use that boilerplate again.
Start with Three.js and learn the concepts it exposes — scene, camera, geometry, material, light, the render loop. Then go one level down when something puzzles you, which it will: why transparency sorts oddly, why your normals look wrong after scaling, why the frame rate collapsed when you added a second light.
Once you are comfortable, the highest-leverage next step is shaders, because that is where the visual ceiling actually is. Everything distinctive-looking on the web is a custom shader. And when you get there, consider writing them in TSL rather than raw GLSL — it targets both WebGL and WebGPU, so the work you do now still runs when you eventually change backend.
F.A.Q
Frequently asked questions
Is WebGL still worth learning in 2026?
Yes. It runs on effectively every device, it is what almost all existing web 3D is built on, and it is the fallback for every WebGPU project that has real users. More practically, the concepts transfer: the pipeline, the shader stages, the cost model and the debugging instincts are the same under both APIs.
What is the difference between WebGL 1 and WebGL 2?
WebGL 1 maps to OpenGL ES 2.0 and WebGL 2 to OpenGL ES 3.0. WebGL 2 brings 3D textures, multiple render targets, transform feedback, instanced drawing in core rather than via an extension, and integer support in shaders. Target WebGL 2 unless you have a specific reason not to; WebGL 1 means carrying extension checks for devices almost nobody uses now.
Do I need to know WebGL to use Three.js?
No to start, yes to get good. You can build a working scene knowing nothing about the underlying API. But the moment you need to debug a performance problem, understand why transparency looks wrong, or write a custom shader, the abstraction stops hiding it — and at that point knowing what a draw call costs and how many times a fragment shader runs is what separates guessing from fixing.
Why is my WebGL scene slow when it has so few polygons?
Polygon count is rarely the culprit. The usual causes, in order: too many separate draw calls where objects should be merged or instanced; oversized textures eating GPU memory; overdraw from stacked transparent surfaces; and post-processing passes each costing a full-screen fragment shader run. Profile which of those it is before optimising geometry, which is usually the least of it.
What is WebGL context loss and do I have to handle it?
The browser can revoke your GPU context — on tab backgrounding, driver resets, or memory pressure, and it happens routinely on mobile. If you do not listen for the webglcontextlost event and rebuild your resources on webglcontextrestored, the visitor is left with a black canvas and no error message. Yes, you have to handle it, and most tutorials never mention it.
Can WebGL run without a GPU?
Often yes, via software rasterisation — browsers fall back to SwiftShader or similar in environments without usable graphics hardware. It works but is dramatically slower, and it is why performance numbers measured in CI or a virtual machine are a floor rather than anything representative. Always confirm on real hardware.
Is WebGL secure?
It has been hardened considerably over a decade. Shaders are validated and translated by the browser rather than handed to the driver raw, and browsers maintain blocklists for driver and hardware combinations with known vulnerabilities. From a site-building perspective there is nothing extra to do — but that blocklisting is one reason a small share of visitors have no WebGL at all, which is why a non-WebGL fallback state is worth designing rather than ignoring.
Should I use WebGL or WebGPU for a new project?
WebGL unless you have a specific reason otherwise — compute shaders, or a profiled CPU-bound draw-call ceiling. Either way, write your custom shading through the node material system rather than raw GLSL, so switching backends later is a configuration change rather than a rewrite.
Try it
Tools and courses on this site
The API is rarely why it is slow
Nine times in ten a struggling WebGL scene is an asset problem — uncompressed textures, unmerged geometry, a model exported straight from CAD. That is fixable without touching the renderer.
Read about 3D models for the webKeep reading
Related guides
Shaders, Explained for Web Developers →
The mental model that makes shader code stop being cryptic: what runs where, how often, and why you cannot use an if-statement the way you expect.
TSL: The Three.js Shading Language →
Write shaders once in JavaScript and compile them to both WGSL and GLSL. The node model, the real syntax, and where it beats writing raw shader code.
3D Models for Websites →
Which format to use, what a model may weigh before it costs you visitors, where to get one legally, and why a CAD export will not work.
Would rather have this built than build it? Interactive 3D Website Development and 3D Product Visualization are the service pages for it, or see everything I do.