That observation became a scrappy raytracer prototype that sat on my disk for years. It worked, barely, and every change broke two other things, so I mostly left it alone.
What finally got it over the line was working on it with AI. Not "the AI wrote it": the idea, the model of the room, and the color decisions are mine. But pair-programming with an agent turned the parts I'd been dreading (porting to TypeScript, a WebGL2 preview that falls back to the exact CPU render, a proper web component, nailing down the determinism guarantees) into afternoons instead of months. For the first time the project was actually fun to tinker with, so I did, a lot.
The result: one sphere in a five-sided room, up to three colored lights. Instead of picking colors from a wheel, you place lights and sample points off the sphere's surface. Click it, or drag a line or circle across it and sample N points along the shape. The palette stays coherent for the same reason the tennis ball's did: every color is the same world seen from a different angle. Mirror walls act as virtual light sources, which is the tabletop bouncing warm light back onto the ball.
The API is deliberately scoped to exactly that: one sphere, lights, and simple sample shapes. It's all I needed for generating palettes, and building a direct-manipulation UI for even this small world (draggable lights, shapes that roll onto the back of the sphere like a trackball) was already plenty of complexity.
The library is ~6 kB gzipped, zero dependencies, DOM-free, and deterministic: same scene, same colors, and a sampled color matches its rendered pixel bit-for-bit (all shading in linear RGB, sRGB only at the 8-bit boundary).
Playground: https://meodai.github.io/ray-color/
code: https://github.com/meodai/ray-color
Happy to answer anything about the shading model, the sampling API, or what it was like dragging a years-old prototype into shape with an agent.
Show HN: Ray-color, color palettes sampled off a raytraced tennis ball
meodai.github.io