Harmonious HSL Color Interpolator

  • D3.js
  • Color theory
  • Tool
  • Personal project
Screenshot from the color tool

… wait what? What does that even mean?

This is a tool to generate beautiful color palettes with colors that have an equal percieved difference in brightness.

Try it out!

Ok, let’s take a few steps back …

When working with data visualisations there’s often a need to use colors to differentiate between various categories or values.

So what’s the big deal? Let’s have a quick look at the RGB color wheel:

color wheel
RGB color wheel

You may notice that the colors at 4, 8 and 12 o’clock appear brighter than the ones in between. The different colors’ wavelengths make up their brightness or percieved brightness, so some colors appear brighter or darker than others even though physically the luminosity is equal.

Here’s a formula to calculate the apparent brightness: (Source: NBD Tech)

Brightness = sqrt( .241 R^2 + .691 G^2 + .068 B^2 )

@[customImage]({“id”:“mike-bostock_wrs2k6.webp”,“alt”:“visual comparison between the three different rainbows: (HSL, HCL, Cubehelix)”})

When I learned that Mike Bostock had included the Cubehelix rainbow into his d3.js library, that’s when I decided that I wanted to create a tool for generating smooth color scales between two colors.

So in this tool, all you have to do is define your colors on either side of your scale and how many colors you’d want in between. The color scale gets conveniently printed out with their respective hex values for you to use in whatever application you need them.


Let’s first discuss the differences between d3.interpolateRgb() and d3.interpolateCubehelixLong(). Let’s say we want a scale between red and green, and find the color exactly in the middle.

First we need to define our scales:

const scale_cubehelix = d3.interpolateCubehelixLong('red', 'green')
const scale_rgb = d3.interpolateRgb('red', 'green')

Then we print some values:

console.log(scale_cubehelix(0)) // red
console.log(scale_cubehelix(0.5)) // rgb(41, 64, 235)
console.log(scale_cubehelix(1)) // green

console.log(scale_rgb(0)) // red
console.log(scale_rgb(0.5)) // rgb(128, 64, 0)
console.log(scale_rgb(1)) // green

As you can see, the d3.interpolateRgb() method has a much more ignorant way of determining the middle colour (128, 64, 0 is exactly in between 255, 0, 0 and 0, 128, 0), whilst d3.interpolateCubehelixLong() produces seemingly more random values.

Here’s a comparison between the interpolateRgb() on the left and the interpolateCubehelixLong() on the right:

And if we convert the colors to greyscale it’s apparent that the brightness is a lot smoother on the right side.