The Secrets of Colour Interpolation

This post discusses about the tricky problem of colour interpolation, and explores possible solutions. Many software and engines offer read-to-use functions to interpolate colours. In Unity, for instance, Color.Lerp is available and does its job pretty nicely. Use the interactive swatch below to see how Color.Lerp works.

There’s nothing wrong in using these functions, as long as you know what the deal with colour interpolation is.

Understanding interpolation

Interpolation is a technique that allows you to “fill a gap” between two numbers. Most APIs expose linear interpolation based on three parameters: the starting point a, the ending point b and a value t between 0 and 1 which moves along the segment that connected them:

    \[c = a + \left(b-a\right)*t\]

When t=0, a is returned. When t=1, a+\left(b-a\right)=b is returned instead. The beauty of this formula is that is easy to understand, efficient to implement, and it works in any dimension. Lerping in two dimension only requires to independently lerp the X and Y components. Lerping always returns points on the line that connects a and b, regardless of the number of dimensions. A standard RGB lerp can be done as such:


If it’s true that linear interpolation works as expected in three dimensions, the same cannot say for colours. There’s a fundamental difference between the XYZ and RGB spaces: the way the human eye perceive colours. While it make sense to connect two points in a 3D space with a line, the same doesn’t always apply for points in the RGB space. Interpolating the R, G and B components independently offers no guarantee on the hue of the intermediate colours. As Stuart Denman highlights in his Improve Color Blending, the RGB space of cyan and red meet halway in grey. A new hue appears because the RGB space does not capture how Humans perceive colours very well.

The next pages of this post will explore better ways to interpolate colours.

A first attempt to compensate for this is to switch to different colour space, such as HSV (also known as HSB). It has been designed to be “artist-friendly”, grouping colours by hue and ignoring how they are created on screen.


The result, as it can be seen above, is rather disappointing. The reason is that interpolating the H component cycles through different hues. In this case we don’t have to go through green, but looping over the H space in the opposite direction.

HSVV255To implement an HSV lerping function we need to understand how these components are handled. For this example, we’ll assume all the HSV components range from 0 to 1. The following code is inspired from Improved Color Blending and relies on the ColorHSV Unity extension by C.J. Kimberlin:


For comparison, the linear lerping through HSV space is also shown together with the corrected lerping (HSV*).

Despite all the effort, the transition still doesn’t look good. The reason is that even if we have correctly learped through the Hue component, different colours have different luminosities. As explained by Gregor Aisch in How To Avoid Equidistant HSV Colors, equidistant colours in the HSV space are not perceived as really equidistant. Even HSV colours with the same brightness (V) can differ in their perceived brightness and luminosity. Many aspects are responsible for this. The R, G and B components of a colour contributes in different ways the perceived luminosity, due to the way their respective photoreceptors work. Several attempts have been made to capture the non-linear relationships between R, G and B in a colour model. One of the most successful is the LCH (also known as HCL for Hue, Chroma and Lightness). Equidistant colours in the LCH space are also perceived as equidistant. The swatches below clearly shows how the LCH space provides a more uniform distribution of the colours.


The conversion from RGB to LCH is very expensive. This is because colours have to be converted into to intermediate spaces, the XYZ and LAB. A very good library which supports all of these conversions is chroma.js.

Using colours with equidistant perceived luminosity is essential for all these applications in which colours have a precise meaning, such as diagrams and heatmaps. Providing uniform luminosity is also important for colour blind people, as discussed in Accessibility Design: Color Blindness. A starting point to design a safe colour palette is ColorBrewer.

Interpolating colours by lerping their RGB components is the most common and lazy easy approach to tackle a very complex problem. If the interpolated colours need to be visible at the same time (for instance in a chart or a diagram) chances are you might need a more advance technique. Conversion from RGB to HSV are supported by most frameoworks, but if you want to go the extra mile you should adopt the LCH colour space.


This post was strongly inspired by the many works of Gregor Aisch.

How To Avoid Equidistant HSV Colors

Related posts

External resources


Accessibility Design: Color Blindness

This tutorial will teach you how to create and use post-processing effects which simulate how colour blind players might experience your Unity game. One of my most anticipated games is The Witness; since it uses so many vibrant colours, it will be used as an example in this tutorial. This is how a player affected by red-green colour blindness (protanopia) might see it:

The image effect provided in this tutorial will help you understand which parts of your game are harder to see for color blind users.

Continue reading

Game Barcode: A Study of Colours in Games

This tutorial shows how to download videos from YouTube and to process their frames with Python; I have used this technique to create game barcode, an image created by sorting the colours in each frame of a particular video. You can see some of most intriguing here:

This tutorial is divided in four parts:

Continue reading

The incredibly challenging task of sorting colours

Let’s start with something trivial: sorting numbers. Regardless of the algorithm you’ll use, real numbers are naturally ordered. Mathematically speaking, they have a total order, in the sense that you can always decide if a number is greater than another one. There is no ambiguity in this, meaning you can actually sort them, and (excluding duplicates) this sort is unique. There are other fields which are not that lucky: colour, for instance, are very unlucky. Supposing you’re representing colours with their RGB values, there is no standard way to order triples in a line, since they are naturally not organised in a line fashion. The problem is even more complicated since colours have a meaning in the real world. How can we sort colours so that they look as continuous as possible? Which parameters affects the sorting order? Is azure closer to blue (similar hue) or to cyan (similar luminosity)? I can stop you all here and say that there is no solution to this problem. You can sort colours, but the overall result depends on what you are trying to achieve. This post will explore how colours can be sorted, and how this can lead to very different results.

Continue reading

How to find the main colours in an image

In a previous post, I explained how I grabbed all the screenshots from #ScreenshotSaturday. If that was something relatively easy to implement, ordering them by colour is slightly trickier. The problem here is that there is no standard way to find the main colours in an image. Quite the opposite, different techniques will produce very different results. Long story short: this is not really a problem for programmers, and that’s why it may be more interesting to discuss about it.

Colour theory

Let’s say that we already have our screenshot. Now, we want to find its main colours. If you are familiar with Photoshop, a starting point  is accessing the histogram which represents the distribution of colours. Peaks in the colour histograms can be associated with the main colours. Conrad Chavez wrote a very detailed post about it, if you are interested in this. The image below shows a level in 0RBITALIS which is predominantly blue and green; this can be seen directly from the colour histogram.

rgb Continue reading