From Math to Canvas: Introduction to GLSL Shaders and 3D WebGL Coordinates
In web development, we spend most of our time styling with CSS and laying out DOM elements. But when you want to render 50,000 interactive 3D particles or an illuminated neon ocean in the browser at 60 frames per second, the CPU cannot keep up.
To achieve this level of visual computing, we write GLSL (OpenGL Shading Language) programs that execute directly across the thousands of micro-cores on the user's GPU.
At Anim Studio, our 3D graphics track turns raw linear algebra into interactive canvas experiences. Here is how shaders work under the hood.
📐 1. The Rendering Pipeline: Vertex vs. Fragment
A 3D rendering pipeline takes a mathematical model and projects it onto a 2D screen through two primary shader stages:
- Vertex Shader: Runs once for every single 3D vertex (coordinate) in your geometry. Its job is to compute
gl_Positionby multiplying the local vertex coordinates by the Model-View-Projection matrix:
$$\mathbf{p}_{\text{clip}} = \mathbf{M}_{\text{projection}} \times \mathbf{M}_{\text{view}} \times \mathbf{M}_{\text{model}} \times \mathbf{p}_{\text{vertex}}$$
- Fragment (Pixel) Shader: Runs once for every single pixel on screen covered by the geometry. Its job is to calculate
gl_FragColor(Red, Green, Blue, Alpha).
🎨 2. Writing a Custom Water Wave Shader in Three.js
Here is how you can deform a 3D plane into flowing waves in GLSL:
// Custom Vertex Shader (wave.vert)
uniform float uTime;
varying vec2 vUv;
varying float vElevation;
void main() {
vUv = uv;
vec4 modelPosition = modelMatrix * vec4(position, 1.0);
// Apply sinusoidal displacement based on X coordinate and Time
float elevation = sin(modelPosition.x * 3.0 + uTime * 2.0) * 0.2;
modelPosition.y += elevation;
vElevation = elevation;
gl_Position = projectionMatrix * viewMatrix * modelPosition;
}// Custom Fragment Shader (wave.frag)
uniform vec3 uDepthColor;
uniform vec3 uSurfaceColor;
varying float vElevation;
void main() {
// Interpolate color based on wave height
float mixFactor = (vElevation + 0.2) / 0.4;
vec3 color = mix(uDepthColor, uSurfaceColor, mixFactor);
gl_FragColor = vec4(color, 1.0);
}🚀 3. Why GPU Parallelism Changes Everything
On a CPU, changing the color of a 1920×1080 display requires iterating through over 2 million pixels in a sequential loop.
A GPU, by contrast, executes your Fragment Shader concurrently across millions of fragments. Because each pixel calculation is mathematically independent, you achieve buttery-smooth 60fps renders even with complex trigonometric equations.
Master 3D WebGL, GLSL custom shaders, and Three.js physics in our 3D WebGL Graphics & Shader Engineering Track.

