What WebGL is
WebGL (Web Graphics Library) is a JavaScript API for drawing graphics with the GPU — the same chip that powers video games. It renders into an ordinary <canvas> element, so 3D scenes, particle fields, data visualizations, and shader art run in the browser with no plugins.
Unlike HTML and CSS, which describe boxes of content, WebGL gives you a blank pixel grid and total control over every pixel — at the cost of doing a lot more work yourself. It's based on OpenGL ES, the graphics standard used on phones and consoles.
The canvas & the context
Everything starts with a <canvas> and a rendering context. Asking the canvas for its "webgl" context hands you the object through which every GPU command flows.
<canvas id="scene" width="600" height="400"></canvas> const canvas = document.getElementById('scene'); const gl = canvas.getContext('webgl'); the gateway to the GPU gl.clearColor(0.05, 0.05, 0.1, 1); RGBA, each 0–1 gl.clear(gl.COLOR_BUFFER_BIT); paint the canvas
1.0 is full intensity; CSS's 255 becomes 1.0.The rendering pipeline
The GPU turns your data into pixels in a fixed sequence of stages. Understanding this flow is the key to all of WebGL:
You feed in a list of vertices (corner points). The vertex shader decides where each lands on screen. The GPU rasterizes the shapes into fragments (pixel-sized pieces). The fragment shader picks a color for each one. The result is drawn.
Shaders: tiny programs on the GPU
A shader is a small program written in GLSL that runs in massive parallel on the GPU — thousands of cores at once. There are two kinds, one for each green stage above:
| Shader | Runs once per… | Job |
|---|---|---|
| Vertex | vertex (corner point) | Output the point's final position (gl_Position). |
| Fragment | pixel (fragment) | Output that pixel's color (gl_FragColor). |
A minimal pair
// vertex shader — place the point attribute vec2 a_pos; void main() { gl_Position = vec4(a_pos, 0.0, 1.0); } // fragment shader — color the pixel (lime green) precision mediump float; void main() { gl_FragColor = vec4(0.52, 0.80, 0.09, 1.0); }
Live: a fragment shader, running now
This canvas draws two triangles that cover the whole frame, then a fragment shader computes a color for every pixel from its position and a u_time uniform. The GPU re-runs it ~60 times a second — that's the animation. Raw WebGL, no library.
precision mediump float; uniform float u_time; uniform vec2 u_res; void main() { vec2 uv = gl_FragCoord.xy / u_res; float v = sin(uv.x*10.+u_time) + sin(uv.y*10.+u_time); gl_FragColor = vec4(0.5+0.5*cos(v+vec3(0.,2.,4.)), 1.); }
Coordinates & buffers
WebGL's screen is clip space: the center is (0, 0), the left edge is -1, the right edge +1, bottom -1, top +1 — regardless of pixel size. Your vertex shader outputs positions in this space.
You send vertex data to the GPU in a buffer (a typed array), then describe its layout with an attribute. A full-screen quad is just four corners:
const verts = new Float32Array([ -1,-1, 1,-1, -1,1, triangle 1 -1,1, 1,-1, 1,1 triangle 2 ]); const buf = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buf); gl.bufferData(gl.ARRAY_BUFFER, verts, gl.STATIC_DRAW);
Why almost everyone uses a library
A spinning cube in raw WebGL is ~150 lines of shader compilation, buffer setup, and matrix math. Libraries like Three.js wrap all of it in friendly objects — scenes, cameras, meshes, lights — so the same cube is a dozen readable lines.
import * as THREE from 'three'; const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(60, 1.6, 0.1, 100); const renderer = new THREE.WebGLRenderer({ antialias: true }); const cube = new THREE.Mesh( new THREE.BoxGeometry(1, 1, 1), new THREE.MeshStandardMaterial({ color: 0x84cc16 }) ); scene.add(cube);
Live: a Three.js 3D cube
Here's that cube, lit and spinning — real 3D, rendered through WebGL by Three.js. Same GPU pipeline from Step 3, just with the boilerplate handled for you:
renderer.render(scene, camera) each frame inside requestAnimationFrame — that loop is what makes it spin.WebGL, Canvas 2D, or CSS/SVG?
WebGL is powerful but heavy. Reach for the lightest tool that does the job:
| Need | Use |
|---|---|
| Buttons, layout, simple hover/scroll motion | CSS — cheap, accessible, no canvas. |
| Icons, charts, crisp scalable shapes | SVG — vector, stylable, in the DOM. |
| 2D games, image filters, lots of sprites | Canvas 2D — per-pixel, simpler API than WebGL. |
| 3D, shaders, 10,000s of objects, GPU effects | WebGL (usually via Three.js). |
WebGL checklist
- Drawing into a
<canvas>viagetContext('webgl') - You understand the pipeline: vertices → vertex shader → raster → fragment shader → pixels
- Colors are floats
0.0–1.0; positions live in clip space-1–1 - Reaching for Three.js instead of hand-writing boilerplate for real projects
- Animating inside
requestAnimationFrame, paused when off-screen - A text fallback exists — canvas content isn't readable by assistive tech