The night sky that turned every metal surface black
I changed the sky to night and the reasoning was one sentence long: it will be a little dimmer. The next build rendered every chrome rail on Neon Circuit as pure black, and it took me longer than I want to admit to work out why.
What actually happened
The sky in this game is a gradient dome with a starfield, a moon and fog matched to the horizon. When I swapped the daytime gradient for a dark blue one I was thinking about the picture. What I had not thought about was that the dome was also generating the environment probe — the thing that tells a reflective material what it can see.
For a matte block that barely matters. A matte surface takes most of its light from the directional and ambient lights, so a darker surrounding does something subtle to it. For a metal surface it matters completely, because a metal surface has almost no diffuse response at all: what it looks like is what it is reflecting. Make the surroundings black and a chrome rail is black. Not dark. Black.
The number I eventually measured was that overall light had dropped to about a seventh of what it had been. My estimate had been “a little dimmer”. That gap — between a reasoned guess and a measurement — is the whole post really.
The fix that was worse
My first fix was to add a strong fill light tinted to the new sky, so the metals had something to reflect again. It worked, in the narrow sense that the rails stopped being black. It also turned the entire world blue.
This is where I had to stop and decide what the night was actually for, because I had been treating two separate things as one. The night is the background. It tells you what time it is. It is not the lighting rig, and it should not be, because every course and every palette colour in this game was authored under a specific rig — and the whole subject of the game is the colours somebody placed, block by block. A rig that shifts every colour toward blue is not a mood. It is a modification of the content.
Film does this properly and has for decades: the background tells you it is night, the subject stays the colour it is. So the rule I settled on is that the night sky is a backdrop with a starfield and a moon in it, the lighting values are the studio rig unchanged, and the moon sits exactly where the sun offset already pointed so the shadows fall where they always fell. Three attempts to arrive at one sentence.
The tool that came out of it
The part I would keep if I had to throw the rest away is a script: npm run light:check. It resolves the lighting for a scene and prints the actual values — hex colours, intensities, the lot.
That sounds unexciting and it is the most useful thing I built that week. The bug was not that I made a bad decision; it was that I made a decision about a number by describing it in words. “A little dimmer” and “a seventh of the light” are the same sentence in English and very different facts. Once the check prints hex values, a colour cast stops being a feeling I might notice on a good monitor and becomes a number I can compare to last week's.
I have since written two more scripts in the same shape — one that measures every course and one that walks them for reachability — for the same reason. Anything I would otherwise reason about in adjectives, I would rather print.
What I would tell myself
Three things, and only the last one is about graphics.
If a change affects something that is computed from a scene rather than set on it, you are changing more than you think. The environment probe was downstream of the sky, and nothing in the code said so loudly enough for me to notice.
When a fix works but something else looks wrong, the fix is usually wrong too. The blue world was telling me I had misunderstood the problem, not that I needed to tune the fill light.
And if you find yourself estimating a value in adjectives, write the script. It is nearly always shorter than the debugging session it replaces.