Diversion: Generating a random color from JavaScript


A colleague posed a little puzzle for a fun little app he was writing in HTML: He wanted to generate a random color.

If you search around the intertubes, you can find several possible attempts at a solution, like this collection, and an interesting example that has trouble with the pigeonhole principle.

The original function to generate a random color went like this:

// Pad a string of up to two characters with a leading zero
// so the result is always exactly two characters long.
function padZero(v) {
 return (v.length == 1) ? '0' + v : v;
}

function randomColor() {
 return "#" + padZero(Math.floor(Math.random() * 256)).toString(16) +
              padZero(Math.floor(Math.random() * 256)).toString(16) +
              padZero(Math.floor(Math.random() * 256)).toString(16);
}

Can you do better? (My solution after the jump.)


That was a short jump.

My first simplification was recognizing that three random 8-bit values is the same as one random 24-bit value.

function padZeros6(v) {
 while (v.length < 6) v = "0" + v;
 return v;
}

function randomColor() {
 return "#" + 
    padZeros6(Math.floor(Math.random() * 16777216).toString(16));
}

Next, I got rid of the padZeros6 function by simply setting bit 25 to force a 7-digit result, then removing the leading 1.

function randomColor() {
 return "#" + 
    (Math.floor(Math.random() * 16777216) +
                                16777216).toString(16).substr(1);
}

Finally, I did some factoring.

function randomColor() {
 return "#" + 
    Math.floor((1 + Math.random()) * 16777216).toString(16).substr(1);
}

That last bit was a bit dodgy due to the wonders of floating point arithmetic, but hey, it's a puzzle now.

Finally, I realized that CSS supports #rgb as shorthand for #rrggbb, so if you don't mind that your color palette is reduced to 4096 colors (and in the case of my colleague's little app, that was not an issue), you can shorten it a bit more:

