WebGPU gets easier after the first triangle.

This is the combined guide for the minimal setup, rebuild fixes, and honesty checks.

You will render a triangle, verify the output, and keep the write up strict.

I cannot verify these steps from this blog repo. Treat this as a lab guide you can run locally.

The promise

By the end, you will:

  • render a WebGPU triangle
  • verify the render loop
  • keep the write up aligned with the code

"One triangle is enough to learn the pipeline."

What this is

High level: WebGPU is a browser API for GPU rendering.

Low level: You request a device, configure a canvas, build a pipeline, and draw.

Key terms:

  • Adapter: GPU capability entry point.
  • Device: The object that creates GPU resources.
  • Pipeline: The configuration that ties shaders to output.

What you need

  • Chrome or Edge with WebGPU enabled
  • A local static server

Start to Finish

Step 1: Create the HTML shell

Goal: Set up a canvas and script entry point.

Actions:

  • File path: index.html
  • Add:
    <canvas id="gfx" width="640" height="360"></canvas>
    <script type="module" src="main.js"></script>

Why: The canvas is the render target. The module script lets you use top level await. This keeps the demo minimal and modern. Without the canvas, nothing will render.

Verify:

  • Open the HTML file in a browser.
  • Expected: a blank page with no errors.
  • This confirms the shell loads.

If it fails:

  • Symptom: script not found.
    Fix: ensure main.js exists in the same folder.

Step 2: Request the adapter and device

Goal: Confirm WebGPU is available and create a device.

Actions:

  • File path: main.js
  • Add:
    const adapter = await navigator.gpu.requestAdapter();
    if (!adapter) throw new Error("No GPU adapter");
    const device = await adapter.requestDevice();

Why: The adapter check is the first gate. The device is required to create pipelines and buffers. This step prevents silent failures. It also tells you whether the browser supports WebGPU.

Verify:

  • Open DevTools Console.
  • Expected: no error thrown.
  • This confirms WebGPU is available.

If it fails:

  • Symptom: No GPU adapter.
    Fix: enable WebGPU in browser flags.

Step 3: Configure the canvas context

Goal: Bind the canvas to WebGPU and set the format.

Actions:

  • File path: main.js
  • Add:
    const canvas = document.getElementById("gfx");
    const context = canvas.getContext("webgpu");
    const format = navigator.gpu.getPreferredCanvasFormat();
    context.configure({ device, format, alphaMode: "opaque" });

Why: The context is how WebGPU draws to the canvas. The format must match the pipeline output. This is the most common source of blank screens. Setting opaque avoids transparency bugs.

Verify:

  • Expected: no errors in console.
  • This confirms the context is configured.

If it fails:

  • Symptom: context is null.
    Fix: confirm the canvas ID and WebGPU support.

Step 4: Build a minimal pipeline

Goal: Create a pipeline that draws one triangle.

Actions:

  • File path: main.js
  • Add a shader string:
    const shaderCode = `
    @vertex
    fn vs_main(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4f {
    var positions = array<vec2f, 3>(
    vec2f(0.0, 0.6),
    vec2f(-0.6, -0.6),
    vec2f(0.6, -0.6)
    );
    return vec4f(positions[vi], 0.0, 1.0);
    }
    @fragment
    fn fs_main() -> @location(0) vec4f {
    return vec4f(0.2, 0.6, 1.0, 1.0);
    }`;
  • Create the pipeline:
    const module = device.createShaderModule({ code: shaderCode });
    const pipeline = device.createRenderPipeline({
    layout: "auto",
    vertex: { module, entryPoint: "vs_main" },
    fragment: { module, entryPoint: "fs_main", targets: [{ format }] },
    primitive: { topology: "triangle-list" }
    });

Why: A minimal shader removes variables. The pipeline ties the shader to the render pass. This is the core of the demo. Without a valid pipeline, nothing will render.

Verify:

  • Expected: no shader compile errors in console.
  • This confirms the pipeline is valid.

If it fails:

  • Symptom: shader compile error.
    Fix: check entry point names and WGSL syntax.

Step 5: Draw the frame

Goal: Issue one draw call and render the triangle.

Actions:

  • File path: main.js
  • Add:
    function frame() {
    const encoder = device.createCommandEncoder();
    const pass = encoder.beginRenderPass({
    colorAttachments: [{
    view: context.getCurrentTexture().createView(),
    clearValue: { r: 0.05, g: 0.05, b: 0.08, a: 1 },
    loadOp: "clear",
    storeOp: "store"
    }]
    });
    pass.setPipeline(pipeline);
    pass.draw(3);
    pass.end();
    device.queue.submit([encoder.finish()]);
    requestAnimationFrame(frame);
    }
    frame();

Why: This is the full draw loop. It clears the screen and draws three vertices. requestAnimationFrame keeps it running. This is the most reliable baseline for WebGPU demos.

Verify:

  • Expected: a blue triangle appears on a dark background.
  • This confirms the render loop works.

If it fails:

  • Symptom: blank canvas.
    Fix: ensure the pipeline and context use the same format.

Verify it worked

  • The triangle is visible.
  • No console errors appear.
  • The output is stable.

Common mistakes

  • Symptom: navigator.gpu is undefined.
    Cause: WebGPU disabled.
    Fix: enable WebGPU in browser flags.

  • Symptom: Triangle flickers.
    Cause: missing loadOp: "clear".
    Fix: add the clear operation.

  • Symptom: Shader compile errors.
    Cause: WGSL syntax issues.
    Fix: check the shader code carefully.

Cheat sheet

  • Request adapter and device.
  • Configure the canvas context.
  • Build a minimal pipeline.
  • Draw three vertices.

Next steps

  • Add resize handling.
  • Add a uniform for color.
  • Keep the write up aligned with the code.

Related links

Final CTA

Get one triangle working, then build one small improvement at a time.