---
title: The default workflow for developing shaders with vgpu
description: 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.
---

# The default workflow for developing shaders with vgpu



## 1. Discover the docs

```bash
npx vgpu docs ls          # top-level paths: /guides plus one entry per package
npx vgpu docs ls /guides  # every guide, by file name
```

The 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

```bash
npx vgpu docs cat getting-started.md
```

`ls /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

```bash
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 loops
```

Single 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

```bash
npx vgpu check ./shaders/glass.wgsl
```

The 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

```bash
npx vgpu doctor --pretty
```

`doctor` 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](/docs/guides/shader-debugging).

## 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:

```ts
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 exits
```

Keep 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](/docs/guides/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](/docs/guides/browser-testing).

## 8. Validate visually with agent-browser

```bash
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 close
```

On 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](/docs/guides/agent-browser-webgpu).

## 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](/docs/guides/shader-debugging) **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.


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)