Joshua Mendoza
JfuseCodes

JfuseCodes

Create a Random Color Generator with Javascript

Joshua Mendoza's photo
Joshua Mendoza
·Jul 25, 2022·

13 min read

Create a Random Color Generator with Javascript

Subscribe to my newsletter and never miss my upcoming articles

Play this article

Hello World:

This is a tutorial and code for building a random color generator that displays the hex color code of the current document background color. To be able to follow along, it is best to at least have an idea about the following topics:

  • Javascript:
    • Document Object Model(DOM) Manipulation
    • Events
    • Array
    • Template Literals and string interpolation
    • built-in functions

Furthermore, I will explain the topics I’m using to build this web app. This is an excellent tutorial for javascript beginners, and here’s what the outcome should be.

app-preview.png

Folder Structure

The first step is to create the files we will be working with. The mandatory HTML and javascript files along with the CSS file. Don’t forget to link the .js and .css files within the .html file.

bg-color-generator.png

Since I will demonstrate mainly DOM manipulation and events, file path linking is the only work that needs to be done on the actual HTML file. Everything will be created and changed purely from within javascript, along with some extra styling within the CSS file.

Elements & ClassName Setup

We’re gonna be setting up essentially the bones of the project. Without this, there’s nothing.

We use the method from a built-in javascript class (document) to create elements. I like to keep things organized, so I usually make the main tag that will act as a container for all the content.

const main = document.createElement('main'),                    // A main to hold the main elements of the document
      colorGeneratorButton = document.createElement('button'),  // the button that changes the "background color" of the document
      hexCodeHeading = document.createElement('h1');            // the text that will display the current document color

We can add text to elements with the following methods:

I use textContent when I want to set an element's text. But if I want to capture what's already within an element, I use innerHTML. The difference in the methods is what data is rendered or returned, as shown in the article linked for innerText.

With that said, let’s add some text to the button.

colorGeneratorButton.textContent = 'Next Color';

When you create an element, it signifies to the browser that the element can be used. It does not mean it will render unless specifically told to do so. So, we can use the following methods to add elements:

I’ve rarely used appendChild because I see it as a way to directly append an element to the end of the parentNode(usually container). While on the other hand, append allows you to add multiple node objects.

So, First, we will add the hexCodeHeading and button elements to the main element. And then add that same main element to the document element.

main.append(hexCodeHeading, colorGeneratorButton);

Tip: add the element in the order that you would want them nested in an HTML file

furthermore, we add the main element to the document like so:

document.body.append(main);

Generate Numbers

First, I usually like to get the app working fundamentally and then refactor it to make it work correctly the way it is intended. In doing so, I have been able to clean up the code and learn new ways of doing things. So we can start by attempting to understand what we need. Background color can compose the actual color’s name, RGB() colors, hue saturation colors, etc. For this project, the goal was to obtain RGB colors and convert them to hex.

We will focus on generating RGB colors. The RGB has three sets of numbers separated by commas ranging from 1 to 255. The goal is to create a function that returns a value at a minimum of 1 up to and including 255. For this, we will use a couple more javascript built-in methods called random(), ceil(), and floor(), all of which comes from the Math object.

const getRandomInt = (min, max) ⇒ {

min = Math.ceil(min);

max = Math.floor(max);

return Math.floor( Math.random() * (max - min + 1)); 

}

There’s a lot to unpack here, but the methods are as follows:

  • Math.ceil()
    • rounds a number up to the nearest integer
    • used to make sure a number is never less than the minimum
  • Math.floor()
    • returns the largest integer less than or equal to a given number
    • used to make sure a number is never larger than the max number inputted
  • Math.random()
    • returns a random number from 0(inclusive) up to but not including 1.

Generate Colors for RGB Format

Now that we can generate numbers, let us create a function that makes the RGB() needed.

We create a function(changeColor) that will later be used in an event listener.

const changeColor = () ⇒ {

  redColor = getRandomInteger(1, 255),

  greenColor = getRandomInteger(1, 255),

  blueColor = getRandomInteger(1,255);

}

