The default workflow for developing shaders with vgpu
Follow these eight steps in order every time you write or change a shader. Each step has one exact command and one thing to check before you move on. Do not skip a step because the shader "looks fine": every claim about pixels must come from pixels you read.
1. Discover the docs
npx vgpu docs ls # top-level paths: /guides plus one entry per package
npx vgpu docs ls /guides # every guide, by file nameThe corpus ships inside the package, so it works offline. Subcommands: ls, cat, grep, find, path, symbols. Full reference: npx vgpu docs cat cli.docs.md.
2. Read getting started
npx vgpu docs cat getting-started.mdls /guides lists the file as getting-started.docs.md; both getting-started.md and /guides/getting-started.docs.md resolve to it. This is the only page that shows the current API end to end — read it before writing any init() call.
3. Read the concepts you actually need
npx vgpu docs cat concepts-effects.md # fullscreen fragment work and set()
npx vgpu docs cat concepts-passes.md # frame.pass, multi-pass work
npx vgpu docs cat concepts-frames.md # frame batching and animation loopsSingle fullscreen shader: read effects only. Anything with more than one target — blur pyramids, G-buffers, transmission — read passes and frames too.
4. Validate every WGSL file
npx vgpu check ./shaders/glass.wgslThe command is check, not wgsl. It validates without running and prints the shader's reflection as JSON — binding names, groups, entry points — so you can confirm the names you will pass to set(). It exits non-zero with the validation errors when the shader is wrong. Run it on every .wgsl file, including pure helper modules.
5. Verify this machine can render
npx vgpu doctor --prettydoctor acquires a real adapter and renders a real frame, then prints a JSON verdict. When it is unhealthy, apply the fix it prescribes literally — usually npx vgpu install-software-renderer, sometimes a VK_ICD_FILENAMES export. Re-run until it reports healthy. If it still fails after its own fixes, use the Docker fallback in Debugging shaders by extracting internal values.
6. Render a static frame in Node and look at it
Do not open a browser to see whether a shader draws. Render one frame headless, read the pixels, and write a PNG you can open:
import { writeFileSync } from "node:fs";
import { PNG } from "pngjs";
import { init, effect, target } from "vgpu/node";
const width = 320;
const height = 180;
const gpu = await init();
const colorTarget = target(gpu, { size: [width, height] });
effect(gpu, `
@fragment fn fs_main(@location(0) uv: vec2f) -> @location(0) vec4f {
return vec4f(uv, 0.5, 1.0);
}
`).draw(colorTarget);
const pixels = await colorTarget.read(); // RGBA bytes, row-major, no padding
const png = new PNG({ width, height });
png.data.set(pixels);
writeFileSync("frame.png", PNG.sync.write(png));
gpu.dispose(); // stops Dawn's polling so the process exitsKeep the target small (a few hundred pixels wide) so the loop stays fast on a CPU renderer. Assert on pixels when you know the expected value; open the PNG when you need to judge composition. See Getting started for the same pattern without PNG encoding.
7. Connect it to the browser when the shader ships to a browser
Port the same code to vgpu (not vgpu/node) with surface(gpu, canvas, ...) and drive it from frameLoop(gpu, ...). Test it the way users run it, with the public API and deterministic frame submission: Browser testing with Playwright WebGPU.
8. Validate visually with agent-browser
agent-browser --session shader-check --webgpu --headed open http://localhost:3001/preview/glass
agent-browser --session shader-check --webgpu --headed wait 6000
agent-browser --session shader-check --webgpu --headed screenshot glass.png
agent-browser --session shader-check closeOn Linux always pass --webgpu --headed: headless Chrome captures the canvas as black even when the render succeeded. Always check the pixels of the capture, never just its existence. Full recipe, including error-overlay checks: WebGPU screenshots with agent-browser.
Escalate to extraction, never to another guess
Stop iterating by eye and switch to numeric extraction the moment any of these is true:
- the shader is multi-pass, or composes several passes (pyramids, G-buffers, ping-pong);
- the math is non-trivial (refraction, dispersion, LOD selection, importance sampling);
- a user reports a visual bug you cannot reproduce as a single wrong number.
In those cases apply Debugging shaders by extracting internal values from the start, before your first fix attempt. The evidence: while building the transmission example, two rounds of by-eye fixes changed the image without fixing it. Encoding the shader's internals as pixels and diffing them against a CPU reference found both root bugs — a clamped blur LOD and a G-buffer storing the wrong ray's surface — in a single pass.