loading...

What the Hex!? (how to generate random hex color codes in javascript)

thecodepixi profile image Emily A. Pixi ・4 min read

For my most recent Flatiron School project, I wanted to be able to programmatically change the background color of one of the elements on the page, at random. What I needed was a reusable function that I could call onclick of various elements on the page. Here are two ways I found to make this happen:

First, I knew I didn't want to have to store a bunch of set colors anywhere. It would be tedious to maintain an array or object filled with set color codes, and I wanted the color selection to be truly random. I decided on using hexadecimal color codes because they are relatively short, and the data needed to comprise them (numbers 0-9, and letters a-f) wouldn't take up too much space. This is how I came up with my initial (somewhat "lazy") solution.

First, we create an array of all possible hexadecimal digits:

const digits = [0,1,2,3,4,5,6,7,8,9,'a','b','c','d','e','f']

Then, we need to setup our base hex code string:

let hexCode = "#" 

We set up a string with the hash/octothorpe ready to go, so we can just append the digits to the string.

Then, we need to choose a hexadecimal digit from the array, at random. To do that we use Math.round(), and Math.random() to get a randomly selected index of the array. Once we have that digit, we append it onto our hexCode string until the string is 7 characters long (6 digits + the hash/octothorpe), since hex color codes are 6 digits long:

while( hexCode.length < 7 ){
  hexCode += digits[ Math.round( Math.random() * digits.length ) ]
}

We multiply Math.random() by digits.length (or the number of items in the digits array) because the Math.random() function returns a float between 0 and 1. By multiplying that number by the number of items in digits, we ensure that we'll always get a float that is anywhere between 0 and the total number of items in the array. We wrap this function in Math.round() to round the returned float to the nearest whole number, which makes the total number inclusive of 0 and the total length of the array. We then use this random whole number as the index to select in the digits array.

Once we've done this, we just need to return hexCode, and our final function looks like this:

function generateRandomHexCode(){
  const digits = [0,1,2,3,4,5,6,7,8,9,'a','b','c','d','e','f']

  let hexCode = "#" 

  while( hexCode.length < 7 ){
    hexCode += digits[ Math.round( Math.random() * digits.length ) ]
  }

  return hexCode 
}

Here are some sample outputs of this function:

> generateRandomHexCode()
'#fd88d4'
> generateRandomHexCode()
'#349cba'
> generateRandomHexCode()
'#43a29e'
> generateRandomHexCode()
'#1a1d94'

This works exactly as we need! But after coming up with this solution, I still wondered if there was a more programmatic way to generate a hexadecimal digit, and it turns out there is!

First, let's talk about how hexadecimal (or base 16) digits work. A hexadecimal digit includes decimal numbers 0-9, and the letters a-f. These correspond to the decimal (or base 10) digits 0-15. Here's a quick chart:

decimal to hexadecimal conversion chart

So, if we can find a way to convert a decimal to another number base, all we need to do is generate a random number from 0-15 and convert it to base 16. In JavaScript, we can quickly and easily convert a number to another number base using the .toString() method, and passing in the base digit.

For example, we can convert numbers to binary using .toString(2)

  > (10).toString(2)
  '1010'

  /* if you do not pass the number to `.toString()` 
inside of parentheses you will get a syntax error */

Let's see what happens when we try this with a few decimal numbers, converted to base 16:

  > (0).toString(16)
  '0'
  > (11).toString(16)
  'b'
  > (5).toString(16)
  '5'
  > (15).toString(16)
  'f'

Perfect! That's exactly what we expected, and what we need!

Using this knowledge we can convert our hex code randomizing function as follows:

  function generateRandomHexCode() {
    let hexCode = "#" 

    while ( hexCode.length < 7 ) {
      hexCode += (Math.round(Math.random() * 15)).toString(16) 
    }

    return hexCode 
  }

In this version of the function we use Math.round() and Math.random() * 15 to generate a random number between 0 and 15, and then convert that number to its hexadecimal equivalent using .toString(16), and append that newly generated digit to the hexCode base until we have 6 digits.

And here are a few of the resulting hex codes:

  > generateRandomHexCode()
  '#d5758c'
  > generateRandomHexCode()
  '#26711b'
  > generateRandomHexCode()
  '#8deca6'
  > generateRandomHexCode()
  '#3df62c'
  > generateRandomHexCode()
  '#1a293a'

Excellent!

You can use this output to update the color (or backgroundColor) of and element in your projects, using something like the following:

  someElement.style.backgroundColor = generateRandomHexCode()

You can see this code in action in the CodePen below.

Let me know if there are other hex code generating methods you know of, and definitely let me know if you try this out in your own projects!

xx Emily/@thecodepixi

Discussion

pic
Editor guide
Collapse
lukaszahradnik profile image
Lukáš Zahradník

