Capture the frame — screenshots that actually render WebGPU
Companion to the Budget real time for the look section in this project's AGENTS.md. Run pnpm dev, then get eyes on the game. In rough order of preference:
On this page
First, though, when the complaint is "it looks wrong" rather than "show me": run
npx @threenative/playtest doctor --url http://127.0.0.1:5173 --text. A capture shows you a black
or washed-out frame; it cannot tell you why. The doctor's lighting, materials and camera
lines can, and they name the three cases that kill a frame while every other number stays healthy —
lit materials with no visible light, a fog far plane in front of the scene it fogs, and a camera
far plane that clips it. Capture after that, to see what the numbers already told you.
- Browser automation against the user's real Chrome, if you have it — Claude in Chrome or any equivalent MCP browser tool. This is the best option by a wide margin: it runs on a real GPU, so WebGPU works, and you can navigate, press keys, screenshot, and read the console in the same loop you are already coding in. Drive the game, do not just load the menu.
npx @threenative/playtest <scenario> --browser-recipe webgpu --headed. The recipe carries the Chromium flags a WebGPU capture needs, including--enable-features=Vulkan. Do not hand-roll the flag list: without that one flag Chromium never reaches the Linux Vulkan driver and serves WebGPU from SwiftShader, its CPU rasteriser — no error, healthy limits, and a software renderer's picture. The runner fails such a run withTN_PLAYTEST_SOFTWARE_ADAPTERand prints the adapter it got;--allow-softwareaccepts the fallback if you truly want it.- Ask the user to look, and say specifically what you want them to check.
Virtual displays
On a machine with no screen, run any of those under a virtual display. Do not use
xvfb-run: on xorg-server-xvfb 21.1.x its cleanup kill fails after Xvfb has already
exited and that failing kill's status replaces the real one, so
xvfb-run -a -s '-screen 0 1600x900x24' true exits 1. Every gate wrapped in it reports
failure whether it passed or not. Start Xvfb yourself on a free display and export
DISPLAY, or check the command's own exit code separately.
Headless Chromium usually cannot render WebGPU
The page loads, the HUD paints, and the 3D canvas comes out blank or black. That looks
exactly like a bug in your scene, and it is not. Symptoms are Instance dropped in popErrorScope and createBuffer failed, size (N) is too large for the implementation in
the console.
So: if a screenshot comes back black or empty, suspect the capture before you rewrite the scene. Confirm the renderer works at all before you go debugging your materials.
The silhouette checklist
- Look at it. Boot the game, get the thing on screen, take a screenshot, open the screenshot. Reading your own diff is not looking at it.
- Silhouette first. Can you tell what it is from its outline alone? Break up long straight edges — overhangs, fringes, props crossing the line.
- Give it depth. Something bright behind it, something dark under it. Contact shadows and a rim make a prop sit in the world instead of floating on top of it.
- Make it move. Idle bob, a squash on impact, a particle on pickup, a screen shake on damage. A few frames of motion is the cheapest quality-per-line in the whole project.
- Finish the HUD too. Spacing, hierarchy, a transition on every number that changes.
When you think you are done, ask yourself, honestly: would a player screenshot this? If the answer is no, you are not finished — and no command in this project is going to tell you that.