function randomColor() {
 return "#" + 
    Math.floor((1 + Math.random()) * 4096).toString(16).substr(1);
}
Comments (32)
  1. Adam Rosenfield says:

    Those samples in the first link belong on TheDailyWTF.  It'd be so much easier if JavaScript had a printf-style format function or operator.  In Python, it'd look something like this:

    def randomColor(): return "#%06x" % random.randint(0, 16777216)

    Or the Python 3-preferred way:

    def randomColor(): return "#{0:06x}".format(random.randint(0, 16777216))

  2. MattT says:

    You can also drop the Math.floor by noting that bitwise operators force an integer conversion. The smallest bitwise operator you can choose is OR 0. If you're looking for saving characters instead of saving operations, you can also drop the constant into a shift:

    function randomColor() {

    return "#"+((1+Math.random())*1<<24)|0).toString(16).substr(1);

    }

  3. Nice little 3-line function. But the real question is, is Math.Random() /truly/ random? My years developing high-grade crypto hardware makes me that it is not. (Nitpicking? Maybe.)

    There's some discussion of Math.Random here: stackoverflow.com/…/how-random-is-javascripts-math-random

  4. Anon says:

    Brian_EE  you develop HIGH level crypto hardware and don't know how random math.random is? scary

  5. MattT says:

    @BrianEE.

    No. It's not truly random (by Cryptographic standards or by Quantum randomness standards). It's implementation defined how it actually works under the hood, but generally it's just a PRNG seeded by the system time (although in IE the seed is a 32-bit cryptographically random seed XORed with the PID and the process start time, so you have at least 32-bits of "real" randomness in the first call to Math.random() in the process).

    Math.Random() is required to be random up to PRNG standards, that is to say that it should be loosely unpredictable, it should be evenly distributed over the range, and it should have a period substantially larger than the range.

    Math.random() is NOT required (but also not forbidden) from being cryptographically random or quantumly random, so you should not rely on it being so.

    Moral of the store: Don't use Random() for cryptographically important functions. (But you weren't doing that anyway, right?)

  6. Danny says:

    @Brian

    The link on stackoverflow you gave is about the randomness of digits scale, not the randomness of the numbers generated. As stated there by jwoolard answer 9/10 number will have the same number of digits. And to answer your question : NO. There is nothing, repeat nothing, in software that is truly random, everything is pseudo-random. If you need a true random generator you have to resort to a hardware generator to be your source (no matter if is quantifying the noise in a transistor gate or "catching" the background cosmic noise, courtesy of Big Bang)

  7. nandhp says:

    Possibly I'm missing the point, but I think the original code is actually broken.

       padZero(Math.floor(Math.random() * 256)).toString(16)

    will convert a zero-padded base-10 string to base 16 (assuming it works at all, since numbers have no length property). The closing parenthesis of padZero should be moved to the end for correct behavior.

    [Yup, the paren should be outside. -Raymond]
  8. Random832 says:

    Of course, this kind of algorithm will result in a lot of muddy gray and brown colors, since that makes up a large portion by 'volume' of the sRGB colorspace. If I wanted to generate a "random color", I'd pick a random hue angle and use some sort of weighted probability for the S/V or S/L components.

    "Math.Random() is required to be random up to PRNG standards, that is to say that it should be loosely unpredictable, it should be evenly distributed over the range, and it should have a period substantially larger than the range."

    IIRC, PRNGs in general are not guaranteed not to have multi-variable correlations (e.g. treating every three subsequent values as a coordinate gets you a set of point that's can't be outside of 11 possible planes).

  9. Nick says:

    It's a random 24-bit (and later 12-bit) color being chosen on the client for use in CSS, why would it need to be securely random? It just needs to be reasonably unpredictable to a human.

  10. MattT says:

    @Random832:

    I assume you're talking about the 15 possible planes in LCG(2^31, 65539, 0, 1), but yes, you're right – PRNGs don't have any guarantees about multi-variable analysis, other than the distance between consecutive numbers is non-linear.

    Note that the limitation of LCG as a PRNG implementation is not systemic. Other PRNGs don't suffer this limitation.

  11. Maurits says:

    Darn, I was hoping for a discussion of human sensitivity to different frequencies and intensities.  For example, note that the colors of the form #0x0y0z look very much more similar to each other than do the corresponding colors #x0y0z0.

  12. Gabe says:

    When I first saw "can you do better", I immediately started thinking along Random832's line of thinking: pick a random hue, etc.

    Then I figured that maybe you just wanted a way to simplify the given method, which led me thinking along the same lines as you. I just came up with a different way to implement padding:

    return "#" + ("00000" + Math.floor(Math.random() * 16777216).toString(16)).slice(-6);

  13. Maurits says:

    I'd probably just do something simple like:

    function randomColor() {

    ____var color = "#";

    ____for (var i = 1; i <= 6; i++) {

    ________color += Math.floor(Math.random() * 16).toString(16);

    ____}

    ____return color;

    }

  14. function randomColor(){

        return "#000004" // chosen by fair dice roll

                         // guaranteed to be random

    }

    Well somebody had to do it…

  15. @AndyCadley – You should always give xkcd credit.

  16. TomC says:

    If you only need good enough you could probably get away with this:

    return "#"+new Date().getTime().toString(16).slice(-3);

  17. Tod says:

    Too complicated, not good enough for enterprise usage. I'd use HTML5 technologies to outsource the problem to some SaaS solution running on the cloud, to promote an agile development process and leverage the synergy between function prosumers and bleeding-edge platform providers.

    Of course the server would be written in node.js, so I'd need to use one of those things too, but that's just an implementation detail.

  18. sugendran says:

    The only problem with that implementation is that you're going to end up with the colour of mud a lot of the time. If you want to pick a random colour that is pleasing then you probably want to use the HSV approach (en.wikipedia.org/…/HSV_color_space) where you limit the desaturation and value layers so that you always end up with a vibrant colour. Or if your mood ring is a bit cold then a sad colour that is not mostly grey.

  19. @Anon – Not sure if you are baiting, but why would I know whether a javascript library software is truly random? I can assure you that high-grade crypto hardware does not run any JS whatsoever. Go learn about TRUE random number generators – i.e. custom chips with a high number of asynchronous ring oscillators (each a different length/frequency) that are summed together. These chips go through extensive testing and characterization to get <govt agency> approvals.

    Back on topic, as a part-time web developer, I can see Ray's function as useful for a banner & button generating site. Something where you only need the *perception* of random.

  20. Miff says:

    @The MAZZTer has it right, don't bother with trying to make a hex number and just write out an rgb()-style color triplet. They're supported by every browser (even IE6!) to there's no real drawback to using them.

    That's what I've always done in the past for dynamic colors via JavaScript.

  21. Dan Bugglin says:

    My contribution:

    function randomColor(includeAlpha) {

     return "rgba(" + Math.floor(Math.random() * 256) + ", " + Math.floor(Math.random() * 256) + ", " + Math.floor(Math.random() * 256) + ", " + (includeAlpha ? Math.random() : 1) + ")";

    }

    Yeah yeah I took all the hexadecimal fun out of it.  rgb(rr, gg, bb) syntax is also valid, btw.

    @Adam not really much of a WTF.  Although Raymond's solution of 0 padding by adding and them removing an extra digit is a bit odd, though it might be nicer than padding manually it's also more difficult to read without a comment to help.

  22. Enterprise Rube G says:

    @AndyCadley. Yup.

    And that's probably the shortest and most time efficient way of generating a random number on the client computer. The only problem is that it fails to perform very well for more than 1 page load, and is downright out for more than 2. One obvious fix would be to have the javascript come from the server when the page is loaded. The server-side would invoke a C++ program that builds and outputs the javascript function. Why C++? Well because template metaprogramming would be used to dynamically generate the hard-coded random number of course. So the server-side program would first run a C++ toolchain, then use the resulting program to generate the javascript. As a pre-build step, the C++ project might need to generate a seed to use for the TMP code. One obvious solution would be to run a program that changes the system clock to a random value so that TIME could be the seed. This would imply that only one page at a time could be

    generated to web clients (to avoid duplicate seeds), but this bottleneck could be ameliorated by having the compilation done 'before-hand'. (ie: have a dedicated server farm or cloud that constantly runs the C++ step, so that the web-server-side script or whatever would just grab a pre-built executable then delete it once done (this is the only part that would have to be done inside a lock).

  23. Henry Skoglund says:

    function randomColor() {

    return "#" +

        Math.random().toString(16).substr(-6);

    }

    Note: negative start numbers in substr() requires IE9 or later.

  24. Rick C says:

    Tod, you're on The Old New Thing, not The Daily Worse Than Failure.

  25. Engywuck says:

    and while the original version was easily understandable ven for the layman the final version needs an added comment so you know why it works. As if JS wasn't easily enough obfuscated without even trying…

    on another note: while the original version got three (most likely consecutive) numbers the other versions just get one. So the "randomness" of the resulting color (and additionally the "randomness" of all generated colors if more than one is needed) changes, depending on implementation of the PRNG. Of course, in a perfect RNG this would not matter, but… :-)

  26. @Tod says:

    I agree, simplicity is the key to success. ;-)

  27. Neil says:

    You could replace(!) "#" + str.substr(1) with str.replace(/./, "#").

  28. Anon E Moose says:

    @Henry

    That won't work.  The random number could be "0.1"

  29. Maurits says:

    Just for fun I looked up the ECMAScript specification for Math.random():

    — begin quote —

    15.8.2.14  random ( )

    Returns a Number value with positive sign, greater than or equal to 0 but less than 1, chosen randomly or pseudo randomly with approximately uniform distribution over that range, using an implementation-dependent algorithm or strategy. This function takes no arguments.

    — end quote —

  30. David Walker says:

    @sugendran: Dang you, I followed the link you gave to the Wikipedia article, then followed its footnotes to some fascinating documents that I skimmed and downloaded to read later.  More stuff to do.  It's all your fault.  :-)

  31. Leonardo says:

       function randomColor() {

           return "#444"; // Chosen by fair dice rolls.

                          // Guaranteed to be random.

       }

    http://xkcd.com/221/

  32. Daniel says:

    return "peachpuff";

    My friends all agree that "peachpuff" is quite a random color.

Comments are closed.