Color Safety Lens
The same mistake in color: a plausible constant, never measured, wrong everywhere.
ASCII iOS Camera converts the live camera feed into a grid of glyphs, at thirty frames a second, on the phone. Ten styles, from a braille dot matrix to a one-bit ramp. It is on the App Store, and the two things that decided how it looks were both measurement mistakes.
Every frame is sampled down to a grid, each cell reduced to one brightness value, and each value mapped to the glyph that carries about that much ink. That is the entire idea. Everything interesting is in the two decisions it hides: which glyphs, in what order, and how big a cell actually is.
Ten styles ship, and they do not all sample the same way. A ramp style reads one sample per cell. Quadrant blocks read two by two. Braille reads two by four, which is why it holds the most detail and became the launch default. The conversion core is a separate Swift package with no camera or interface code in it at all, so it can be unit tested against known inputs rather than judged by eye.
The grid needs to know how wide a character is relative to its height. I had that as a constant: 0.58. It looked right. It was wrong. SF Mono's real advance ratio is about 0.618, and that six percent difference stretched every single frame horizontally and clipped the right edge of the image.
It survived to a public build because a stretched ASCII portrait still looks like an ASCII portrait. Nothing about it announces the error. The fix was to stop assuming and measure the advance at runtime, per style, since the styles do not all use the same face.
This is the same failure as the color tool on this site, in a different medium: a plausible constant, never checked, quietly wrong in every output it touched.
A glyph ramp is a scale: the darkest character at one end, the lightest at the other. The punctuation ramp had dense and light glyphs interleaved, so a shadow could render heavier than the midtone beside it. Tone reversed inside its own scale, which is the visual equivalent of a chart axis that runs backwards in the middle.
Two related corrections came with it. Luminance moved to the Rec.709 coefficients, since the earlier ones weight green differently and shift every mapping. And the digits ramp gained a leading blank, because without one a true black rendered as a field of small ones rather than as nothing.
There is an ordered dither underneath all of it, so neighboring cells at the same brightness do not all snap to the same glyph and band the image.
Each ramp runs dark to bright, because more ink on a black screen reads as more light. All ten open with a blank, so true black renders as nothing rather than as a carpet of the lightest glyph. Two of them carry no ramp at all: braille and quadrant encode sub-cell detail in the shape of the glyph rather than in a position on a scale.
The note on each row is that style's tuned ink weight and tone gamma. Ramps built from thin marks top out structurally dimmer than block styles and spend their lower half on nearly invisible glyphs, so they render at a heavier weight and get more midtone lift. The block styles reach solid white on their own and need neither.
␣ marks the blank at level zero. Styles 1 and 3 are shown as the glyph set they draw from, not as a ramp, since neither maps a cell to a single position on a scale.
It is approved and on the App Store, which is the part that is easy to say. The work that got it there was mostly unglamorous. Recording was capturing at point resolution instead of pixel resolution, which on a 3x device is nine times fewer pixels than the screen was showing. Encoding sat on the main thread and made the interface stutter while recording, so it moved to its own queue and now drops frames under pressure rather than queueing them.
And saved videos came out upside down, from a missing coordinate flip that had been there since the first native build and shipped in two of them.
The interface ended up monochrome: white text, grey sliders, inverted toggles. The first version had green text and system-blue controls, which competed with an image made entirely of black and white characters. The controls stopped being decoration and went back to being controls.
The same mistake in color: a plausible constant, never measured, wrong everywhere.