6
\$\begingroup\$

Is there any better way of doing this?

function getRandomColor() {
    var chars = '0123456789ABCDEF'.split('');
    var hex = '#';
    for (var i = 0; i < 6; i++) {
        hex += chars[Math.floor(Math.random() * 16)];
    }
    return hex;
 }
\$\endgroup\$
8
\$\begingroup\$

Hex colors can be in 3 digits or 6 digits. In the case where you want 3 digits, you can't do it because 6 is hardcoded. You may want to move it into a variable with a default value of 6.

Strings characters in JS are accessible via indices the same way as an array. So splitting the characters into an array is unnecessary, not to mention the additional memory consumed by creating an array.

Adding | 0 after a number truncates the decimal places from the number. Internally, it converts the internal representation of the number from a float into an integer, dropping the decimal information. So basically, it does the same thing as Math.floor, albeit in a more vague but shorter way.

You could also use reverse looping, and using the length as the condition. When length is zero, it's essentially falsy, thus stopping the loop.

function getRandomColor() {
  var length = 6;
  var chars = '0123456789ABCDEF';
  var hex = '#';
  while(length--) hex += chars[(Math.random() * 16) | 0];
  return hex;
}

document.write(getRandomColor());

\$\endgroup\$
  • \$\begingroup\$ Works like a charm thx! \$\endgroup\$ – Patiphan Mahamat Oct 20 '15 at 13:28
  • \$\begingroup\$ it's worth noting that any 3-digit hex color is also representable as a 6-digit hex-code, so unless you have a particular reason for wanting 3-digit colors, it's unimportant. (#ABC => #AABBCC.) \$\endgroup\$ – j6m8 Oct 20 '15 at 22:54
  • \$\begingroup\$ @j6m8 It's also worth noting that while it's unimportant, it's an option. Just because you can peel an apple doesn't mean you can't eat the skin. \$\endgroup\$ – Joseph Oct 20 '15 at 22:57
  • \$\begingroup\$ For sure! Although, to be really nitpicky, if you have a random distribution of 6-digit codes and 3-digit codes, you'll double the probability of those three-digit-representable colors ;D \$\endgroup\$ – j6m8 Oct 21 '15 at 2:46
6
\$\begingroup\$

Edited: The first version was a little naive, and would sometimes return less than 6 digits. To ensure 6 digits, with leading zeroes, and allowing for pure white to be returned, we need to do some extra magic:

function getRandomColor() {
   max = 1 << 24;
   return '#' + (max + Math.floor(Math.random()*max)).toString(16).slice(-6);
}

// Lets generate some colors
document.write("<pre>");
for (i=0; i < 50; i++) {
   document.write(getRandomColor()+ "  ");
   if (i % 5 == 4) {
      document.write("<br />");
   }
}
document.write("</pre>");

First of all it adds the max, i.e. 0x1000000, to ensure leading zeroes, with the random number multiplied by max. This number is then converted into hexadecimal notation and we take the last six characters of this result. If you want only three digits, change the slice value. If extending for 24 bits, you'll also need to extend the max value approriately.

Edit hat tip to MarcDefiant: The code does now allow for pure white, 0xFFFFFF, and pads the returned number with leading zeroes.

\$\endgroup\$
5
\$\begingroup\$

You are discarding most of the random bits from the random numbers that you generated. All you need is a random integer between 0x000000 and 0xffffff, inclusive.

I would use a very similar solution to @holroy's. Also, I suggest dropping "get" from the function name, since it's generating rather than retrieving something.

function randomColor() {
    var color = Math.floor(0x1000000 * Math.random()).toString(16);
    return '#' + ('000000' + color).slice(-6);
}

Note that picking a color uniformly from the RGB color space might not be the best approach, depending on what you are using the color for. If you want to pick colors that are uniformly distributed according to human color perception, use the L-a-b color space instead, then convert the result to RGB.

\$\endgroup\$
  • \$\begingroup\$ nice solution. what's the reason you aren't just returning '#' + color? \$\endgroup\$ – Jonah Oct 20 '15 at 19:27
  • 1
    \$\begingroup\$ @Jonah Zero padding. \$\endgroup\$ – 200_success Oct 20 '15 at 19:30
  • \$\begingroup\$ Wow, @200_success. I didn't know that. infinity * Thanks \$\endgroup\$ – Sherali Turdiyev Oct 21 '15 at 12:34
3
\$\begingroup\$

This can be condensed and simplified a bit more.

Math.random().toString(16)

This line will return a decimal value which we want to turn into a String. We also want the number to be written in base 16. So, by doing these two methods we get a result that looks like this:

0.ae5782d36ac5331

All we need to do is skip past the zero and the decimal and grab the first 6 characters. We don't need to worry about padding because the length of the number that is returned by Math.random() will always be more than 6 digits.

This leaves us with the following:

return "#" + Math.random().toString(16).slice(2, 8);

The final step is adding the "#" in front of our 6 characters.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.