Random hex color code generator in JavaScript
For fun I asked a few friends for ideas on a random color generator in a single line of javascript. You know, these guys: #0afec0, #c9f2d0, #9b923e.
Here's what we came up in about two minutes (in chronological order)…
'#' + (function co(lor){ return (lor += [0,1,2,3,4,5,6,7,8,9,'a','b','c','d','e','f'][Math.floor(Math.random()*16)]) && (lor.length == 6) ? lor : co(lor); })('');
Similar recursive technique, but using a string instead of an array and aliasing the Math object:
(function(m,s,c){return (c ? arguments.callee(m,s,c-1) : '#') + s[m.floor(m.random() * s.length)]})(Math,'0123456789ABCDEF',5)
Using a named function expression instead of arguments.callee.
'#'+(function lol(m,s,c){return s[m.floor(m.random() * s.length)] + (c && lol(m,s,c-1));})(Math,'0123456789ABCDEF',4)
If we assume JavaScript 1.6, then we could just use Array.map():
'#'+'0123456789abcdef'.split('').map(function(v,i,a){ return i>5 ? null : a[Math.floor(Math.random()*16)] }).join('');
But then, the magic of Math struck (16777215 == ffffff in decimal):
'#'+Math.floor(Math.random()*16777215).toString(16);
Follow me on twitter: @paul_irish
oh wow, i absolutely love that final solution, i would have never thought of that
Very nice. Though you might want to pad with zeros for when the random value is less than 0×100000.
nlogax just offered a revision to the last one to use a bit-wise shift left operator, plus FFFFFFF in hex for clarity.
Excellent tip, I could have used this just the other day. The 0xFFFFFF is clearer, and good comment about padding to the left because either way you will occasionally get 5 (or fewer) digit colors.
Man, there are two problems here:
1 -
Math.random()*1<<0will never produce 1. For a range 0,N with virtually same possibilities you needMath.random()*(N+1)<<0so the right one is:otherwise 0xFFFFFF will never happen
2 - as somebody said already you need to pad the string so the quick one is:
Regards
Obviously, this is even better (could be confusing though):
Just for fun, I add a quick and dirty pad function as well:
so, the color is
and we can reuse the pad function everytime we need.
Stretching the original "quick, dirty one-liner" concept a bit, and fixing the obvious problems with it:
nulogaksu: Nope, because that algorithm will never generate uniform black (#000000)!
Also, you can't just multiply Math.random() by a number and round it*, that doesn't produce a truly random range. But, picking a random colour isn't like drawing random cards for a poker game - it doesn't need perfect uniformity (or at least: as perfect a randomness as your RNG can muster).
*) The reason is the pigeon hole principle: An IEEE double has a 52-bit mantissa, so that's how much randomness you generally start out with (52 bits worth). Lets say you now want to generate a random number between 0 and 2^52-2. Then, exactly one number in your supposed uniform range shows up twice as often as any other number - after all, there are 2^52 input numbers which map to 2^52-1 numbers, so exactly two of the 2^52 numbers map to the same output number. In practice its worse, and some output numbers can never be attained. I also oversimplified how IEEE doubles work, but this is enough detail to explain why Math.random()*max is a flawed way to generate random integers. In java, you should use Random.nextInt(maxNum), but that method isn't available in javascript.
Again, mostly an academic point - the flawed random number generation is still decently random, if not entirely uniform, and I can't think of any situations where strict uniformity is an absolute requirement when picking a random colour!
Reinier: You're right, that's missing a pair of parens. No idea why I didn't just use
16777216when I aimed for compact rather than readable. :)I was wondering on IRC earlier when someone would say "It's not random enough!" but as you said, it's good enough for this purpose.
Reinier, nobody rounded numbers here … <<0 is like a Math.floor … but I already wrote how to obtain a virtually correct random, no?
Math.floor(Math.random() * (N + 1));
which is exactly like:
Math.random() * (N + 1) << 0
since bitwise has less precedence than multiply operator
again, 0×000000 to 0xFFFFFF is:
(Math.random()*0×1000000<<0).toString(16)
no? Regards
Your padding solutions are quite general, but not small enough!
Here's the shortest random hex color code I could come up with:
And this is even shorter, if #ABC colors are fine (vs #ABCDEF) :P
Now, what's the shortest and best algorithm for generating a good contrast color with the random color (one that is visible against the base color)?
@MTyson, I did some research on this. From this particletree article (with a shoutout to friend BarelyFitz), the below would be the best for calculating:
I'll leave it up to the community to shorten. :)
Interesting read. That's essentially what I came up with also, check the brightness and use white or black accordingly (and blend in a little color from the base for spice). Thanks!
Not exactly one line but of you go for rgb() easier to understand! Can be put in one line if need be. Leave it for you Ninjas
@"Cowboy" Ben Alman
the substr with negative start doesn´t work in IE, producing invalid hex-color code.
http://www.haroldbakker.com/substr.php
http://www.wormus.com/aaron/stories/2005/06/15/whats-with-substr-in-internet-explorers-javascript.html
Does it have to be hex? Colors can also be defined using plain integers via rgb():
color = 'rgb(' + Math.floor(Math.random() * 255) + ',' + Math.floor(Math.random() * 255) + ',' + Math.floor(Math.random() * 255) + ')';
Hey Paul - randomly landed on your post looking for something - look what I post on Twitter two weeks before you blogged: http://twitter.com/rem/status/15427387974 - spooky!
@Remy Sharp
"look what I post on Twitter two weeks before the one year anniversary that you blogged:"
FTFY.
:)
face => palm. Okay, so you worked this out just 50 weeks before I did! :-D
I think there's a flaw in both Paul's and Remy's scripts. You're multiplying
Math.random()by16777215, which isFFFFFF(hex) in decimal. Then, you’re rounding the whole thing down using `Math.floor()` (or in Remy’s case, a double bitwise NOT).However,
Math.random()returns a pseudo-random number in the range [0,1) — that is, between 0 (inclusive) and 1 (exclusive).Since
Math.floor()will never return 1, the random color generator will never return#FFFFFF, sinceMath.floor(1 * 16777215) === 16777215.The solution is simple: just use 16777216 instead of 16777215. This number has the added advantage of being a power of 2 (
2^24), so using some bitwise left shiftin’ love you can shorten it to1<<24.So using a combination of Mathias' mega strength and my recent desire to ensure that every value produces a genuine colour, I've fixed the generator as such:
(function(h){return '#000000'.substr(0,7-h.length)+h})((~~(Math.random()*(1<<24))).toString(16))
working example
This ensures that if the number generated via random is less than or equal to 1048575 like 255 for instance, it will still produce a real colour and not generate a short invalid hex value that will then default to black.