At this point, we have created a function that can return 3 different sets of numbers, all between 1 to 255. Now our next goal is to convert the RGB numbers into a hex color code.

Convert RGB to Hex

Hex color code is preferable when coding because:

  • more readable and cleaner code
  • slightly improves web page load time

So to create a function that converts RGB to Hex, once again, we must understand the core concept of a hex code and RGB color code.

A hex color code is:

  • usually, a 6-symbol code made up of up to three 2-symbol elements
    • each of the 2 symbol elements expresses a color value from 0 to 255
  • based on the hexadecimal number system, which is base-16, that which has 16 unique characters
  • characters used:
    • numbers 0 - 9
    • letters A - F
    • the last digit is F

An RGB goes by the decimal number system, based on base-10, which includes 10 unique characters, 0 - 9. So each character 1 - 15 in the decimal number system corresponds to a hexadecimal system.

The first step is to create a system for which we can look up numbers when it's time to convert. Create an object with the key-value pairs being decimal:hex.

const system = {
            0: 0,
            1 : 1,
            2 : 2,
            3 : 3,
            4 : 4,
            5 : 5,
            6 : 6,
            7 : 7,
            8 : 8,
            9 : 9,
            10 : "A",
            11 : "B",
            12 : "C",
            13 : "D",
            14 : "E",
            15 : "F",
        }

For this function, we will use template literals and string interpolation.

Template literals are created with interpolation is a great way to insert a variable/expression/function within a string.

