The Issue

You’re trying to program some cool LED effect but keep getting these weird not-quite-right colors. Maybe you’re trying to mix orange (say 100% red, 50% green) but the LEDs show yellow instead. What gives?

An LED driver IC — the “smarts” inside NeoPixels and other addressible LEDs — use pulse-width modulation, switching the LED on and off very quickly (about 400 times per second in the case of NeoPixels), much faster than our eyes can perceive; we just see a uniform brightness. The “on” vs. “off” time determines the intensity.

When you program in a “halfway” level (like 127 out of the maximum 255), you are indeed getting something very close to a proper 50% duty cycle. The LEDs are doing the correct thing.

Why yellow then, instead of orange? It’s nothing to do with the LEDs or your code, it’s how our eyes work…

Eyes evolved to find food by daylight and evade predators by starlight. That’s a huge dynamic range. A sort of non-linearity is built in so details can be seen at both extremes. It’s extraordinarily sensitive at the low end…we perceive changes there as more pronounced than objective measurement (or LED duty cycle numbers) would suggest:

Going linearly by numbers, there’s huge discontinuities at the left end of this gray ramp, while the right squares are nearly indistinguishable:

The trick then is to apply an inverse function — gamma correction — to compensate for this non-linearity in our perception:

Now each step appears more even:

To get something that appears 50% bright, we request a much dimmer value from the LED…instead of 127, it might be only 36 or so. The two extremes, 0 and 255, remain unchanged.

Your monitor, computer operating system and applications typically already have this correction built in. So when you pick orange in Photoshop, the R/G/B values shown are 255/127/0, as you’d intuitively expect. We can do something similar for LEDs…

Last updated on 2015-05-04 at 04.27.56 PM Published on 2014-08-29 at 04.57.57 PM