Command Palette

Search for a command to run...

Hectal
Course outline
Topic 14.3Advanced ~19 min · step 3 of 6

A Triangle in Raw WebGPU

In one line

WebGPU draws the same triangle with other ideas: ask for an adapter and a device (both asynchronous), write both shaders in one WGSL module, fix every setting in advance in a pipeline, record commands into an encoder, and submit them at once. Errors are checked and reported in detail.

0/6 · 0%

Think of it like this

A restaurant kitchen. WebGL is a cook shouting each step as it happens. WebGPU writes the recipe card first (the pipeline), puts the ingredients on a tray (bind groups), writes the orders on a ticket (the command encoder) and hands the ticket over once (submit).

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

adapter
A GPU the browser is willing to use (GPUAdapter); requestAdapter() can return null.
device
Your connection to an adapter (GPUDevice); every buffer, texture and pipeline comes from it.
WGSL
WebGPU Shading Language, the language of WebGPU shaders.
render pipeline
An immutable object with the shaders and fixed settings (formats, topology, blending) for one kind of draw.
command encoder
Records GPU commands into a list you submit later.
render pass
A recorded section that draws into a set of textures, with a clear and store rule for each.

Step by step

01Run it in a WebGPU browser

The status bar says WebGPU · raw and the console drew one triangle with WebGPU, canvas format bgra8unorm (or rgba8unorm). Without WebGPU the page says why: no navigator.gpu…, or requestAdapter() returned null… as in this site's checker, where Chrome also prints its own warning, No available adapters.

Run it in a WebGPU browserdiagram
Rendering diagram…

02Mind the byte order

On a bgra8unorm canvas, a raw copy of the middle pixel reads 44, 107, 255, 255: Lumen orange with blue first. Tools that read pixels back must check the format.

03Count the promises

requestAdapter, requestDevice and onSubmittedWorkDone are asynchronous: the browser may have to start or choose a GPU process. Everything after requestDevice is called synchronously.

04Read a WGSL error

The second example writes vec4f(1.0, 0.1, 0.05), three numbers for a four-number type. getCompilationInfo() gives error at line 2, column 10: no matching constructor for 'vec4<f32>(abstract-float, abstract-float, abstract-float)' (the full message then lists all 20 constructors it could have meant). Using the module later raises [Invalid ShaderModule "glow shader"] is invalid due to a previous error.: your label appears in the message.

Try it yourself

  1. 1

    Change the topology

    In the first example, change "triangle-list" to "line-strip". With WebGPU you see two orange lines (the three points joined in order, not closed). The pipeline, not the draw call, decides how vertices join.

  2. 2

    Fix the WGSL

    In the second example, write vec4f(1.0, 0.1, 0.05, 1.0). getCompilationInfo() is now silent, and the next error appears: Entry point ""vs"" doesn't exist in the shader module [ShaderModule "glow shader"]. (Chrome doubles the quotes). Each fix reveals the next bug, by name.

Code & diagrams

A triangle in raw WebGPU New tab

No `output` is checked: it depends on the browser. With WebGPU it prints `drew one triangle with WebGPU, canvas format bgra8unorm` (or `rgba8unorm`); without, why not.

idle

Console

Run the scene to see its console.
A WGSL mistake, and where its message goes New tab

With WebGPU: the `uncaptured error event` line, `error at line 2, column 10: no matching constructor for 'vec4<f32>(…)'`, then `uncaptured error event: [Invalid ShaderModule "glow shader"] is invalid due to a previous error.` Without, it says so.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Forget await on requestAdapter

In the first example, remove await before navigator.gpu.requestAdapter().

terminal
$ Run it in a WebGPU browser
── what you'll see ──
WebGPU failed: adapter.requestDevice is not a function
The `if (!adapter)` check passes, because a Promise is truthy.

Myth vs fact

Myth

If navigator.gpu exists, WebGPU works.

Fact

requestAdapter() can still return null: a blocked driver, a disabled GPU, a headless browser. This page's own test browser is that case.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Label everything (label: "glow pipeline"). Chrome's Dawn and Firefox's wgpu put labels into error messages; unlabeled objects appear as [Buffer (unlabeled)] (Topic 14.4).

  • ▸

    WebGPU has no getError(). Errors go to the device asynchronously: wrap risky code in pushErrorScope("validation") / popErrorScope(), and listen for uncapturederror for everything else.

Remember this

  1. 1

    The problem this solves: WebGL copies 1990s OpenGL: one global state, checked call by call. Modern GPU APIs (Vulkan, Metal, Direct3D 12) use pre-built pipelines and recorded command lists. WebGPU brings that to the web: less CPU work per draw, compute shaders, precise errors.

  2. 2

    Adapter and device. navigator.gpu exists only where WebGPU does. await navigator.gpu.requestAdapter() returns a GPUAdapter (a GPU the browser will use) or **null**: a blocklisted driver, or this page's test browser. await adapter.requestDevice() gives the GPUDevice everything is created from. A device can be lost (driver crash): device.lost resolves with the reason.

  3. 3

    The canvas. getContext("webgpu"), then context.configure({ device, format }) with navigator.gpu.getPreferredCanvasFormat(). On most desktops that is bgra8unorm, blue first in memory: the triangle's middle pixel, read back as raw bytes, is 44, 107, 255, 255: the same orange, stored B, G, R, A.

  4. 4

    WGSL and the pipeline. One WGSL module holds both shaders as @vertex and @fragment functions. This triangle needs no vertex buffer: the vertex shader makes the corners from @builtin(vertex_index). createRenderPipeline fixes the shaders, output format and primitive type in one immutable object, checked once when it is made. WebGL's per-draw switches become one object you can't half-set.

  5. 5

    Record, then submit. createCommandEncoder(), beginRenderPass (which texture, how to clear), setPipeline, draw(3), end(): all recorded, not run. encoder.finish() closes the list and device.queue.submit() sends it; the GPU runs it later.

Explain it without notes

01

List the objects you create to draw one triangle with WebGPU, in order, and what each is for.

02

What can requestAdapter() return, and how should a page react?

Practice

01

Clear to Lumen blue instead of the stage colour.

02

Print the adapter's largest texture size and largest uniform binding.

Done when you can

  • You can draw a triangle with raw WebGPU

  • You check navigator.gpu and the adapter, and await every promise

  • You read WGSL errors with getCompilationInfo

  • You label your WebGPU objects