Rainbow colormap in javascript

Simple one one this time! How to make an RGB rainbow colormap in javascript? Example:

This is useful, for example, when plotting charts with something like chart.js.

This is the code for it. The first part is converting hsl colors to rgb ones, which you might skip depending on which browsers you want to support.

The second part generates a rainbow by simply rotating the hue of a hsl color. The other parameters are optional: you can make the colormap cycle several times, and you can control other hue properties (saturation and lightness) to get a different rainbow.

/**
 * CREDIT: http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
 * 
 * Converts an HSL color value to RGB. Conversion formula
 * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
 * Assumes h, s, and l are contained in the set [0, 1] and
 * returns r, g, and b in the set [0, 255].
 *
 * @param   Number  h       The hue
 * @param   Number  s       The saturation
 * @param   Number  l       The lightness
 * @return  Array           The RGB representation
 */
function hslToRgb(h, s, l) {
    var r, g, b;

    if (s == 0) {
        r = g = b = l; // achromatic
    } else {
        function hue2rgb(p, q, t) {
            if (t < 0) t += 1;
            if (t > 1) t -= 1;
            if (t < 1 / 6) return p + (q - p) * 6 * t;
            if (t < 1 / 2) return q;
            if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
            return p;
        }

        var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
        var p = 2 * l - q;
        r = hue2rgb(p, q, h + 1 / 3);
        g = hue2rgb(p, q, h);
        b = hue2rgb(p, q, h - 1 / 3);
    }

    return [r * 255, g * 255, b * 255];
}

/*
 * Create an array containing a rainbow colormap in rgb strings.
 *
 * @param   Number  valcnt    How many items are in the colormap
 * @param   Number  cyclelen  How long the cycle is before repeating
 * @param   Number  s         The saturation (deault 100)
 * @param   Number  l         The lightness (default 50)
 * @return  Array             The RGB representation
 *
 * The resolution is 360 different colors (probably hard to see);
 * you can make more by repeating the process at different `s` and `l`.
 */
function cmap_rainbow(valcnt, cyclelen = null, s = 100, l = 50, stepsize=1) {
    if (cyclelen == null)
        cyclelen = valcnt;
    var cm = new Array(valcnt);
    /* Deliberately exclude the endpoint because it's cyclic. */
	if (cyclelen <= 2)
		var step = 360;
	else
    	var step = 360. / (cyclelen - 1);
    for (var i = 0; i < valcnt * stepsize; i += stepsize) {
        let h = Math.round((i % cyclelen) * step);
        // Use this if you want hsl values:
        //cm[i] = 'hsl(' + h + ',' + s + '%,' + l + '%)';
        // Use this if you want rgb values:
        let rgb = hslToRgb(h / 360., s / 100., l / 100.);
        cm[i] = 'rgb(' + Math.round(rgb[0]) + ',' +
            Math.round(rgb[1]) + ',' + Math.round(rgb[2]) + ')';
    }
    return cm;
}

And you can use this extra to preview the colors:

/*
 * Shows a colormap preview.
 */
function show_cm(cm) {
    var container = $('<div id=colors1></div>').css({
        display: 'flex',
        justifyContent: 'space-between',
		marginBottom: '0.3em'
    });
    cm.forEach(function(color) {
        $('<div title="' + color + '"> </div>').css({
            display: 'flex',
            flexGrow: 1,
            width: '100%',
            height: '2em',
            backgroundColor: color
        }).appendTo(container);
    });
    return container;
}

var cm = cmap_rainbow(35, cyclelen = 15, s = 100, l = 75);
show_cm(cm).appendTo(document.body);

For example, A simply rainbow, just 25 default colors:

A short one, for if you don't have 25 lines in your chart:

A cyclic one:

The rainbow from before but with colors shifted to lower lightness (75 instead of 50).

EDIT: I added the ability to change stepsizes. That way you can have non-similar colors next to eachother, but still have a really long cycle of slightly different colors.

A cycle of 17 colors, with steps of 3. Note that the number of colors should not be divisible by the step size if you want all colors to appear, so I used primes:

Two cycles of 107 colors, with the first 17 shown. Stepsizes are respectively 62 and 25, tuned to have yellow among the first colors. (Yellow is pretty narrow in hue-space, and is close to the start at 60 deg):

Overall, to really get a perfect colormap, using rotations in hue is not enough. Some colors have too many slight variations while others are 'narrow'. Additionally, human eyes perceive some colors as lighter and others as darker than the numbers for monitor representation would suggest. Still, the method works fairly okay, and it is really simple to play with.

You can try it in this jsfiddle.

Comments

Mark

Now finally with color-coded source!

Mark

The post has been expanded to include non-rainbow colormaps!

You need to be logged in to comment.