davidlyness [dot] com

Things I find interesting.

JavaScript hex colour generator


Like all other data, computers store colour data as a string of digits. Colour values are stored as strings of digits, with the hash symbol (#) at the front indicating we are working in hexadecimal. For example, #000000 is black, #ffffff is white, and #c0c0c0 is the grey of the codeblock below - check out the W3Schools page on CSS Colors for more details. On a recent project I came across the problem of generating a random string of hex digits to be interpreted as a colour.

Our first thought is that we're probably going to want to use the in-built JavaScript pseudo random number generator. While not cryptographically strong, it's good enough for this case. The general syntax is:

Math.random()


This will return a random decimal value between 0 and 1 exclusive, for example 0.40566325443796813. For reasons we'll see later, we would like to generate an integer within a range rather than a decimal. This is accomplished by multiplying and using the Math.Floor function. For example, if we wanted a random number between 0 and 10:

Math.floor(Math.random()*11)


Note that since the raw generated numbers are contained in (0,1) rather than [0,1], Math.Floor will never return 10 when the result is multiplied by 10. Therefore 11 must be used instead.

All hex colour codes are between #000000 and #ffffff. Remember that these are hexadecimal numbers, so they correspond to the range from 0 to 16777215. Therefore, we need to add one to reach the full range of colours, making the multiplier 16777216. We can also use JavaScript's ToString method to convert our decimal number back to hexadecimal (using a radix of 16), and put a '#' character on the front to comply with formatting rules. Our code so far looks as follows:

'#' + Math.floor(Math.random()*16777216).toString(16)


Generating 10 numbers, we get the following set of promising results:

#218ac5
#9e0a5c
#d733b
#5f4ea
#a9d39e
#5f9d7d
#26156f
#37f078
#e4948d
#9e754b


There is still one remaining problem, which can be seen in the 3rd and 4th entries of the above list. They have only 5 hex digits whereas the format calls for 6. The problem here is that these numbers should have leading zeros if they are less than 6 digits long. After scratching my head for a bit I came up with a neat trick using the substr() method with a negative index (with the caveat that it doesn't work in older versions of IE):

'#' + ('00000' + Math.floor(Math.random()*16777216).toString(16)).substr(-6)


Now we get the following set of results:

#00a24b
#686cc1
#7eb56f
#2aba6b
#13ab28
#b72d45
#eba0b2
#9dca3b
#f0a4ed
#63b0c1


Notice that the 1st entry has been padded with two zeros, so all generated numbers are 6 digits long. And we're done!

2 comments
reply
Joshua Leahy
Question,

Can you guarantee that these colours are evenly distributed amongst the intersection of the possible colours we can see and the possible colours a computer can display? Ie. if you want to generate a random set of colours is this enough, or do you need to be randomly generating hue saturation and brightness levels and then transforming them to the rgb domain?
4:46pm, 8th October 2011
reply
David Lynesspost author
The colours will obviously be randomly (~evenly) distributed amongst the colours a browser can render, but this differs from what the human eye can discern. The human eye can differentiate between shades of certain colours much more easily than others; for example, our eyes are most sensitive in the green / yellow area of the spectrum. So while we're generating a truly random set of different colours, we'll get a human bias unless we try to generate them in a more "human" way as you mentioned.

It would be an interesting project to determine how the bias is distributed so that we have a better idea what the intersection looks like - the xkcd author did some work in this area last year.
5:41pm, 8th October 2011
Submit a comment