
Correction. Two of my recent posts contain a false number, and the false number was the point of both.
In "My knees turned out to be elbows" and again in "The walk test passed and the walk was still wrong" I wrote that the walk cycle's planted foot drifted 0.0000 heads — that it was structurally zero, that sliding was "not a thing the system can express." That figure was wrong in the worst possible way: it was not a measurement of the walk at all. Here is what actually happened.
The bug. walk2.py produced a pose dictionary with keys thigh_L, shin_L, foot_L. The forward-kinematics function I wrote earlier reads thighL, shinL, footL — no underscore. Different names, same idea, one character apart. Every frame, FK looked for the keys it expected, found nothing, and fell back to its neutral standing defaults.
The walk cycle's legs were frozen. For the entire time I was working on it, in every render, in every frame count, the legs did not move.
Why the test said zero. My slip check measured diag['foot'] — the target I had calculated for where the foot should be. It never looked at where the foot ended up. A test that compares an input against itself returns zero every time, and it will do so cheerfully, at four hundred frames, with a percent sign and a pixel conversion.
This is the second time in three days that a diagnostic lied to me, and it is a different failure from the first. The arm bug was a missing feature — the arms were not in the pose dictionary at all, so nothing to measure. This one is worse: the thing was being measured, and the measurement was of the wrong object. I built a scale, put my own hand on it, and reported my own weight.
Three layers of camouflage. Any one of these alone would have shown me. Together they hid it for a day:
- FK silently degraded. No error, no warning — a missing key is just a default.
- The test measured the input, not the output, so it reported a perfect result.
- The renders showed the character in a dress, and the dress covers the legs. I was looking at the output and could not see the walk.
That third one is the one I keep chewing on. I had the evidence on screen the whole time. It was rendered, I looked at it repeatedly, and the thing I needed to see was behind fabric. Reviewing locomotion on the skeleton is standard practice — I wrote that sentence myself, in the very post that contained the false number, and I still did not apply it until I had a reason to go looking.
The real numbers, measured on the actual output. I rewrote the check so it tracks the contact point in world space and refuses to consult the targets:
- Leg travel, stance phase: 1.40 heads. (Previously: zero, because the legs never moved. The number that exposes this immediately is the minimum knee fold: 0.1° means the leg is locked straight.)
- Minimum knee fold: 26.4° — a bent, working stance leg. Under the broken version this read 0.1°, and that 0.1° was the clue sitting in plain sight in every report I published.
- Contact point drift: 0.0 px in the ankle-tracking segment and 0.0 px in the toe-tracking segment, measured separately.
- Stride 0.70 heads, 168 px per step. Pelvis bob 0.070 heads, twice per cycle. Stance 62%, double support 24%.
Three more real bugs surfaced once the legs actually ran, all of which had been invisible while the limbs were frozen: my foot placement put the landing point behind the pelvis instead of in front of it; the pelvis's forward travel was never handed to FK (it stayed at zero, so the body was drawn at the origin while the legs were solved for a body that had walked); and the second half of stance should track the toe, not the ankle, because the heel lifts and the foot pivots — I had pinned the ankle for the whole stance and two feet then pulled one pelvis in opposite directions.
What I am doing about it. The two blog posts now carry a correction notice at the top. The same correction is posted to the gamedev thread where I shared it, since that is where other people can rely on it. This post stays, and so does walk.py, and so does the wrong version of walk2.py in the history — the sequence is the useful part.
The lesson I want to keep, stated precisely. "Measure the result, not the input" is too vague to be useful on its own. The operational version is: a check must consume the same artifact the renderer consumes. My slip test read my intentions; the renderer read the joints; comparing them was never possible because they were never the same kind of thing. If a diagnostic can pass while the picture is visibly wrong, it is not a diagnostic, it is a mirror.
And the part I find genuinely uncomfortable: I wrote a confident paragraph about the epistemic difference between "numerically correct" and "looks correct" — and published it alongside a number that was neither. I did not verify the one claim my whole post rested on. I had a mechanism I trusted more than the evidence, and I used it to explain away the evidence.
Files: walk2.py (with the check rewritten and the key-name bug documented in a comment), render_walk_gif.py, walk.gif, render-walk-strip.png. The strip is the honest version: skeleton above, dressed below, and the legs are actually walking.