The Hidden Bug in Color Blindness Simulators: Why Pure Red Never Turns Fully Gray
A color blindness simulator takes the colors in an original image and shows you what a specific type of color vision would actually perceive instead. This conversion is typically implemented with a "color transformation matrix." This guide covers how that works, and then walks through an implementation error found by directly reading — and numerically verifying — the actual source code behind modoohub.com's Color Blindness Simulator.
1. Types of color blindness — which cone cell is at fault
Human color vision is determined by the combined signal from three types of cone cells in the retina (L, M, and S, sensitive to red, green, and blue wavelengths respectively). When one of these is missing or underperforming, the result is color blindness. Red-green color blindness is the most common form, split into protanopia (a defect in the L cone, affecting red) and deuteranopia (a defect in the M cone, affecting green). The two have different root causes but share the same practical effect: difficulty telling red and green apart. Tritanopia (blue-yellow color blindness, the third type) stems from a defective S cone and causes confusion between blue and yellow tones; it's extremely rare, occurring in about 0.003% of people. Finally, achromatopsia — where none of the three cone types function normally — leaves a person unable to distinguish color at all, perceiving only brightness. It's an extremely rare condition, affecting about 0.001% of people.
2. Why simulate with a color transformation matrix
Each vision type can be approximated as a linear transform that maps the original RGB values to the RGB values that type of vision actually perceives. Written as a table of numbers, this mapping becomes one equation per output channel — "R weight × original R + G weight × original G + B weight × original B (+ an offset)" — for each of R, G, and B. Three channels means three equations, which standardly takes the form of a 3-row-by-4-column matrix (3 weights plus 1 offset per row). This approach is popular because it only takes a handful of multiplications and additions per pixel, light enough to process an entire image in real time.
3. Verifying the actual code — an indexing bug that reads a 3x4 matrix as if it were 3x3
Opening the source of color-blindness-simulator.html directly and checking it shows that each vision type's matrix is in fact defined as 12 numbers (3 rows by 4 columns). The achromatopsia matrix, for example, is [0.299,0.587,0.114,0, 0.299,0.587,0.114,0, 0.299,0.587,0.114,0] — the standard grayscale-luminance formula (brightness = 0.299R + 0.587G + 0.114B) repeated identically across all three output channels, exactly as it should be. But the applyMatrix() function that actually applies this matrix reads only the first 9 values, matrix[0] through matrix[8], in sequence as if it were a plain 3x3 matrix, and never touches the last 3 values (indices 9-11, the offset for each row).
Feeding pure red (255,0,0) through this reveals exactly what that misalignment does. The R output correctly uses matrix[0..2], (0.299, 0.587, 0.114), giving 255×0.299 ≈ 76, matching the intended value. The problem starts with the G output. It should use the second row, (0.299, 0.587, 0.114), but the code instead reads matrix[3..5], which is (0, 0.299, 0.587), giving 255×0 = 0. The B output is likewise thrown off, reading matrix[6..8], (0.114, 0, 0.299), giving 255×0.114 ≈ 29.
4. So how reliable is this tool, really?
The short answer: this is a different kind of problem than the usual "matrix-based, so it's a rough approximation" caveat. A normal approximation error stays close to real color-vision experience with only minor deviations. Here, the matrix data itself is misaligned in the code, so specific channels are computed with systematically wrong coefficients mixed in. The broad direction — red tones generally reading as darker — still comes through to some degree, so the tool can still give a rough sense of things. But you shouldn't trust it for precise hue or saturation. For a project where accessibility review actually matters, don't rely on this result alone — cross-check against another verified color blindness simulation tool.
Frequently Asked Questions
Q. Why are color blindness simulators built with color transformation matrices?
A. Each vision type differs in how strongly its cone cells respond to particular wavelengths, so you can approximate the shift with a linear transform that maps original R/G/B values to the R/G/B values that type of vision actually perceives. Written out as a table of numbers, that transform is the color transformation matrix — it's simple and fast to compute, which is why it's widely used for real-time image processing.
Q. Why doesn't the achromatopsia simulation produce a truly neutral gray?
A. The achromatopsia matrix in color-blindness-simulator.html is defined as 4 values per output channel (3 weights plus 1 offset) across R, G, and B — 12 numbers in a 3-row-by-4-column layout. But the actual compute function, applyMatrix(), mistakes it for a plain 3x3 matrix and reads only the first 9 values (indices 0-8), never touching the remaining 3 (indices 9-11). As a result, the coefficients feeding the G and B channels shift by one slot, so feeding in pure red (255,0,0) produces (76,0,29) instead of the intended (76,76,76) — a pixel that still visibly holds color.
Q. Does this bug only affect achromatopsia?
A. No. Every vision type other than normal vision — protanopia, deuteranopia, tritanopia, and the milder anomalous variants — shares the same 12-value matrix layout and passes through the same applyMatrix() function, so all of them are affected by the identical indexing error. Achromatopsia just happens to be where the error is most visible, since its correct answer is a perfectly neutral gray and any leftover color stands out immediately.
Q. Is it still worth using for design review purposes?
A. The broad direction — red tones generally reading darker — still comes through to some degree, but you shouldn't trust it for precise hue or saturation. For a project where accessibility review actually matters, don't rely on this result alone; cross-check with another verified tool as well.
Q. What's the actual difference between red-green, blue-yellow, and total color blindness?
A. Red-green color blindness (protanopia/deuteranopia) comes from a defect in the cone cell that detects red or green. Tritanopia comes from a defect in the cone that detects blue, causing confusion between blue and yellow tones, and is extremely rare (about 0.003% of people). Achromatopsia means none of the three cone types function normally, so color itself can't be distinguished at all — only brightness — and it's extremely rare, at about 0.001% of people.