Chapter 11

Shaders, WebGPU & 3D

The GPU pipeline: vertex, fragment, compute.

GLSLWebGPUThree.jsThrelte
Uniforms drive a parallel fragment shader. WGSL vs GLSL; WebGPU vs WebGL. Three.js/Threlte build scene graphs on top.

The CPU draws one thing at a time. The GPU draws millions of pixels at once. A shader is a tiny program that runs in parallel across all of them — and once you grasp that the same function executes simultaneously for every pixel, the whole world of plasma, noise fields and 3D lighting opens up.

The pipeline: vertices in, pixels out

A draw call flows through two programmable stages. The vertex shader runs once per vertex and decides where geometry lands in clip space. The rasterizer fills the triangles, then the fragment shader runs once per covered pixel and decides its color. Both are massively parallel: thousands of cores execute the same shader on different data (SIMD).

Vertex shader
positions
Rasterizer
fill triangles
Fragment shader
per-pixel color

The live shader editor

This is the centerpiece. A full-screen triangle is drawn with raw WebGL2, and the fragment shader below is yours to edit. It recompiles live; if you make a mistake, gl.getShaderInfoLog shows the exact line. Move your pointer over the canvas (u_mouse warps the field) and drag the sliders to feed u_scale / u_speed.

GLSL fragment-shader playground (WebGL2) live demo
compiled ✓
fragment shader · GLSL ES 3.0

Noise: where organic patterns come from

Pure sines give you stripes. The thing that makes shader art look like marble, smoke, or terrain is noise — a smooth pseudo-random function of position. Value noise (in the demo) hashes a grid and interpolates; gradient (Perlin/Simplex) noise is smoother. Stack several octaves at rising frequency and falling amplitude (fractal Brownian motion) and flat math turns into clouds. Try replacing the sines in the editor with more noise() calls.

GLSL vs WGSL

WebGL speaks GLSL (the editor above). WebGPU speaks WGSL, a newer, Rust-flavored shading language with explicit types and bindings. The same idea, different syntax:

GLSL (WebGL)
#version 300 es
precision highp float;
uniform float u_time;
out vec4 o;
void main() {
  o = vec4(
    abs(sin(u_time)),
    0.4, 0.8, 1.0);
}
WGSL (WebGPU)
@group(0) @binding(0)
var<uniform> u_time: f32;

@fragment
fn fs() -> @location(0) vec4f {
  return vec4f(
    abs(sin(u_time)),
    0.4, 0.8, 1.0);
}

WebGPU: the next-generation API

WebGPU is the successor to WebGL — a modern, lower-overhead GPU API modeled on Vulkan/Metal/D3D12. It exposes compute shaders: programs with no pixels or vertices at all, just a grid of threads doing general math (particle simulation, physics, ML inference) directly on the GPU. WebGL never had that.

WebGPU on this device live demo
checking…
WebGPU default-on Nov 25 2025
Chrome/Edge
Firefox
Safari

Milestone announced Nov 25 2025: WebGPU shipped default-on across Chrome/Edge, Firefox and Safari. Chrome since v113 (2023); Safari 26 (Sept 2025); Firefox 141 on Windows (Jul 2025), 145 on Apple Silicon. Gaps remain: Linux, Firefox Android (behind a flag), and importExternalTexture not yet in Firefox stable. Two implementations under the hood: Dawn (Chrome) and wgpu (Firefox).

Scene graphs: you rarely write raw WebGL for 3D

Hand-writing matrices and buffers is fine for a quad; for a lit, textured 3D scene you want a scene graph. Three.js is the foundation; React Three Fiber + Drei wraps it for React; Threlte wraps it for Svelte; Babylon.js is a batteries-included engine. For physics you bolt on Rapier. The scene below is Threlte — declarative Svelte components compiling down to Three.js.

Threlte scene · drag to orbit live demo
initializing 3D…

Drag to orbit · scroll to zoom. Rotation uses useTask (delta-based).

Playground: a bare WebGL2 shader from scratch

No libraries, no imports — just a <canvas> and ~40 lines that compile a fragment shader and animate it. Edit the FRAG string and re-run to see your changes.