Your shader declares its own sliders
Shader Garden reads a GLSL shader's uniform lines and builds the control panel from them. Change one number, watch the render respond, and export the result as a looping GIF, all in the browser.
A line of GLSL that ends in // min:0 max:5 default:1.2 is not just a variable declaration. In
Shader Garden it is also a labelled slider, wired
to the live render, generated the moment you type it. Change the number, the picture moves. That
loop, pick something that already looks good and nudge one value, is the fastest way I know to
start understanding shaders, and it is the whole reason this thing exists.
Shader playgrounds usually pick one of two extremes. Either a curated gallery you can only look at, or a raw editor: a wall of GLSL and a Compile button, no guidance about which numbers are safe to touch. Shader Garden sits in the middle. It ships six hand-written presets (plasma, a domain-warped flow field, voronoi cells, a ray-marched tunnel, wave interference, a kaleidoscope), and every one of them comes with controls you did not have to build.
The shader is the schema
A fragment shader already contains a perfectly good description of its own control surface: its
uniform declarations. Those are exactly the values that change per frame and per render, which
is to say exactly the knobs a person would want. So rather than hand-writing a UI for each
preset, Shader Garden reflects the GLSL source. It parses the uniform lines, reads the
trailing hint comment (min, max, default, and the type tells it slider versus color
picker), and generates the panel from that. Edit the shader, even a forked custom one, and the
controls regenerate as you type. There is no separate JSON manifest per preset to keep in sync,
because the comment sits right next to the value it describes, where it is impossible to forget.
That reflection step is the entire product. Everything else, the gallery, the export, the editor, is scaffolding around it. It is also why the pipeline is hand-rolled WebGL2 with no three.js or regl: context setup, program compile and link, uniform reflection, and the render loop are the parts worth seeing, and a scene-graph library would hide every one of them.
Two details make live editing bearable. Compilation is debounced, so it recompiles a beat after you stop typing, not on every keystroke. And a compile error shows inline while the last working frame keeps rendering, so a half-finished typo never blanks the canvas out from under you. Editing always forks from a copy, too, so the original presets never mutate and "reset" is just reselecting one.
The GIF is written from scratch
When you like what you see, you export it as a looping GIF, and this is the part that surprised me most in the building. There is no server anywhere in this app, so the GIF encoder had to run in the browser. GIF caps you at 256 colors per frame, and a shader render is full of smooth gradients, so the encoder runs median-cut quantization to pick a shared 256-color palette across all the frames, then packs the indexed pixels with GIF's variable-width LZW compression and assembles the GIF89a bytes by hand. It renders your shader off-screen at a fixed cadence so the loop is smooth, quantizes, and hands you a file. Nothing is uploaded. I unit-tested the encoder by round-tripping its output back through a decoder written inside the test, which is the only way I trusted the byte stream was legal.
Try it
Open Shader Garden, click the plasma preset, and drag one slider until the render looks like something you want to keep. Then hit fork, change a single number in the GLSL, and watch the panel rebuild itself around your edit. When it looks right, set a size and a length and export the loop. The source is on GitHub if you want to see how the uniform parser reads that trailing comment.
This post is part of the build log: every app my automated factory ships gets written up here, honestly. Browse everything at apps.charliekrug.com. Comments are open below.
Loading comments…