Hi, I just wanted to point out small bug.

hexCode += digits[ Math.round( Math.random() * digits.length ) ]

should use Math.floor instead of Math.round.

Also I wrote different approach to generate random hex. There is no need for loops.

const generateHex = () => `#${Math.floor(Math.random() * 256**3).toString(16)}`
Collapse
thecodepixi profile image
Emily A. Pixi Author

Yeah someone else pointed this out to me. Math.floor vs Math.round is one of those things I always forget. Also, excellent work on the alt method! Just as a note, with that solution, it is possible you will get hex codes that are less than 6 digits in length, which won't work as color codes. You'll still need a way to check for the length of the code.

Collapse
beerwerd profile image
Beerwerd

Your code is shorter but harder to understand.

Collapse
lukaszahradnik profile image
Lukáš Zahradník

How? You generate random number and convert it to string of number with base 16.

The same approach is in the article, but uses 6 random number instead of one number.

Thread Thread
thecodepixi profile image
Emily A. Pixi Author

Just to clarify, in the article, the intention is to generate individual digits and convert them to hex one at a time until you have a string of 6 hexadecimal digits. If you just generate any decimal number between 0 and 16777215 and then convert that number to hexadecimal, it will sometimes output hexadecimal numbers shorter than 6 digits (i tested and got a few 4 and 5 digit results) which cannot be used as a hex color code and will cause the function to break if used to assign a code to an element on the page. You still need a way to check for length of 6 on your output. That said, your code does work a lot of the time. But sometimes it doesn't. For me, it broke about 1/20 times when I tested it.

Thread Thread
lukaszahradnik profile image
Lukáš Zahradník

Fair point, easy fix

const generateHex = () => `#${Math.floor(Math.random() * 256**3).toString(16).padStart(6, '0')}`

or more readable

const generateHex = () => {
  const hex = Math.floor(Math.random() * 256**3).toString(16).padStart(6, '0')
  return `#${hex}`
}

//edit: Btw, because of your use of Math.round values don't have the same probability distribution

Collapse
khauri profile image
Khauri

Nice work! I love seeing the thought processes behind figuring out how to make code better. It's like watching someone piece together a mystery in front of you.

I'm sure you know this as well, but for anyone who doesn't, if you prefix a number with 0x you can use hex values in your code. (There's also 0b for binary and 0o for octal).

Something else to note is that when using Math.floor1 since Math.random generates a random number between 0 and 1, but never exactly 1, you have to add 1 to your maximum or else it will never generate that value. So in this case, you should go up to 16 or you'll never get the value f:

Math.floor(Math.random() * 16)
// or 
Math.floor(Math.random() * 0x10)

1 I didn't read the article well enough and assumed Math.floor was being used

Collapse
thecodepixi profile image
Emily A. Pixi Author

Thanks, Khauri! I actually didn't know about the 0x thing. That's super cool! And ++ to the Math.floor() point. It's one of those things I always forget. I used Math.round() in its place but do know that that's not the best solution.

Collapse
lukaszahradnik profile image
Lukáš Zahradník

That's in case of Math.floor, not Math.round as is in the article. Math.round just causes issue with probability distribution of characters.

Collapse
pachicodes profile image
Pachi (she/her/ela)

Just wanted to say I am proud of you for writing this post!
It is great and I must go do something similar now 😂 maybe with cats 🤔

Collapse
thecodepixi profile image
Emily A. Pixi Author

I demand colorful cats!

Collapse
pachicodes profile image
Pachi (she/her/ela)

O M G !!! How do I do colorful cats? Idk but I must find out!

Thread Thread
thecodepixi profile image
Emily A. Pixi Author

I have no idea but I believe in your ability to figure it out haha

Thread Thread
pachicodes profile image
Pachi (she/her/ela)

Thank you for your trust! 🌈😽😼

Collapse
xanderyzwich profile image
Corey McCarty

You could also scale your random by the max value #ffffff and you wouldn't need a loop.

Collapse
thecodepixi profile image
Emily A. Pixi Author

Someone else pointed this out, and it does work quite often, but you do still need to check for a length of 6 on your output for it to be a valid hex color code. Testing that code, it does occasionally output numbers of 4 or 5 digits in length which will break the intended functionality.

Collapse
xanderyzwich profile image
Corey McCarty

I believe that there is a function to pad the left side of the string with zeroes up to a given length (or you could do it yourself)

Collapse
halented profile image
Hal Dunn

so fun

Collapse
thecodepixi profile image
Emily A. Pixi Author

I know, right!? I want to sit and click that lil square on codepen forever haha

Collapse
helleworld_ profile image
Collapse
shan810 profile image
Shantanu Borgohain

Very Nice!
But, I could see the function is excluding the value f every time. Do you have a fix for that?