Prototyping in the Medium: What a WebGL Flower Taught Me
I recently built a random gradient generator for my lab. It started from Justin Jay Wang's notes on random gradients, which describe three looks: radial blends, noise heightmaps, and a soft floral style that he made with an AI image model.
I wanted the floral one to be generated, not prompted. So I built it in WebGL: real 3D petals arranged on a spiral, lit from behind, and blurred like a macro lens with a shallow depth of field. Every click makes a new flower from a seed.
The rendering was the fun part. The lessons were in everything that came after the first working version, because almost none of the important decisions could have been made in a design file.
Slider ranges are design decisions
The tool has a Haze control that sets how soft the flower looks. My first version ran it from fully sharp to very soft, which seemed like the obvious range.
Then I used it. Below about halfway, the flowers just looked bad: the petals turned hard and plasticky, and nothing about the sharp end was worth keeping. So I remapped the slider. What used to be 50% became the new 0%, and the whole range now lives where the results look good.
A range that includes bad results isn't flexibility, it's homework for the user. You only find out where the bad part is by moving the slider yourself, many times, on real output.
Look at every edge case, not the hero shot
The flower has a petal count from 10 to 48, and each count produces a different arrangement. The hero shot looked great. But at maximum haze, a few petal counts still showed rough, shard-like edges on some petals.
The only way to find them was to render every variation and look. The cause turned out to be technical, a blur pass that left a hard step at the edge of the focus area, and the fix was a smoothing pass plus making the minimum blur scale with haze. Finding it was the design work. Fixing it was the engineering.
A designer working from mockups would have signed off on the hero shot. The bug only exists in the running thing.
Color is perception, not numbers
Some palettes include yellow, and yellow kept rendering as olive or mustard. The colors were mathematically correct. They just didn't look like yellow, because the shadows in the lit petals pulled them toward brown.
The fix was a small lightness boost for yellows and amber-tinted shadows instead of neutral ones. No color picker would have shown the problem, because the problem only appeared after lighting, blur, and tone mapping had all done their part.
Performance is something you feel
The heightmap view has a Scale slider that zooms the noise in and out. It worked, but dragging it felt terrible: the image barely updated while the pointer moved.
The cause was a sensible-looking optimization. Every time the setting changed, the renderer threw away the render in progress and started a new one. During a fast drag, a new value arrived before any render finished, so almost nothing reached the screen, about 0.7 updates a second. Letting in-progress renders finish, and dropping only the ones that were already outdated, took it to around 41 updates a second. Same code, same renderer, completely different feel.
The floral renderer had the same lesson at a different scale. Capping the blur resolution to what the blur radius actually needed took a preview render from about 11 ms to 2.6 ms, and a slow software renderer from over a second to under 200 ms. On paper both versions "worked." Only one felt like an instrument.
Small affordances come from use
The palette shows a row of color dots. My first version copied the hex code when you clicked one. Using it, I wanted to know it was copyable before clicking, so the dots now show a copy icon on hover and a check after copying. It's a tiny change that I would never have specified up front.
Why build in the medium
None of these decisions were hard. They were just invisible until the thing was running: the slider range, the 48 variations, the yellow, the drag, the dots. A static design would have looked finished and been wrong in all five places.
That's the case for prototyping in the medium. Not because code is better than design tools, but because the medium is where the product is actually experienced, and that's where most of the decisions turn out to live.
You can try the gradient tool and judge the Haze range for yourself.