WebGL fundamentals for game developers
WebGL gives you direct GPU access in the browser. It's the foundation for 3D (and performant 2D) web games. This tutorial covers the essentials.
1) Getting a WebGL context
const canvas = document.getElementById('game')
const gl = canvas.getContext('webgl2') || canvas.getContext('webgl')
if (!gl) {
// Fallback or error message
console.error('WebGL not supported')
}Prefer webgl2 when available—it's widely supported and offers better features.
2) The render pipeline (simplified)
- Vertex shader runs once per vertex. It positions your geometry.
- Fragment shader runs once per pixel. It colors the output.
- Buffers hold data (positions, UVs, colors).
- Textures are images sampled by shaders.
3) A minimal shader pair
Vertex shader:
#version 300 es
in vec2 a_position;
void main() {
gl_Position = vec4(a_position, 0.0, 1.0);
}Fragment shader:
#version 300 es
precision mediump float;
out vec4 fragColor;
void main() {
fragColor = vec4(1.0, 0.5, 0.2, 1.0); // orange
}4) Compiling and linking shaders
function createShader(gl, type, source) {
const shader = gl.createShader(type)
gl.shaderSource(shader, source)
gl.compileShader(shader)
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error(gl.getShaderInfoLog(shader))
gl.deleteShader(shader)
return null
}
return shader
}
function createProgram(gl, vs, fs) {
const program = gl.createProgram()
gl.attachShader(program, vs)
gl.attachShader(program, fs)
gl.linkProgram(program)
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error(gl.getProgramInfoLog(program))
return null
}
return program
}5) Creating buffers
const positions = new Float32Array([
-0.5, -0.5,
0.5, -0.5,
0.0, 0.5,
])
const buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW)6) Connecting buffers to shaders
const positionLoc = gl.getAttribLocation(program, 'a_position')
gl.enableVertexAttribArray(positionLoc)
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0)7) The game loop
function render() {
gl.viewport(0, 0, canvas.width, canvas.height)
gl.clearColor(0, 0, 0, 1)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.useProgram(program)
gl.drawArrays(gl.TRIANGLES, 0, 3)
requestAnimationFrame(render)
}
render()8) Loading textures
function loadTexture(gl, url) {
const texture = gl.createTexture()
gl.bindTexture(gl.TEXTURE_2D, texture)
// Placeholder pixel until image loads
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
new Uint8Array([255, 0, 255, 255]))
const img = new Image()
img.onload = () => {
gl.bindTexture(gl.TEXTURE_2D, texture)
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img)
gl.generateMipmap(gl.TEXTURE_2D)
}
img.src = url
return texture
}9) Common gotchas
- Power-of-two textures aren't strictly required in WebGL 2, but mipmaps work better with them.
- Lost context: handle
webglcontextlostevents—mobile browsers will evict contexts under memory pressure. - Float textures need extension checks (
EXT_color_buffer_float).
10) When to use a library
Raw WebGL is verbose. For most games, consider:
- Three.js — full-featured 3D
- PixiJS — fast 2D rendering
- PlayCanvas — game engine with editor
Use raw WebGL when you need:
- maximum control
- a custom renderer
- to learn how things really work
One note for 2026: WebGPU now ships by default in every major browser (Chrome and Edge since 113, Firefox 141 on Windows and 145 on macOS, and Safari on iOS 26, iPadOS 26, and macOS Tahoe 26). The fundamentals here still apply, since libraries like Three.js run their WebGPU renderer with an automatic WebGL 2 fallback, so WebGL 2 remains the dependable baseline. If you're starting a new renderer from scratch today, read the WebGPU tutorial alongside this one before committing to an API.
Related
- WebGPU for games
- Canvas 2D game loop
- Ship a web game that loads fast
- Web Games Tech Stack in 2026 — when to pick WebGL vs WebGPU vs Wasm
- Game physics libraries — physics engines that render through WebGL
- Web Game Engines Comparison — Three.js, PixiJS, PlayCanvas, and others
External Resources
- MDN: WebGL API — full API reference
- WebGL2 Fundamentals — in-depth WebGL2 tutorial series
- The Book of Shaders — interactive guide to GLSL fragment shaders
- Three.js documentation — the most popular WebGL framework