The Dice Weren't Silent. They Were Floating.

The Dice Weren't Silent. They Were Floating.

The Dice Weren't Silent. They Were Floating.

My dice roller throws 3D dice, and I wanted each one to make a little clack as it landed. One sound per die.

My assistant built it, and a test throwing fifty dice found that one to four of them stayed silent. Every time. Different dice, different counts, never zero.

Each fix it tried passed once and failed the next run. Two rounds of good theories later, a two-minute probe found the real answer. The dice weren't silent at all. They had never landed.

Theory one: dice landing on dice

The sound fired when a die "reached the floor." The first idea was that some dice never reach the floor, because they land on top of another die and stay there.

That's a real thing that happens with fifty dice. So the assistant changed what landing meant. Instead of "touches the floor," landing became "the fall suddenly stops," wherever that is.

Ran the test. Still silent dice.

Theory two: dice sliding gently

The second idea was more subtle. A die that slides slowly down the face of another die never stops suddenly. It just eases to a halt, so "sudden stop" never fires.

Also a real thing that happens. So landing became "the fall stops at all," sudden or not.

Ran the test. Still silent dice. One run was worse than before.

Both theories were reasonable. Both described real physics. Both fixes made the detector more forgiving. And neither changed the result, which should have been a clue in itself.

The probe

At this point we stopped theorizing and looked at the data.

The assistant added a small throwaway probe. It recorded every die's height over the whole throw, then printed the full trace for only the dice that stayed silent. Not the forty-seven that worked. Just the failures.

The pattern was obvious in seconds. Every silent die had been released between 5.7 and 6.3 seconds into the throw.

The dice leave the hand one after another, 90 milliseconds apart. The physics recording stops at six seconds. With fifty dice, the last few were released right at the end, or after it. They were cut off mid-air.

Some of them sat at their release height for the entire recording.

So they weren't just silent. They were being drawn floating. Hanging in the air above the table, perfectly still, in a dice roller.

The landing detection had been right the whole time. There was nothing to detect. Those dice never fell.

The sound bug was a visual bug

This is my favorite part. A bug reported as "some dice don't make a sound" was really "some dice never land." The missing sound was the only symptom anyone noticed, because a missing click is easy to hear and one frozen die in a pile of fifty is easy to miss.

Two rounds of fixes were spent making the sound code smarter, and the sound code was never the problem. The fix belonged in how the throw was timed, so that every die is released with time to land before the recording ends.

Debug the failures, not the theory

I think the lesson is worth spelling out, because both theories felt like progress.

When a fix "works sometimes," it's tempting to keep refining. Make the detector a bit looser, handle one more edge case, run it again. Each theory explains some of the failures in your head. None of them has been checked against the actual failures.

The probe did one thing differently. It looked only at the cases that failed, and it looked at the raw data rather than a summary. Forty-seven working dice are noise. Three silent ones, with their whole history printed, told the story immediately.

If your fix works sometimes, stop improving the fix and look at the times it didn't.

Why an AI is prone to theory loops

An AI assistant is very good at producing plausible explanations. Ask why some dice are silent and it'll give you three reasons, each one sensible, each with a fix attached.

That's useful. It's also a trap, because every plausible explanation comes with a plausible code change, and code changes feel like work. Measuring feels like stalling.

So now when a fix fails twice, I ask for a probe before a third theory. Print the raw data for the failures. It's nearly always faster than the next guess.

Keep the probe out of the source

One detail I liked about how the assistant did it. The probe didn't go into the source code. It patched the built output only, ran the test, and left it there.

The next normal build overwrote it. No cleanup commit, no stray logging left behind, no "remove debug code" task to forget.

For a throwaway question, that's the right place for throwaway code.