A WebGL canvas starts as a blank frame, gains a clear color, has shaders compiled and linked, and finally produces a triangle when drawArrays runs. The replay connects each GL call to the rendered pixels.

Program

WebGL is state-machine drawing: you set up the GPU (shaders, program, buffer, attributes) and then the actual pixels only appear when drawArrays runs. Step through the program to see which calls change the canvas and which only configure state.

webgl_triangle_scene.js
const canvas = document.querySelector('#stage');
const gl = canvas.getContext('webgl');

gl.clearColor(0.07, 0.10, 0.18, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);

const vsSrc =
  'attribute vec2 aPos; attribute vec3 aColor; varying vec3 vColor;' +
  'void main() { gl_Position = vec4(aPos, 0.0, 1.0); vColor = aColor; }';
const fsSrc =
  'precision mediump float; varying vec3 vColor;' +
  'void main() { gl_FragColor = vec4(vColor, 1.0); }';

const vs = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vs, vsSrc);
gl.compileShader(vs);

const fs = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fs, fsSrc);
gl.compileShader(fs);

const program = gl.createProgram();
gl.attachShader(program, vs);
gl.attachShader(program, fs);
gl.linkProgram(program);
gl.useProgram(program);

const vertices = new Float32Array([
   0.0,  0.6,   1.0, 0.4, 0.4,
  -0.6, -0.5,   0.4, 1.0, 0.5,
   0.6, -0.5,   0.5, 0.5, 1.0,
]);

const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

const aPos = gl.getAttribLocation(program, 'aPos');
gl.enableVertexAttribArray(aPos);
gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 20, 0);

const aColor = gl.getAttribLocation(program, 'aColor');
gl.enableVertexAttribArray(aColor);
gl.vertexAttribPointer(aColor, 3, gl.FLOAT, false, 20, 8);

gl.drawArrays(gl.TRIANGLES, 0, 3);
WebGL context `canvas.getContext('webgl')` returns a `WebGLRenderingContext`. All later `gl.*` calls drive the GPU through this object.
shaders and program A vertex shader runs per vertex; a fragment shader runs per pixel. Together they form a `WebGLProgram`. Compiling shaders or linking a program does not draw anything by itself.
buffer and attributes A `WebGLBuffer` holds vertex data on the GPU. `vertexAttribPointer` tells the GPU how to read positions and colors out of each vertex stride.
drawArrays `drawArrays(TRIANGLES, 0, 3)` runs the program for three vertices and paints the resulting triangle into the canvas bitmap.