Now, have you ever thought about how the math behind this color gradient actually works?
If you didn’t have ggplot2, matplotlib, d3.js or any other tool doing this for you, how would you build that gradient yourself?
Turns out it’s way harder than you’d think.
Naive approach #1: RGB
Every color on your screen is defined by three channels: Red, Green, and Blue. Each ranges from 0 to 255.
For instance: - Pure red is rgb(255, 0, 0) - Pure green is rgb(0, 255, 0) - White is rgb(255, 255, 255)
So if you want to transition between two colors, the most intuitive approach is to linearly interpolate each channel independently. Take the start value, take the end value, and compute evenly spaced steps in between. Simple math!
And for some color pairs, it works fine! White to blue, for example, gives a clean gradient.
But try going from red to green.
You’d expect something vibrant: maybe passing through yellow or orange. Instead, the midpoint lands at rgb(128, 128, 0): a muddy, brownish yellow that's totally unpleasant.
This actually happens with any pair of complementary colors: blue to yellow, cyan to red… The key takeaway here is that the math behind the RGB space doesn’t match how humans perceive color at all.
RGB is an engineering format designed for screens, not for human eyes. Equal numeric steps don’t produce equal perceived changes.
Naive approach #2: CIELAB
Ok so RGB doesn’t work.
Scientists figured this out decades ago and designed color spaces that are "perceptually uniform": meaning equal numeric distances correspond to equal perceived differences.
The most well-known is CIELAB (or Lab). Instead of Red/Green/Blue, it uses three axes: - L for lightness (0 = black, 100 = white) - A for green-to-red - B for blue-to-yellow
Interpolating in CIELAB solves the muddy-middle problem described above! A red-to-green gradient now passes through bright, plausible intermediate colors instead of brown.
But the problem is not solved yet.
Try interpolating from white to blue in CIELAB. The shortest path between these two points in Lab space cuts through purple. You get an unexpected violet detour in the middle of what should be a clean white-to-blue ramp. 😳
This website allows to visualize the transition for many color spaces.
This happens because “shortest path” in a 3D color space doesn’t always align with what looks natural to us.
The key message
Color is complicated.
People write entire books about it. There are dozens of color spaces (HSL, HCL, OKLab, CIELUV…), each with different trade-offs, and none of them is perfect for every situation. I would have to write 10 blog posts on the topic to explain how to solve this issue.
So my advice: never build a color scale from intuition. Don’t pick two colors and interpolate between them, no matter how good the math feels. Instead, lean on well-established palettes and the tools that implement them. Brilliant minds have already solved this problem: benefit from their work! 🎨
See you next week! Yan
PS: my 3 dataviz online courses explain how to deal with color properly using R, Python and D3.Js. But that's long stories!