How To Avoid Equidistant HSV Colors

As some of you pointed out in the comments of my last post, taking equidistant colors in the HSV color space is no solution for finding a set of colors that are perceived as equidistant. This post describes what's wrong with HSV and what we can do about this. Note that since this post contains interactive elements built on the latest web technologies, you might need a modern browser to get the most out of it.

What's so wrong with HSV?

Well, the main problem is that the value component of HSV is just a measure for the physical lightness of color, but not for the perceived brightness. Thus, fully saturated yellow has the same "value" as blue. The same is true for the HSL color space. Here is a set of six colors of the same value to demonstrate this effect. The second row shows how the colors look after converting to grayscale via Photoshop.

Beside of this strong hue-dependency of brightness, there is also no linear brightness gradient within a single hue. For instance, in the following HSL color scale the brightness step between the second and the third red appears much bigger than the step between the 3th and 4th color. Even worse, this effect seems to differ across different hues, as the comparison to the blue scale shows.

After all, this should be enough reason to avoid equidistant HSV/HSL scales. But what options do we have instead?

For the lazy ones: Use ColorBrewer

So the question is, what shall we do about it? In the comments someone mentioned ColorBrewer, which is in fact a great solution for those who just want to get some colors without caring too much about the details. Here's a selection of sequential ColorBrewer scales.

One drawback of this solution is that you're limited to a fixed number of colors. For each color scale, the collection gives you variants from 3 to 9 colors. Another drawback is that sticking to a predefined set of colors means giving away some artistic freedom. Also, and most importantly, it's not half as fun to pick existing solutions, isn't it?

For the color geeks: Make friends with CIE L*a*b*

At this point, you better prepare yourself for some ultimate color geekyness. Thanks to the gentle introduction of David Dalrymple, I finally dared to enter the magic world of the CIE L*a*b* color space. To get a better understanding of this color model, I ported David's code to JavaScript and built a tiny Lab color selector. The vertical slider allows you to navigate through the space. Also you can change the x and y axis using the links next to the slider.

In general, the Lab color space has one component for Lightness and two bipolar color components a (yellow-blue) and b (green-magenta). While the lightness ranges from 0% to 100%, determining the valid ranges for a and b is somewhat tricky.

The problem is that CIE L*a*b* contains more colors than are available in RGB. For instance, the "valid" range for a depends on the lightness and the second color channel. Another problem is that it's hard to select colors by mixing four colors. To get around those issues, David Dalrymple suggests a fancy transformation of the Lab space.

CSL: Making CIE Lab more accessible

The trick is easy. You use the a and b components to compute a color angle (hue) and a radius (saturation). Because it's not exactly the same as hue in HSV, it's named c (for chroma). Basically this gives this us the CSL color space (although I'm not sure how this is named in vis science).

While the problem of non-existent colors still remains (especially if you increase saturation) this looks like something we can start to work with. For instance, if we take a look at equidistant CSL colors of the same lightness we get a much better result. Actually, if you'd convert this into grayscale again you get the exact gray value for all colors. Well, this is no big surprise, I bet Photoshop itself uses CIE L*a*b* for grayscale conversions.

CSL

Now, let's grab some colors..

This is where the fun begins, so I'm glad you made it up to here. To learn more about the difference of equidistant colors across color spaces, I visualized the linear gradient in the color selector. Feel free to experiment with them and check how the CSL color space compares to other spaces like LAB, HSV, HSL or RGB.

Also, it's interesting to directly compare the interpolated colors across different color spaces:

Nice! How do I get those colors?

To make things easy, I packed everything up in a small JavaScript library named chroma.js. It has a simple yet powerful API,check out the Github repository to learn more. Since this is still in early beta phase, l'd appreciate if you document bugs you may encounter.

// most simple interpolation in CSL space
chroma.interpolate("#383D41", "#EFEE69", 0.5, 'csl').hex(); 
// returns "#5C9A7C"
 
// also, you can instantiate CSL colors directly 
chroma.csl(60, .7, 1).hex();
// returns "#C6A860"

What do you think?

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" cssfile="">

Comments

  1. Great post and explanation!

    The interpolated colour comparison in particular is a fantastic example that I'll be pointing students to on a regular basis.

  2. Aubrey Pullman   

    Please be aware that some color to gray conversions in Photoshop use unequal channel mixing, so the values will be different.

    Thank you for introducing me to the CSL colorspace!

  3. I've been waiting for such a thing for ages, but realized that only after reading it. Thanks a lot, this is super-useful and I am looking forward to sharing it with my research group here in Konstanz. We are quite serious about color scales and we have a number of tools you might like:

    - http://infovis.uni-konstanz.de/tools/gradientcreator/
    - http://infovis.uni-konstanz.de/tools/colormap/index.html

  4. This is awesome! Thanks so much for building upon my work. :)

  1. [...] How To Avoid Equidistant HSV Colors (suggested by david d) Tweet(function() { var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true; po.src = 'https://apis.google.com/js/plusone.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s); })(); This entry was posted in Development, Devices and Applications by Katie Kuksenok. Bookmark the permalink. [...]

  2. [...] Colors in Chroma.js For my blog post “How To Avoid Equidistant HSV Colors” I created an interactive color space explorer, also known as “color selector”. [...]