Before we can start making patterns with our shiny new NeoPixel prototyping board, we must first understand how to structure color information in a format the Arduino can interpret.

This starts with an understanding of basic color models.  If you’re an artist or designer this should be second nature, but for most people the last time they heard about primary colors was in grade school art class.  You may have been taught that the primary colors are blue, red, and yellow.  These three colors are approximations for cyan, magenta, and yellow (still yellow!) or CMY, the subtractive primary colors.  Mixing these colors in varying amounts can create any other color, and mixing them all in equal amounts will make black.  These are used for anything involving ink or pigment, including your home inkjet printer.

When working with light we are adding lightness rather than taking it away, so we use the additive model where the primary colors are red, green, and blue (RGB). As with the subtractive model, mixing these colors in varying amounts allows you to make any other color, though mixing them in equal parts will make white instead of black.  The most common example of RGB mixing is an LCD screen, where each individual pixel is made up of distinct red, green, and blue elements. The featured image for this post is a close-up photo of an LCD TV, whose pixels are larger and easier to photograph than computer or cell phone displays.

The most common way to represent the additive color spectrum digitally is with three bytes, one for each primary color.  A byte is composed of 8 binary bits, each able to represent either a 0 or a 1.  Two states per bit means that we have 28 values per byte, or 0-255.  This is called 24 bit color depth (8 bits per channel * 3 channels, red green and blue).  With just three bytes we can represent 255 * 255 * 255 = 16 million colors.

Keeping track of three separate numbers for each color can be cumbersome, so we can simplify this by representing the entire color as one hexadecimal number.  The decimal number system is base 10 (0-9), binary is base 2 (0-1), and hexadecimal is base 16 (0-9, and then A-F).  This is useful because a single hexadecimal digit can represent 4 bits, and two digits can represent a single byte.  This means our three separate numbers/bytes, a total of 24 bits, can be represented by just six hexadecimal digits.

Let’s list out some colors as an example.  We’ll start with the extremes – red, green, blue, white, and black.  Red, green, and blue are represented using the maximum values of their respective channels.  White is created using the maximum values of all three channels, and black is the minimum value of all three.  Here they are with an HTML color swatch, the RGB notation, and the hexidecimal notation (prefixed by #).

             R:    G:    B:
 White       255 | 255 | 255  #ffffff
 Red         255 | 0   | 0    #ff0000
 Green       0   | 255 | 0    #00ff00
 Blue        0   | 0   | 255  #0000ff
 Black       0   | 0   | 0    #000000

By combining RGB channels we can also make colors that are mixtures of the various components. For example, the subtractive primary colors:

 Cyan        0   | 255 | 255  #00ffff
 Magenta     255 | 0   | 255  #ff00ff
 Yellow      255 | 255 | 0    #ffff00

Or any other color!

 Pink        255 | 182 | 193  #ffb6c1
 Dark Green  47  | 79  | 79   #2f4f4f
 Goldenrod   218 | 165 | 32   #daa520
 Indigo      75  | 0   | 130  #4b0082

One of the key advantages of the RGB model for computers is that it allows you to send color information directly to a display or an RGB LED without doing any transformations on the data.  It’s also an efficient system for storing color information, requiring just three bytes per pixel.

Despite its advantages the RGB color model is not particularly intuitive.  It can sometimes be beneficial to work in a model called HSV, short for Hue, Saturation, and Value (i.e. “brightness”).  In this model, hue is represented by angle around an axis (0-360°), while the other two values are percentages from 0-100%.

Here are the same RGB color swatches from above, though this time the values have been converted to HSV.

            Hue:  Sat:  Val:
 White       x    | 0%   | 100%  #ffffff
 Red         0°   | 100% | 100%  #ff0000
 Green       120° | 100% | 100%  #00ff00
 Blue        240° | 100% | 100%  #0000ff
 Black       x    | x    | 0%    #000000
 Cyan        180° | 100% | 100%  #00ffff
 Magenta     300° | 100% | 100%  #ff00ff
 Yellow      60°  | 100% | 100%  #ffff00
 Pink        351° | 29%  | 100%  #ffb6c1
 Dark Green  180° | 41%  | 31%   #2f4f4f
 Goldenrod   43°  | 85%  | 85%   #daa520
 Indigo      275° | 100% | 51%   #4b0082

Notice that white can exist at any hue so long as saturation is 0% and value is 100% (i.e. no color and full brightness), and black can exist at any hue or saturation as long as value is 0% (i.e. any color, though no brightness).  You might also notice that the hex values didn’t change – that’s because hex values always refer to RGB, and not HSV.  HSV can easily be converted to RGB, which makes it quite useful when designing color patterns.

I hope this has provided a good overview of basic digital color representations.  The above information should be more than enough for most projects involving RGB LEDs, though if you want to go further down the rabbit hole of digital color I would recommend reading up on things like gamut and color space.

Categories: Articles

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Would you like to know more?