I was trying to generate some random numbers for the Markers on the map:

The colours need to be diverse for each travel pairs for easy distinction, and cannot be too light.
I want to use an array of three numbers so that I’ll have the flexibility to use rgba() for background opacity alternation:

1
2
3
4
5
6
7
8
9
10
11
12
const rgb = props.rgb;
const rgbString = `${rgb[0]},${rgb[1]},${rgb[2]}`;
const toStyle = {
border: `2px solid rgb(${rgbString})`,
color: `rgb(${rgbString})`,
backgroundColor: `rgba(256, 256, 256, 0.8)`,
};
const fromStyle = {
border: `2px solid white`,
color: `white`,
backgroundColor: `rgba(${rgbString}, 0.8)`,
};

Random RGB numbers

The naive approach is just to generate random numbers for rgb:

1
2
3
4
function random_rgb() {
var o = Math.round, r = Math.random, s = 255;
return [o(r()*s), o(r()*s), o(r()*s)];
}

This gets me totally random numbers that could be very light or dark.

Using HSV

Hue, Saturation, and Value (HSV) is a color model that is often used in place of the RGB color model in graphics and paint programs. In using this color model, a color is specified then white or black is added to easily make color adjustments. HSV may also be called HSB (short for hue, saturation and brightness).

  • S: 0(not colorful) -> 1(fully colourful)
  • V: 0(black) -> 1(white)
    So I want the saturation and value stable, but hue random.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
function generateRandomRGB() {
let h = Math.random();
return HSVtoRGB(h, 0.95, 0.7);
}

function HSVtoRGB(h, s, v) {
var r, g, b, i, f, p, q, t;
if (arguments.length === 1) {
(s = h.s), (v = h.v), (h = h.h);
}
i = Math.floor(h * 6);
f = h * 6 - i;
p = v * (1 - s);
q = v * (1 - f * s);
t = v * (1 - (1 - f) * s);
switch (i % 6) {
case 0:
(r = v), (g = t), (b = p);
break;
case 1:
(r = q), (g = v), (b = p);
break;
case 2:
(r = p), (g = v), (b = t);
break;
case 3:
(r = p), (g = q), (b = v);
break;
case 4:
(r = t), (g = p), (b = v);
break;
case 5:
(r = v), (g = p), (b = q);
break;
}
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}

Improve the distribution using golden ratio

This article recommended using golden ratio to achieve more evenly distributed hue.
The algorithm for this is extremely simple. Just add 1/Φ and modulo 1 for each subsequent color.

1
2
3
4
5
6
7
function generateRandomRGB() {
const goldenRatioConjugate = 0.618033988749895;
let h = Math.random();
h += goldenRatioConjugate;
h %= 1; //0.5 % 1 = 1, 1.5 % 1 = 1
return HSVtoRGB(h, 0.95, 0.7);
}

Reinforcing even distribution (not random)

Here’s another guy’s code using the new CSS functions hsl() and hsla() to colour an array of elements:

1
2
3
4
5
var hue = 0;
Array.prototype.slice.call(document.querySelectorAll('.myClass')).forEach(function(mc) {
mc.style.color = 'hsla(' + hue + ', 75%, 50%, 0.5)';
hue += 222.5;
});

hsl() and hsla() take care of normalizing the hue to the [0, 360) range for you. 222.5 is approximately 360/Φ

Neat!