Building with coding agents¶
Noisemaker has two layers, and a coding agent is useful at both.
The Polymorphic DSL composes existing effects into a render graph. The effect layer is where effects come from: your own GLSL and WGSL, wrapped in a definition that declares parameters, passes, and UI. Neither layer is the “real” one — most sessions move between them, and an agent earns its keep in both places.
This page covers the workflow for each: what context to give a model, and how to drive the engine — compile, render, measure, compare backends — as tool calls rather than hand-rolled glue.
Composing: the DSL layer¶
A DSL program names effects and wires them together. The engine compiles it to a GPU render graph for WebGL2 or WebGPU, allocates resources, and schedules the passes:
search synth
noise().write(o0)
render(o0)
Nothing here is shader code, and for a large class of work nothing needs to be: the library covers generators, filters, mixers, particles, and simulations, and models are good at recombining named ideas. Errors come back as structured diagnostics rather than a black screen, which is what an iterating agent needs.
shade-mcp is a shader development harness¶
shade-mcp is where the authoring loop gets fast. It is not a documentation lookup — it runs a real Chromium against a real viewer, compiles your shader, renders it, and measures what came out. The tools exist because shader bugs are silent: the program compiles, the frame renders, and the image is wrong.
It points at whichever effect library you are working in — this repository’s, a portable effect directory, or your own — so the same tools serve a contributor and someone who has never cloned Noisemaker.
Compile and see the frame
compileEffect— compiles an effect and returns pass-level diagnostics, so a failure names the pass and the line rather than producing a black canvas. Takes a glob or CSV to sweep a whole library.renderEffectFrame— renders a frame and computes image metrics: mean RGB, variance, blank and monochrome detection. Optional PNG capture. “It rendered” and “it rendered something” are different answers, and this gives the second one.describeEffectFrame— sends the rendered frame to a vision model and describes it. The check for “is this what I meant” that no numeric metric performs.runDslProgram— compiles and executes arbitrary DSL, for exercising a new effect in composition rather than in isolation.
Keep the WebGL2 and WebGPU halves honest
Writing an effect twice is the part of this layer that actually hurts, and it has the most tool support:
testPixelParity— renders the same effect on both backends and diffs it pixel by pixel within an epsilon you choose. A ported branch that reads one texel off shows up as a percentage, not as a vague sense that WebGPU looks different.checkEffectStructure— the linter for the definition layer: a GLSL program with no WGSL counterpart, shader files nothing references, uniforms declared but never bound, reserved words, names that shadow builtins, naming that breaks convention.compareShaders— static structural comparison of a GLSL/WGSL pair: function names, uniforms, line counts.checkAlgEquiv— semantic comparison of the same pair by a model, ignoring syntax, returningequivalentordivergentwith its confidence and specific concerns.
Verify behaviour, not just output
testUniformResponsiveness— drives every uniform and reports which ones changed the image. Catches the parameter that is wired to the UI but not to the shader.testNoPassthrough— asserts a filter actually modifies its input.benchmarkEffectFPS— frame rate, jitter, and frame timing against a target.analyzeBranching— flags branching that costs more than it saves.
Find things
searchEffects by concept, tag, algorithm, or visual style;
searchShaderSource by regex across every effect’s GLSL, which is how
you find the effects that already implement the hash you were about to
write; analyzeEffect for a full definition plus shader source;
searchShaderKnowledge over curated notes on DSL grammar, GLSL
techniques, and common errors; generateManifest to re-scan an effects
directory after adding one.
Client configuration for Claude Code, VS Code Copilot, Cursor, and Windsurf, plus the environment variables that point it at your effects directory and viewer, are in the shade-mcp README.
The development loop¶
Composing and authoring run the same loop; only the artifact differs.
Describe what you want. For composition that is a DSL program; for a new effect it is the shader plus its definition.
Compile. Diagnostics come back structured, per pass.
Render a frame and capture metrics — or the image itself.
If the effect ships both backends, diff them.
Feed the diagnostics, metrics, parity report, or frame back and iterate.
Steps 2–4 are the part most shader work leaves to a hand-rolled test page, a screenshot, and an eyeball. As tool calls they close the loop for a coding agent — and they are just as usable from a terminal by someone who writes every line of GLSL themselves.
Give your model the context pack¶
Machine-readable references ship with the project:
llms.txt — the short public map: what Noisemaker is, where the specs live, and the agent tooling.
llms-full.txt — the full development contract for agents working in the codebase.
The language spec, pipeline spec, and effect definition spec.
The Portable Effects specification — format, shader requirements, parameters, and a starter effect to work from.
Every effect carries a structured definition and an entry in the effect manifest, so the library is searchable by machine as well as by eye.
For the composition layer, llms.txt (or its URL) in your agent’s
context is the whole setup. For effect authoring, add the format
specification and one existing effect directory as a worked example.
Compositions are recipes¶
Models are good at recombining named ideas, and the effect library is built for that: an underwater look is a caustic over cell noise; a fractal zoom starts from julia or mandelbrot; organic growth comes from cellular automata and the particle simulations. Browse the effect library to see what the names mean, then ask your model to combine them — and when the recipe runs out, write the effect the recipe was missing.