Lindsay Edwards

The slider that did nothing

On this page

I was building a small mobile game in Godot, mostly to get out of my usual backend and AI work for a weekend. The look I wanted was old hardware: chunky, snapping geometry and warped textures. I wrote a custom shader for it with two knobs, wired up an intensity slider, and felt quite pleased.

Then I dragged the slider from 0 to 100 percent and the screen did not change.

Not subtly. Not “hard to tell”. Nothing. I am not a game dev, so my first assumption was that I had wired the uniform up wrong. I had not. The shader was doing exactly what I told it to, which turned out to be nothing at all.

Computing a value is not outputting a value#

The first effect was vertex snapping, the thing that makes old console geometry jump between pixels instead of moving smoothly. My code did the maths correctly. It took the vertex position, snapped it to a grid, and stored the result.

In a varying. Which I then never wrote back to the actual output position.

So every frame, the GPU dutifully computed a perfectly snapped position and threw it away, then drew the geometry at its normal, un-snapped position. The calculation was real. The effect was not, because a computed value that never reaches the output is just heat.

Faking a thing the engine already undoes#

The second effect was affine texture mapping, the wobble you get when a renderer does not perspective-correct its textures. I wrote a blend between the normal perspective UV and my “affine” UV, controlled by that intensity uniform.

Here is the part I did not think through. My affine UV was the perspective UV times depth, divided by depth. That is algebra for “the perspective UV again”. So I was blending a value into an identical copy of itself.

// intensity from 0 to 1, and both sides are the same value
uv = mix(uv_persp, (uv_persp * depth) / depth, intensity);

Cranking intensity to 1 gave me exactly uv_persp. So did 0. So did anything.

The deeper miss was that the engine already perspective-corrects for you. To fake the retro wobble I did not need to add an effect, I needed to deliberately defeat one the renderer was doing on my behalf. A linear self-blend cannot do that. You have to actually break the correction, not mix a number with itself.

If turning a parameter to 100 percent changes nothing on screen, you have not built a feature. You have built decoration with a slider glued to it.

Test the extremes, not the vibe#

The reason both bugs survived so long is that I verified them the lazy way. The shader compiled. The scene looked roughly like an old game. My brain filled in the rest and moved on.

What I never did was pin the slider to its extremes and demand a difference. Zero should look obviously wrong. One hundred should look obviously overcooked. If both ends look the same, the thing in the middle is doing nothing, and no amount of “looks about right” will catch that.

This is not really a graphics lesson. Any config-driven behaviour has the same trap. A feature flag, a retry count, a cache size, a blend weight. Ship one of those dead in a real product and you have a setting a customer can toggle all day with no effect, and a support ticket nobody can reproduce. It is easy to confirm the code runs and much harder to confirm the config actually moves the output. So push it to the ends. If the extremes are indistinguishable, the knob is fake, and you would rather learn that from a slider than from someone asking why the setting does nothing.

Keep reading