Build, debug, or review WebGPU renderer work — three.js/TSL scene layers, node materials, or raw WGSL passes and compute. Use when changing GPU resource layouts, render or compute passes, node materials, bind groups, buffers, shaders, frame orchestration, depth/overlay composition, capability handling, performance, or browser-verified renderer visuals.
npx skills add https://github.com/dzhng/skills --skill renderer
Use this for WebGPU renderer work where correctness depends on GPU resource
ownership, pass orchestration, shader/material layout, depth semantics, or
browser-verified output. Find the current owners in the codebase — projection,
depth convention, environment presets — before changing anything; do not assume
this skill knows today's module layout.
contracts, and validation routes before adding a pipeline or buffer.
groups, ownership, update frequency, read/write access, and lifetime.
buffer transforms, and work-list construction.
overlay phases when visibility semantics differ.
modes, frame phases, semantic roles, vertex strides, and bind group schemas
should live in one canonical module/source and be imported by renderers,
shaders, and verifiers.
concept (lighting environment, haze, material palette, terrain projection,
water mask), refactor to the shared primitive you would design from scratch.
Do not bolt an adapter or alias beside the old owner unless it is a tiny
temporary bridge with a named removal path.
changed pass, open the produced PNG, and use compare-screenshots when a
visual before/after needs telemetry.
modules currently own them. A camera is posed through the shared camera
helper, never hand-rolled orbit math in a route or pass; a preset-dependent
material knob lives on the environment owner, never in a pass.
depth ranges; whichever convention is in force, it is engine-wide — depth
compare direction, clear value, and format move together or not at all.
and perf gate as harness — never a ride-along on a feature commit.
into obvious 16-byte slots when it reduces layout ambiguity.
read, read-write, and write; make renderer stats and GPU pipeline state
speak the same language.
Adding a depth attachment is a pass-wide change: update every pipeline in the
pass, split the pass, or keep the pass depthless.
class, prop type, material, or instance bucket is valid only when the pass has
the correct depth semantics for the world it draws.
world objects can write depth; translucent decals, shadows, selection rings,
roads, and UI overlays need separate read-only depth or overlay phases.
ground cues, shadows, roads, or other depth=read world decals begin, no
later world pass should write depth; otherwise the frame is relying on
painter-order color overwrites instead of the depth buffer.
submit it through the world camera and let real geometry occlude it; reserve
screen overlays for labels, HUD, minimaps, debug UI, and deliberately
non-world effects.
semantic ground-cue/effect pass, not by an incidental terrain or mesh bucket.
A correct cue can be a read-only world decal or tactical line without being
part of the terrain geometry count.
world objects they mark. A selection ring, shadow, road, or footprint that
assumes flat z=0 can disappear under the terrain or drift away from the
model even when its x/y coordinates are correct.
rail, river, or path continuity by cutting endpoint gaps around occluders;
resample the path onto the canonical surface and let depth-tested world
objects occlude it.
terrain. An instance layout that carries only x/y/scale can look fine on a
flat fixture while trees, rocks, crowds, or buildings float, sink, or lose
depth ordering on the real map.
labels, props, or hit tests must stay geographically aligned while the camera
moves, project and draw them from the same 3D surface/height contract. A flat
textured underlay plus separate raised world objects will drift under
perspective even when the source coordinates are correct.
coast foam, fog reveal, biome tints, and terrain overlays must sample or be
generated from the same world-space mask that owns the gameplay geography;
unmasked decorative quads/ellipses are only valid for non-geographic
atmosphere and must not independently decide where land or water exists.
sky, soldiers, or props all need the same weather, haze, palette, or light,
make that a shared renderer contract and have every pass consume it. A wrapper
that preserves old duplicated constants is still a failed architecture unless
it is explicitly transitional and tracked.
overview, reflection, shadow, and debug views should expose or consume
canonical world-space anchors instead of carrying private scale/offset math;
verifier tolerances should match the source grid resolution.
first, submit the nested/rear object later, then sample or crop pixels that
prove depth, not painter order, owns visibility.
PNGs after WGSL, pipeline, camera, pass-order, depth, or blend changes, and
reject black frames, transparent canvases, flattened occlusion, or UI layered
over world geometry by accident.
prove the intended subject is framed: derive camera targets from live
renderable bounds or explicit semantic anchors, and reject frames that show
mostly empty terrain, sky, water, or one flat colour while entity stats look
healthy.
route stats and app hooks alive while command buffers are invalid and the
canvas is black. Capture console warnings and fix the root contract, commonly
vertex stride/attribute offsets, bind-group layout drift, attachment mismatch,
or a depth mode that no longer matches the pass.
let bindings are immutable. When staged shader values need overrides,use var; reassigned let expressions can invalidate the pipeline and leave
JavaScript stats healthy while the actual canvas is black.
If a shader path depends on a texture, mask, depth mode, or feature toggle,
the route stats should say which path is active and what resource dimensions
it consumed.
Hard-coded verifier copies of depth formats, phase names, role maps, or vertex
strides drift into false confidence.
show a clear failure/fallback UI, but it must not silently route production
visuals through an unrelated renderer to hide missing GPU renderer behavior.
forests, props, particles, or decals, reserve by canonical region/tile or
connected feature before global sorting; batching and top-N selection are
performance details, not permission to drop whole visible landforms.
content. NaN instance fields, zero coverage, bad projection, or invalid
shader state can leave counts healthy while pixels are blank; pair stats with
crop/content probes for each visual class.
buffer, opaque/transparent sort order inverts silently — zero validation
errors, and a low-renderOrder backdrop can cover the whole world. After any
depth-convention or sort change, prove draw order empirically (hostile-order
fixture), and expect to own the sort comparators.
positionNode silently discards instanceMatrix. Smell: everyinstance renders at the origin or with one shared transform while counts look
healthy. Per-instance work must re-apply instancing explicitly.
normalNode is view-space. Lighting math that assumes world-space normalsreads plausibly wrong (moves with the camera); transform deliberately.
time node is BANNED in renderer code — it breaks byte-stablesnapshots. All animation keys off an owned, injectable time uniform plus
seeded RNG.
far=Infinity; use a largefinite far that converges on the infinite-limit matrix, and pin the
equivalence with a unit test.
renderer.info resets every browser frame via three's internal loop —snapshot the counts at render time before publishing stats.
attribute() inferred as string dropsthe whole swizzle/operator surface) — use explicit generics; if published types
don't cover a module, keep a *narrowed* local declaration, never any.
detail (a distant crowd fades to mush); the antialias choice is a per-world
contract, not a default.
gameplay zoom fires at overview rig distances; range floors must clear the
rig's maximum eye distance.
TSL/WebGPU (including reverse-Z, timestamps) faithfully but orders of
magnitude slower; perf gates run on hardware only, and the standing crowd
perf gate + frame-time ledger judge every renderer-affecting slice.
to software is silent: Playwright's default headless is
chromium_headless_shell, which ships no GPU backend at all and lands on
SwiftShader without an error, so the run looks green and every number in it is
fiction. Launch with channel: 'chromium' for the full browser against the
real device, and read the unmasked renderer string out of the page
(ANGLE (Google, ..., SwiftShader Device) versus
ANGLE (Apple, ANGLE Metal Renderer: Apple M3 Pro)) into the capture's own
report so the check is recorded, not remembered. One measured case: the same
1024x1024 scene ran 32fps on the shell and 127fps on the real GPU — a 4x lie
from one line of launch config. Fix a fallback with
npx playwright install chromium rather than keeping the number with a
caveat attached; a software-rendered run still counts for pixel, budget, and
functional checks, but its timings are void.
GPU, and the frame-time collapse drifts app time away from wall time: timed
phases and screenshot baselines flake for a reason that never appears in the
diff. workers: 1 for anything that renders.
shell shape, not only lab shells — a capability that only ever ran in a
simpler configuration can be invalid in the real one.
GPU-side phase preparation for scale.
culls an instanced mesh as one bounding sphere; cull via CPU instance
compaction or GPU-driven culling, publish culled counts in stats, and once
shadow cascades exist cull against the union of view + cascade frusta or
shadows pop at the screen edge.
removable.
state, apply,integrate, constrain, and correct; use ping-pong buffers/textures when
a pass reads previous state and writes next state.
grids, tiles, or compacted work lists over O(n^2) scans.
counts, tile sizes, LOD thresholds, pass timings, and readback limits.
the generated PNG yourself.
baseline path. Baselines prove what was accepted before; actual captures prove
what the current GPU code rendered.
every tile or frame class, not just one representative. A grid can pass while
repeated crops are consistently off-center, clipped, or aimed between the
models.
fixture with known land/water/prop points and sample both semantic state and
rendered pixels across multiple cameras before debugging the full production
map or scene.
verifier intentionally preserves transient effects while the default freeze
hides them, compare same-tick hidden-vs-visible pixels so the cache cannot
silently reuse the wrong frame.
sprites, flags, depth overlaps, or LOD artifacts.
or baseline; do not tune visible spacing against the center of an atlas quad
when the requirement is about an icon edge, marker edge, or text baseline.
exist. If a GPU renderer label atlas pass owns text measurement, use that pass for
collision/culling policy and expose cull stats; do not scatter per-entity
offsets to paper over overlapping labels.
content counts can explain movement, but acceptance depends on named visual
requirements and human readability.
screenshot-critique with anunprimed second pass before accepting it.
blend state.
tilt, zoom, or perspective changes.
invalid pipeline or command buffer.
report submitted entities; suspect camera framing, crop origin, projection,
or all-content-hidden CSS before chasing mesh generation.
phase should be.
leaving no clear cause for the result.
camera helper.
time, performance.now, unseeded RNG) first, then parallel test workers
contending for the GPU, before suspecting the GPU itself.
renderer string before the code; a lost GPU reads as a slow commit.
Take dzhng/renderer from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.
The instructions reference npx.
Without those the skill loads but fails at the first command.