Browser API · GLSL
WebGL
The baseline. Everything on this site runs on it.
The reference scene
Live · running on WebGL in your browser right now
What it is
WebGL is OpenGL ES 2.0 (or 3.0, for WebGL2) exposed to JavaScript. It has shipped in every browser for over a decade, works on effectively every device you will meet, and needs no library, no build step and no permission prompt.
It is also the most verbose option here by a wide margin. Drawing one triangle means compiling two shaders, linking a program, creating a buffer, describing its layout, and binding it — before anything appears. That verbosity is why the labs on this site use it: the ceremony is the subject.
Reach for it when
- You want to understand what the GPU is actually being told, without a layer editorialising.
- You need to run on anything, including older mobile and locked-down corporate machines.
- You want zero dependencies and a bundle measured in the code you wrote.
Look elsewhere when
- You want a scene with models, lights and cameras — you would be rebuilding Three.js badly.
- You need compute shaders, storage buffers or modern GPU features. That is WebGPU territory.
The code
The same plasma, written the way WebGL wants it written.
precision highp float;
varying vec2 vUv;
uniform float uTime;
void main() {
// Inigo Quilez's cosine palette: three cosines a phase apart.
vec3 colour = 0.5 + 0.5 * cos(uTime + vUv.xyx + vec3(0.0, 2.0, 4.0));
gl_FragColor = vec4(colour, 1.0);
}const gl = canvas.getContext('webgl');
const program = createProgram(gl, vertexSource, fragmentSource);
const quad = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, quad);
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([-1, -1, 3, -1, -1, 3]),
gl.STATIC_DRAW,
);
const position = gl.getAttribLocation(program, 'aPosition');
const uTime = gl.getUniformLocation(program, 'uTime');
function frame(now) {
gl.viewport(0, 0, canvas.width, canvas.height);
gl.useProgram(program);
gl.bindBuffer(gl.ARRAY_BUFFER, quad);
gl.enableVertexAttribArray(position);
gl.vertexAttribPointer(position, 2, gl.FLOAT, false, 0, 0);
gl.uniform1f(uTime, now / 1000);
gl.drawArrays(gl.TRIANGLES, 0, 3);
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);Two shaders compiled, a program linked, one quad uploaded, one uniform updated per frame. This is the whole thing.
Things that will catch you
- One big triangle, not a quad
- A full-screen effect is usually drawn as a single oversized triangle rather than two triangles forming a square. It covers the same pixels with one fewer primitive and no seam down the diagonal where the two triangles meet.
- Context loss is real and you must handle it
- The browser can take your GPU context away at any moment — tab backgrounded, driver reset, another tab being greedy. Listen for webglcontextlost or your canvas silently freezes.
- gl.lineWidth mostly does nothing
- Almost every implementation clamps it to 1. If you need thick lines you build them from triangles. This surprises everyone exactly once.
Where to learn it
- WebGL FundamentalsGregg Tavares
- WebGL2 FundamentalsGregg Tavares
- LearnOpenGLJoey de Vries
- The Book of ShadersPatricio Gonzalez Vivo & Jen Lowe
These and 33 more on the reading path.