Skip to course content
Free data visualization course

Data Visualization and Dashboard Storytelling

Unit 08.02: Palettes that survive colour-blind readers

Red and green are the most common encoding pair and the one that fails most often.

What the palette looks like to a colour-blind reader

A rough simulation of deuteranopia applied to three palettes.

The code shows the separation that survives for each.

def simulate_deuteranopia(rgb):
    """Rough approximation: the most common form of red-green deficiency."""
    r, g, b = rgb
    return (int(0.625 * r + 0.375 * g), int(0.7 * g + 0.3 * r), b)


PALETTES = {
    "red / green":       [(214, 39, 40), (44, 160, 44)],
    "blue / orange":     [(31, 119, 180), (255, 127, 14)],
    "blue / light blue": [(31, 119, 180), (174, 199, 232)],
}
for name, colours in PALETTES.items():
    seen = [simulate_deuteranopia(c) for c in colours]
    diff = sum(abs(a - b) for a, b in zip(*seen))
    print(f"{name:20} as seen: {seen}  separation: {diff:>4}")

print("""
Red and green collapse to nearly the same colour. Blue and orange stay
distinct, which is why it is the default first pair in most modern palettes.

Around 1 in 12 men has some form of this. Do not rely on hue alone -- vary
lightness too, or add a shape or a direct label.
""")

Red and green collapse to nearly the same colour. Blue and orange stay clearly separated - which is why it is the first pair in most modern default palettes rather than an aesthetic preference.

The third pair, blue against light blue, works for colour-blind readers because the separation is in lightness rather than hue, and lightness is preserved.

The mistake this prevents

The mistake is relying on hue alone even with a safe palette. Vary lightness too, or add a shape or a direct label - then the chart survives greyscale printing as well, which is a separate and common failure.

Takeaway

Do not encode meaning in red versus green. Prefer blue/orange, vary lightness as well as hue, and add a second channel so the chart survives greyscale.