Rebuilding a WebGPU Triangle Demo: A Clean Fix Pass
Small WebGPU demos break for boring reasons.
This post shows a clean fix pass and how to verify the rebuild.
You will isolate one issue at a time and prove the fix works.
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:
- fix a broken triangle render
- clean up the render loop
- verify the output is stable
"Fix one thing, then verify it, then move on."
What this is
High level: This is a minimal rebuild pass for a broken WebGPU demo.
Low level: You verify the adapter, fix the pipeline, and confirm the draw call runs.
Key terms:
- Render loop: A function that draws each frame.
- Pipeline: The config for shaders and output.
What you need
- A WebGPU capable browser
- A minimal WebGPU demo project
Start to Finish
Step 1: Confirm the adapter and device
Goal: Ensure WebGPU is available before debugging anything else.
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: If the adapter is missing, nothing else matters. This is the first hard check. It reduces false debugging. It also tells you whether the browser supports WebGPU.
Verify:
- Run the demo and check the console.
- Expected: no "No GPU adapter" error.
- This confirms WebGPU is available.
If it fails:
- Symptom: adapter is null.
Fix: enable WebGPU in the browser flags.
Step 2: Rebuild the pipeline
Goal: Create a minimal pipeline that can draw a triangle.
Actions:
- File path:
main.js - Add:
const pipeline = device.createRenderPipeline({layout: "auto",vertex: { module, entryPoint: "vs_main" },fragment: { module, entryPoint: "fs_main", targets: [{ format }] },primitive: { topology: "triangle-list" }});
Why: If the pipeline is wrong, nothing draws. A minimal pipeline reduces the number of moving parts. This also makes shader issues easier to spot. It is the fastest route back to a working demo.
Verify:
- Run the demo and look for a triangle.
- Expected: a triangle appears.
- This confirms the pipeline works.
If it fails:
- Symptom: blank canvas.
Fix: verify shader entry point names.
Step 3: Clean the render loop
Goal: Ensure each frame clears and draws correctly.
Actions:
- File path:
main.js - Use this loop:
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: A clean render loop removes flicker and blank frames. It also makes the draw call obvious. This is the simplest loop that still updates every frame. It is the most reliable baseline.
Verify:
- Expected: the triangle stays visible without flicker.
- This confirms the loop works.
If it fails:
- Symptom: triangle flickers.
Fix: ensure you useloadOp: "clear"and submit the command buffer.
Verify it worked
- A triangle appears.
- The render loop runs without errors.
- The output is stable.
Common mistakes
-
Symptom: Nothing renders.
Cause: Shader entry points mismatch.
Fix: align entry point names in code and shader. -
Symptom: Canvas is black.
Cause: No clear color or draw call.
Fix: addclearValueanddraw(3). -
Symptom: Device lost errors.
Cause: driver issues.
Fix: reload and try again.
Cheat sheet
- Check adapter and device.
- Use a minimal pipeline.
- Use a clean render loop.
Next steps
- Add resize handling.
- Add a uniform for color.
Related links
Final CTA
Fix one issue at a time and verify after every change.