Since this topic has come up a few times in conversation, recently, I assume that people (especially software developers) need better information.

As a quick note before I get started, a small amount of this content was grabbed from my Quora answer on What are ways to become a web developer/designer for someone working full-time in a different field?, which I originally answered on Saturday, May 3rd, 2014. I don’t use much, since that was an unrelated topic, and it has all been adapted, but if you want to dig into that career end of things, you might want to give that a quick read, too.

Color at Nasir al Molk

I somehow end up involved in a lot of projects where a design needs a new color, and it seems like a lot of people—developers, especially, but not exclusively—are terrified to work with color. But, here’s the thing: It’s possible to fake your way through most decisions like this with math.

The obstacle, though, is that the industry mostly operates on the RGB color model, which is terrible for showing relationships. It works well from a display standpoint, because most humans physically see the world through sensors detecting red, green, and blue light. However, there are at least two confounding factors.

First, our brain obviously reinterprets the physical input in a way that doesn’t match reality. An important example of this is that visible colors are transmitted as photons with wavelengths from about 380 (violet) to 740 (red) nanometers. However, despite red and violet being as far apart in the spectrum as possible, we see those two colors to be as similar as yellow (580 nm) is to green (530 nm).

The other big problem is that—surely related to the brain lying to you—as you’ve surely seen if you’ve tried to pick a color off a single on-screen palette, is that RGB color spaces feel strangely asymmetrical, with reds and purples seeming to dominate the spectrum.

Something similar goes for the CMY color model, commonly used in printing, with the most recognizable difference being that two of the three component colors (cyan and magenta) aren’t generally a part of the natural world.

two colors

All that is to say that, if I present a pair of colors that we agree look nice together—let’s say #e27d60 and #085dcb as shown to the right, for the sake of the example—and then ask for a third color that might work, finding that third color in the RGB space is a matter of trial and error or “taste.” Give it a shot. I’ll wait. And no, “gray” isn’t a legitimate answer, since that has almost certainly already been taken. Nice try, though…

OK, so what can we do instead?

Hue Can Do It!

The biggest change that we need to make is to describe colors based on how they look, rather than how they can be broken down into components. The relevant broad terms, there, are hue, colorfulness, and lightness. Lightness talks about how closely the color resembles black or white. Colorfulness deals with how closely the color resembles a gray of some lightness value. And hue involves how well the color can be described as a common color, such as red, blue, or yellow.

If every color in an image is the same—within some tolerance, of course—hue, that’s a monochrome image. If we change the colorfulness and lightness together, we’re creating a shade (mixing the color with black) or tint (mixing the color with white), because those change the lightness while reducing the colorfulness. If we change the colorfulness without the lightness, we’re adding or removing a neutral gray. I can’t find a term for changing the lightness without changing the colorfulness, but that’s what you generally see in a “monochrome color scheme.”

The hue, as mentioned, is a number that gives a sense of how we would describe a color.

Hue Color
0° Red
30° Orange
60° Yellow
120° Green
180° Cyan
240° Blue
270° Violet
300° Magenta
360° Red, Again

That’s not quite how we imagine a rainbow—cyan and magenta seem to take up an oddly large amount of space for colors we don’t ordinarily see—but it’s pretty close and red does meet at both ends of the spectrum.

Let’s look at our colors above, again, this time in terms of hue, saturation (the computer-y term for colorfulness), and lightness.

RGB Hue Saturation Lightness
#e27d60 13.4° 69.1% 63.1%
#085dcb 213.8° 92.4% 41.4%

I hope that’s a bit more enlightening. The first color is a red-orange or a rust that’s somewhat dull. The second is a bold but medium-lightness cyanish-blue. Right off the top, I think it’s obvious that this is a useful notation, just because it gives a better intuition for what the color looks like, if you can remember most of that table above. And, y’know, you can just look it up quickly; no shame in that.

Have you ever heard a designer complain that the boss or client “would like the blue to be bluer”? They might want the hue closer to 240°, but more likely is that they want to increase the saturation. They want the color to be “softer”? They probably mean bringing the lightness closer to its neighboring colors.

Anyway, let’s pick our third color! I’m going to assume that the orange color is being used as a background, since otherwise we’re guessing. The most naive color we can pick takes the difference between each of the three values and swings them around the other side of our background. So…

Color Hue Saturation Lightness
Orange 13.4° 69.1% 63.1%
Blue 213.8° 92.4% 41.4%
Difference 200.4° 23.3% -21.7%
Orange — Δ 173.0° 45.8% 84.8

three colors