They are created using back-tick(`) characters. To use interpolation, we use a dollar sign and open closing brackets with the expression inside(${functionExpressionName}).

We will create a function that takes in a value which is the random color integer from rgb.

So the pseudocode is as follows:

  • take input value

  • divide input value by 16 which usually results in decimal

  • first digit is number before decimal point

  • second digit is the number after decimal point * 16

  • save first number to a variable

  • save second number to a variable

  • use both numbers to look up in the system

  • the value to the key is the hex color code number

const rgbToHex = x => {
        character = ((x / 16).toFixed(2)).toLocaleString().split('.'); 
        firstCode = Number(character[0]), secondCode =  (Number(`.${character[1]}`))*16;
        return `${system[firstCode]}${system[secondCode.toFixed(0)]}`;
    };

A few notes:

  • We use the method(toFixed()) with the number 2 to get only 2 digits after the decimal point to avoid any crazy decimal numbers.
  • We convert to string to use a string method(split (link))
    • Splitting the string allows us to make the calculations needed on two numbers, the number before the decimal and the number after the decimal, by accessing it via array indexing.
  • For any calculations to be made, the data type must be a number which is why we have to convert it back to a number.
  • We return the variable secondCode as a fixed decimal point version because:
    • It rounds the number down or up to the nearest integer.
    • The created object "system" can look up precisely what number is needed by searching for a key and receive an exact value.
  • The system object and rgbToHex code snippets must be placed before the changeColor function because it avoids the undefined variable/function error.

Events & Listeners

In this section, we will focus on actually creating the web app's interactivity. But before that, let’s refactor the changeColor function. We will place the rgbToHex function with the return value of the random integers as the input.

There are two ways to do this.

We can create separate variables and place the variables within the function like this:

const changeColor = () => {

        let redColor = rgbToHex(getRandomInteger(1,255)),
             greenColor = rgbToHex(getRandomInteger(1,255)),
             blueColor = rgbToHex(getRandomInteger(1,255));

        let hexRed = rgbToHex(redColor),
             hexGreen = rgbToHex(greenColor), 
             blueColor = rgbToHex(blueColor);
}

Or, as a way to be more concise and less verbose, we can call the function within the parameter of the rgbToHex like so:

const changeColor = () => {

        let redColor = rgbToHex(getRandomInteger(1,255)),
             greenColor = rgbToHex(getRandomInteger(1,255)),
             blueColor = rgbToHex(getRandomInteger(1,255));
}

We then create a variable that will display the randomized hex color code value using template literal and string interpolation. We will eventually set the document's background color to that hexCode value.

    hexCode = `#${redColor}${greenColor}${blueColor}`;
    document.body.style.backgroundColor = hexCode;

Ultimately we display the actual hex color code number using the textcontent method on the heading element we created earlier.

    hexCodeHeading.textContent = hexCode;

Now we add the functionality of the web app, the event listener. We take the button, addEventListener, the event is "click," and add the function we want to run once the event occurs, which is the changeColor function.

So our changeColor function should look like this:

const changeColor = () => {
        let redColor = rgbToHex(getRandomInteger(1,255)),
            greenColor = rgbToHex(getRandomInteger(1,255)),
            blueColor = rgbToHex(getRandomInteger(1,255));

        hexCode = `#${redColor}${greenColor}${blueColor}`;
        document.body.style.backgroundColor = hexCode;
        hexCodeHeading.textContent = hexCode;
      };

And last but not least, our eventListener:

colorGeneratorButton.addEventListener('click', changeColor);

This is our entire javascript file up to this point:

      const main = document.createElement('main'), // A main to hold the main elements of the document
      colorGeneratorButton = document.createElement('button'), // the button that changes the "background color" of the document
      hexCodeHeading = document.createElement('h1'); // the text that will display the current document color

      main.classList.add('main');
      colorGeneratorButton.classList.add('btn');
      hexCodeHeading.classList.add('hexcode-heading');
      colorGeneratorButton.textContent = 'Next Color';
      main.append(hexCodeHeading, colorGeneratorButton);
      document.body.append(main);

      const getRandomInteger = (min, max) => {
        min = Math.ceil(min);
        max = Math.floor(max);
        return Math.floor(Math.random() * (max - min + 1));
      };

      const system = {
        0: 0,
        1 : 1,
        2 : 2,
        3 : 3,
        4 : 4,
        5 : 5,
        6 : 6,
        7 : 7,
        8 : 8,
        9 : 9,
        10 : "A",
        11 : "B",
        12 : "C",
        13 : "D",
        14 : "E",
        15 : "F",
    }
    const rgbToHex = x => {
        character = ((x / 16).toFixed(2)).toLocaleString().split('.');
        firstCode = Number(character[0]), secondCode =  (Number(`.${character[1]}`))*16;
        return `${system[firstCode]}${system[secondCode.toFixed(0)]}`;
    };

      //ADD EVENT LISTENER TO THE BUTTON

      const changeColor = () => {
        let redColor = rgbToHex(getRandomInteger(1,255)),
            greenColor = rgbToHex(getRandomInteger(1,255)),
            blueColor = rgbToHex(getRandomInteger(1,255));

        hexCode = `#${redColor}${greenColor}${blueColor}`;
        document.body.style.backgroundColor = hexCode;
        hexCodeHeading.textContent = hexCode;
      };

      colorGeneratorButton.addEventListener('click', changeColor);

User Experience(UX)

Now we will just make the web app create a better user experience. So we have to tweak the javascript a bit more to add color dynamics to the heading and button elements. So if the background color is darker, then the heading and button would be a lighter color that is complementary and vice versa.

We will use the HSP(http://alienryderflex.com/hsp.html) system. In summary, it uses 3 floating point numbers that represent effectiveness from a human's perspective in regards to the brightness of the color. A few mathematical operations will determine whether to use a dark or light shade.

This is the pseudocode for what we want to do:

  • convert hex color code back to RGB values(SKIP THIS STEP IF RANDOM RGB COLORS ARE SAVED TO VARIABLES )
  • take the square root of the sum of: ( .299 Red color value squared) (.587 Green Color Value squared) (.114 Blue color value squared)
  • save the result to a variable(HSP)
  • if the HSP value is greater than 127.5
    • the result is a lighter color should be used for complimentary effects
  • else
    • a darker color should be used instead

So now let’s build our function. We’ll name it lightOrDark. The first step is to convert the hex color code to RGB form. Which we will also create an extra function for.

In order to convert the hex color code to rgb form, we take every two characters, multiply the first half by 16 and add it to the second half. For Example, take #4A18E2. We take 4A, 18, E2. 4 * 16 = 64 + 8 is 74. That would be the red color in RGB. And we continue that process two more times.The pseudocode in regards to the converter would be:

  • set parameter for 1 input value
  • take input
  • split into two
  • take the first character
    • and look into system object
    • retrieve the key by looking up its value
    • and multiply the key(number) by 16
  • take the second character
    • and look into system object
    • retrieve the key by looking up its value
    • and take the product of the key * 16 and add to this key
  • and return the product

This is the converter function:

const hexToRgb = (x) => {
        splitInput = x.split('');
        firstChar = splitInput[0], secondChar = splitInput[1];
        for(let key in system) {
            if(system[key] == firstChar) firstChar = Number(key);

            if(system[key] == secondChar) secondChar = Number(key);
        };
        return (firstChar * 16) + secondChar;
    }

This is the light or dark function:

const lightOrDark = (red, green, blue) => {
        red = hexToRgb(red);
        green = hexToRgb(green);
        blue = hexToRgb(blue);

        hsp = Math.sqrt( 
            .299 * (red * red) + 
            .587 * (green * green) + 
            .114 * (blue * blue));

        if(hsp > 127.5) return 'light'
        else return 'dark';
      };

These functions should be placed before our primary "changeColor" function. Now we just tweak our change color function to consider the light or dark function.

This is now what our change color function will now look like:

const changeColor = () => {
        let redColor = rgbToHex(getRandomInteger(1,255)),
            greenColor = rgbToHex(getRandomInteger(1,255)),
            blueColor = rgbToHex(getRandomInteger(1,255));

        hexCode = `#${redColor}${greenColor}${blueColor}`;
        document.body.style.backgroundColor = hexCode;
        hexCodeHeading.textContent = hexCode;

        let colorType = lightOrDark(redColor, greenColor, blueColor);
        if(colorType == 'light') {
            hexCodeHeading.style.color = '#000000';
            colorGeneratorButton.style.backgroundColor = '#000000';
            colorGeneratorButton.style.color = '#FFFFFF';
            colorGeneratorButton.style.borderColor = '#000000'
        }
        else {
            hexCodeHeading.style.color = '#FFFFFF';
            colorGeneratorButton.style.backgroundColor = '#FFFFFF';
            colorGeneratorButton.style.color = '#214469';
            colorGeneratorButton.style.borderColor = '#FFFFFF'
        }
      };

The contrast showing now is excellent for light background colors as well as dark background colors.

USER INTERFACE (UI)

And to finish things off, in this section, we will just style the elements for a better user design.

The next block of code will do the following:

  • reset 3 CSS styles, import
  • import a font family
  • center the elements
  • upgrade the look of the button
*{
    box-sizing: border-box;
    margin:0;
    padding:0;
}
@import url('https://fonts.googleapis.com/css2?family=Anton&display=swap');

html{
    font-family: 'Anton', sans-serif;
};

body{
    display:flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    overflow: auto;   
};

h1{
    font-size:128px;
};

button{
    height:40px;
    width:128px;
    font-size:22px;
    border: 2px solid;
    font-weight:bold;
    cursor: pointer;
};

main{
    display:flex;
    flex-direction: column;
    align-items: center;
    justify-content: space-around;
};

Until Next Time

And here it is, the end of this article. An explanation/tutorial on how to create a random color generator with Javascript. Project-based learning, where the developer gets out there and puts the knowledge to use, is the most significant way to learn, in my opinion at least. This project provides that necessary challenge to put into practice the use of DOM, Events, and a couple of bonus topics we needed to finish the color generator.

Check out the live preview of the finished project and its source code over at my codepen.

Any questions or feedback? Please don't hesitate to reach out to me via my website or Twitter.

Did you find this article valuable?

Support Joshua Mendoza by becoming a sponsor. Any amount is appreciated!

Learn more about Hashnode Sponsors
 
Share this