Core Idea
The window above contains no engine — it is raw gl.* calls: one program, one buffer, one draw, and a frame whose entire rhythm is the three calls printed under it. That is the machine this page is about. WebGL is OpenGL ES transplanted into the browser — a context you ask a <canvas> for, and the direct line from JavaScript to the GPU that every 3D thing on the web ultimately runs through. Its scope is famously narrow:
“WebGL only cares about 2 things: clip-space coordinates and colors.” — WebGL Fundamentals
Your vertex shader must produce clip-space positions, your fragment shader must produce colors, and everything else — geometry, cameras, lighting, materials — is your code’s responsibility. The GPU pipeline page maps what happens between those two obligations; this page is about the API that exposes them.
A State Machine
WebGL is stateful: you don’t hand a draw call its inputs — you bind things into global slots (gl.bindBuffer, gl.useProgram, gl.enable(gl.DEPTH_TEST)) and the next draw call uses whatever happens to be bound. This is the API’s eternal footgun: a wrong render three calls later, caused by a binding someone forgot to change, with no error anywhere. WebGL2’s Vertex Array Objects exist precisely to tame it — a VAO snapshots the whole attribute-binding state so it can be restored in one call instead of re-plumbed by hand.
The Boilerplate Is the Lesson
A triangle needs a compiled vertex shader, a compiled fragment shader, a linked program, a buffer upload, attribute pointers describing the bytes, and finally a draw. Hover the left column:
The point of the ladder isn’t that raw WebGL is bad — it’s that the calls on the right are what “a mesh with a material” physically is. One naming trap worth defusing while you’re there: gl.ARRAY_BUFFER is a bind point in the state machine pointing at GPU memory; JavaScript’s ArrayBuffer is CPU-side memory that merely feeds it — the name collision is historical, the systems are separate.
Where Three.js Sits
Three.js is this API with the state machine managed for you:
| You write | It becomes |
|---|---|
Mesh(geometry, material) |
a program + buffers + attribute pointers |
material.color = … |
a uniform upload |
scene.add(…) |
nothing yet — a CPU-side list |
renderer.render(scene, camera) |
the binds, uniforms, and one draw call per object |
That last row is why draw-call counts — the CPU half of the frame — map one-to-one onto scene objects.
Versions and Successors
WebGL2 (the default today) added VAOs, instanced drawing, uniform buffer objects, and GLSL ES 3.0 syntax. WebGPU is the successor rather than the next version: a lower-overhead API with compute shaders, speaking WGSL instead of GLSL — Three.js targets both backends. One platform footnote: on Windows, browsers run WebGL through ANGLE, transpiling your GLSL to Direct3D — invisible day-to-day, but the reason subtle rendering differences appear across platforms.
Connections
WebGL is the browser’s door onto the GPU pipeline; the two programs it demands are written in GLSL, fed by the typed-array machinery of buffers, and obliged to deliver clip-space coordinates. It descends from OpenGL, is abstracted by Three.js, and is being succeeded by WebGPU — same pipeline, newer door.