That gives us our new color (#c6eae6, if you want to play along in RGB), calculated to look as different from the orange as the blue is, sounds like—based on the middle-of-the-road saturation and high lightness—a washed-out cyan. Yes, the blue and cyan sound close, but this isn’t necessarily bad. In fact, refer to the example to the right; the cyan is definitely not as bold a part of the mix as the other two, but it’s distinctive and doesn’t clash with either color. Success!

And, of course, keep in mind that the monochrome discussion applies here, too: Adjust the lightness and saturation of the colors to produce supplemental colors. I’m just mechanically coming up with the non-hue values because I’m more confident in them, but a bolder or darker cyan is almost certainly a decent choice, too.

There’s also a good chance we can take intermediate colors, too. Let’s say 93.2° hue, 57.45% saturation, 73.95% lightness? That’s a faded yellow-green (#b9e396), which you can draw on the sample on your own time.

This approach is referred to as choosing “adjacent colors,” even though the colors are pretty far apart. It’s what I suggested earlier, that you have one “main” color and then one color to either side, the same angle away. You’ll occasionally hear the term “triadic” used in cases like this, where the main color is far—more than 90°—from its neighbors.

Oh, and there’s one other important color to consider: The complementary color. Not “compliment,” like free things that say nice things about you—look up the joke about “complimentary peanuts” before pointing out the grammar mistake—but the opposite of a thing that makes it whole. Complementary colors are those whose hues are 180° apart from each other, so the complement to our rusty orange is 193.4° (#60c5e2), which is sort of a sky blue, and you’ll notice that it’s right between the bold blue and the cyan.

Generally speaking, complementary colors are too bold to use as more than an occasional accent or one color has a saturation or lightness that makes it feel muted. But that accent generally works inside any color scheme.

Notice that triadic colors are adjacent to the complementary color.

In addition to adjacent/triadic schemes, you’ll also generally see reference to “tetradic” color choices. This is sort of a mix of adjacent and triadic, where the main color and its complement each get an adjacent “partner.” Five colors generally get a spread that looks like triadic, but taking adjacent colors of the non-primary. Likewise, six colors split the primary color to two adjacent colors. If you need more than six colors with all of saturation and lightness values available, your vision might be too complicated.

Anyway, if the rusty orange is our main color, know the sky blue is complementary, and we have the bold blue as our secondary color, then the fourth color should be the bold blue’s complement, 33.8° hue, 92.4% saturation, and 41.4% lightness (#cb7608), which is a brownish-orange. That doesn’t look so great, but it’s more because the orange colors are too similar.

Tetradic example

Let’s take a more random example, and when I say random, I mean “let http://randomcolour.com pick the main color.” In this case, it gave me #8d1450, or 330.2° hue, 85.8% saturation, and 55.3% lightness, a dark reddish-magenta. We know the complementary hue is 330.2° - 180° = 150.2°, somewhere between green and cyan. Now we need a random angle, something less than 90° but larger than the 20° that felt too close to eyeball. Generating a random number in that range gives me 37°. So, 330.2 + 37 = 367.2° (or 7.2°, since this is a circle) and 150.2 + 37 = 187.2°.

(If you look at the table of hues above, you might notice that an angle of 30° or 60° is probably going to give recognizable results.

Is this a color scheme that’s going to win awards? Probably not. The red looks pretty muddy against the purple, but the other combinations work well enough to function (especially with black, white, and grays added to the mix), it’s not jarring (just invisible), and the red and cyan could almost certainly stand to be lightened instead of leaving all the saturation and lightness values the same. So, I’d call that a success.

If you have a keen eye (or downloaded the stylesheet to check), you should notice that my website uses a triadic color scheme, the background color being yellow, headers being red, and text in blue, with variants being steps up and down in lightness.

Does That Color Have a Name?

So, here’s a related question with…yes, obviously with the same answer: Given an arbitrary color, can we find the closest color with an official name?

As it turns out, I created a tool to solve this in Go, back in 2016, mostly to experiment with the language. The hardest part of solving the problem was getting the list of named colors, which I just stripped out of Wikipedia.

The program does what you would probably expect, at this point: It converts the RGB values to HSL (or HSV, which is mostly interchangeable) values and then calculates the distance between the input and all the colors to find the nearest.

I recommend trying the same thing—you can adapt the conversion algorithm/formula from my code or this answer on Stack Overflow—and comparing the results to the nearest color in RGB space (that is, the color with the nearest red, green, and blue intensities) to see which finds a better match. It might be interesting to try to find a color that matches better using the RGB distance.

Contrast

An important aspect of what makes a good color scheme, beyond just the colors looking acceptable next to each other. The World-Wide Web Consortium suggests calculating contrast as (approximately) the ratio of luminances, which…OK, this is a little intricate.

  • Take each of the three color components (R, G, and B) as fractions between zero and one (i.e., the integer á 255);
  • For values less than or equal to 0.03928, divide by 12.92;
  • For all other values…
    • Add 0.055,
    • Divide by 1.055, and
    • Raise to the 2.4 power;
  • Sum as 0.2126 ✕ R + 0.7152 ✕ G + 0.0722 ✕ B.

This Stack Overflow answer converts that mess to fairly straightforward JavaScript.

function luminance(r, g, b) {
    var a = [r, g, b].map(function (v) {
        v /= 255;
        return v <= 0.03928
            ? v / 12.92
            : Math.pow( (v + 0.055) / 1.055, 2.4 );
    });
    return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
}
function contrast(rgb1, rgb2) {
    var lum1 = luminance(rgb1[0], rgb1[1], rgb1[2]);
    var lum2 = luminance(rgb2[0], rgb2[1], rgb2[2]);
    var brightest = Math.max(lum1, lum2);
    var darkest = Math.min(lum1, lum2);
    return (brightest + 0.05)
         / (darkest + 0.05);
}

If the results are better than 50% for each pair of colors in use, then the colors are probably readable against each other. Personally, I find it nice that this is something that can quickly work for user-defined color schemes—providing feedback without much overhead—in addition to making it easier to know if a color scheme is usable.

Color Blindness

Closely related to ensuring contrast is validating color schemes for people with different kinds of color blindness. For example, I was once on a project where a client was using their corporate branding colors to highlight the most recent selections in a list. Our team’s problem with fixing problems was that several people were color-blind (they still are, individually, but the team no longer exists…) and so couldn’t see much difference between the colors they gave us.

Unfortunately, converting colors to simulate color blindness is non-trivial for most programming, because it requires applying matrices to the color space. Each color “channel”—one of the technical terms for one of the R, G, or B values—is a weighted mix of magnitudes of the three original channels.

We can do something like this, assuming that the “matrix” is a linear array with all the rows combined, for simpler notation.

function crop(n) {
  return Math.floor(10 * (n<0 ? 0 : (n<255 ? n : 255))) / 10;
}
function AdjustChannelByMatrix(c, m, ch) {
  var offsets = {
    r: 0,
    g: 5,
    b: 10,
    a: 15
  };
  var i0 = 0 + offsets[ch];
  var i1 = 1 + offsets[ch];
  var i2 = 2 + offsets[ch];
  var i3 = 3 + offsets[ch];
  var i4 = 4 + offsets[ch];
  return crop(
    c.R * m[i0] +
    c.G * m[i1] +
    c.B * m[i2] +
    c.A * m[i3] +
    m[4]
  );
}
function AdjustColorByMatrix(c, m) {
    return({
      R: AdjustChannelByMatrix(c, m, 'r'),
      G: AdjustChannelByMatrix(c, m, 'g'),
      B: AdjustChannelByMatrix(c, m, 'b'),
      A: AdjustChannelByMatrix(c, m, 'a')
    });
};

We’re missing the matrices, of course. The best information I can find is based on the Color Blindness Emulation project, made available under the terms of the CC0 1.0 Universal Public Domain Dedication.

{
  'Normal': [
    1,0,0,0,0,
    0,1,0,0,0,
    0,0,1,0,0,
    0,0,0,1,0,
    0,0,0,0,1
    ],
  'Protanopia': [
    0.567,0.433,0,0,0,
    0.558,0.442,0,0,0,
    0,0.242,0.758,0,0,
    0,0,0,1,0,
    0,0,0,0,1
  ],
  'Protanomaly': [
    0.817,0.183,0,0,0,
    0.333,0.667,0,0,0,
    0,0.125,0.875,0,0,
    0,0,0,1,0,
    0,0,0,0,1
  ],
  'Deuteranopia': [
    0.625,0.375,0,0,0,
    0.7,0.3,0,0,0,
    0,0.3,0.7,0,0,
    0,0,0,1,0,
    0,0,0,0,1
  ],
  'Deuteranomaly': [
    0.8,0.2,0,0,0,
    0.258,0.742,0,0,0,
    0,0.142,0.858,0,0,
    0,0,0,1,0,
    0,0,0,0,1
  ],
  'Tritanopia': [
    0.95,0.05,0,0,0,
    0,0.433,0.567,0,0,
    0,0.475,0.525,0,0,
    0,0,0,1,0,
    0,0,0,0,1
  ],
  'Tritanomaly': [
    0.967,0.033,0,0,0,
    0,0.733,0.267,0,0,
    0,0.183,0.817,0,0,
    0,0,0,1,0,
    0,0,0,0,1
  ],
  'Achromatopsia': [
    0.299,0.587,0.114,0,0,
    0.299,0.587,0.114,0,0,
    0.299,0.587,0.114,0,0,
    0,0,0,1,0,
    0,0,0,0,1
  ],
  'Achromatomaly': [
    0.618,0.320,0.062,0,0,
    0.163,0.775,0.062,0,0,
    0.163,0.320,0.516,0,0,
    0,0,0,1,0,
    0,0,0,0,0]
}

So, for every color scheme we generate, we actually have nine schemes to validate for usable contrast.

Putting It Together

That might sound daunting, but bear in mind that most of the joy of software is that we only need to work out (or find, as the case may be) the tedious math like this once, and we can apply that solution in bulk.

So, given what we know, we should be able to accept a primary color, angle, and geometry (adjacent, triadic, etc.), generate a color scheme, and validate—possibly adjust, if you’re willing to have code iterate on saturation and lightness—the contrast of the resulting color scheme and how it will look to each of the eight kinds of people who are color-blind.

It’s still important to visually inspect the results, of course, but I’d call that a pretty long way to go without needing to think much about preference or taste.

However…

All of this should be taken as thinking about new work. If you’re working on something for a company that exists, you can usually short-cut most of this work through the simple expedient of asking someone for the company’s branding guidelines. I mean that literally. If the company has graphic artists, their manager is probably the keeper of the document. Otherwise, Human Resources often knows where to find it, since someone there is generally responsible for job fair banners and the like.

If you don’t have inside contact with the organization or are too shy to ask your colleagues for help, you can also try searching the web for the company name and “branding guidelines.” Companies involved in a lot of partnerships or have significant media attention generally keep their expected color palette, fonts, and assorted branding assets public.

If you have a branding guide, avoid deviating from it, if you can. Adding a small amount of an accent color is usually acceptable, but generally speaking, the further you are from the text, the more likely a manager is going to reject the design.

If you need to quickly get up to speed on broader design than just color, I can’t “highly recommend” it, because it’s a strange (and probably illegal) combination of licenses like the GPL and CC-BY-NC, but Digital Foundations discusses free software art tools, symmetry and gestalt, the grid-based Bauhaus school of design, some basic color theory (mostly definitions), image types, and so forth. Like I said, I’m not pleased with the strange licensing, but it’s the broadest text on the topic that is designed for beginners.

More broadly, most Floss Manuals books are licensed under the GPL with the intent of distributing them with the applications they talk about. They seem to have fallen a bit out of date on the “manual” aspect, but they’re still decent starting points on a lot of topics.

Not at all Free Culture (and not encouraging free software), unfortunately, but I haven’t found anything nearly as useful as Butterick’s Practical Typography on the topic of…well, typography. You might also want to search for articles on “font pairings” for articles with some useful advice. Throwing in “open source” wouldn’t be the worst idea, if you’re trying to keep to work that can be easily shared.

Since you can’t generally deal with design while ignoring user experience (unless you’re just creating a static image, which seems unlikely), again, it’s unfortunately not Free Culture, but it’s hard to find better advice—advice as backed by data, even—than Nielsen Norman Group’s biweekly articles. I have yet to find their videos useful, but they’ve been posting fairly solid advice regularly since the early days of the web and frequently revisit their old findings to see if they still hold true. For example, they sounded the alarm on the ineffective nature of web-based advertising in the summer of 1997!

I also like 24 Ways as a source of interesting tips and advice that mostly revolve around presentation. You’ll notice that my website (including this blog) takes more than a couple of cues from one particular article. Likewise, CSS Tricks has an enormous backlog of advice, plus Snippets with quick tricks.

If all else fails, you can also just work from Google’s Material Design guidelines. It may not be interesting, but it’s a complete, documented system that’s familiar enough that most people won’t find a problem using your design. Alternatively, flat design might as well be “design for programmers,” with no borders, shadows, gradients, textures, animation and usually a limited color palette on a neutral background; Material Design branched out of a flat approach, with a small handful of depth levels and shadows.

Alternatively, if you’re working on desktop software, you can’t go wrong just using your target platform’s design language. On Windows, that’s either Aero or Fluent. On macOS, it’s Aqua. Android uses Material Design. Unfortunately, I can’t find an explicit design language for any Linux distributions, most generally just having a set of guidelines that expand whenever something new is needed. But the point is that, if you use a language that your users/readers have already learned, you’ll have a much easier time and many decisions will have already been made for you.

Again, though, a lot of the above information isn’t entirely under a Free Culture license. If you’ve seen better material on any of these counts that has been made available under a usable license (if it allows any use, optionally under conditions of attributing the author and/or sharing any derivatives, the license should qualify), please leave a comment, and I’ll update this post crediting you. Heck, I’ll even take mediocre material! The best I can find is this clunker on Wikibooks that…well, it needs a lot of love to make it into something I would feel comfortable recommending it.


Credits: The header image is Eine Innenansicht der Nasir-ol-Molk-Moschee in Schiras, Iran by Ayyoubsabawiki and is released under the Creative Commons Attribution-Share Alike 4.0 International license.