Published on

How to find Closest or Similar Color in Javascript

Authors

Recently I was working on a project which requires changing lots of colors. And the new color palette was somewhat different from what we've used before.

So on multiple occasions, I had to figure new color based on the existing color.

It looked tedious to do manually every single time.

And here is how I automated it.

Algorithm

  • Figure out RGB for the given CSS hex code color
  • Figure out the distance between two colors. One current Color and another from the color palette. Do it until we've compared the current color with all the colors from the color palette
  • For calculating color distance we can use various approaches, in our case we'll be using Euclidean distance formula

Let's say we need to find the closest color to "#0aaa6e" and we're comparing it against other colors (#38ad4f, #9fe0ac, #297f3a)

Using the Euclidean distance formula, we'll get:

  • Distance between "#0aaa6e" and "#38ad4f" = 22.67
  • Distance between "#0aaa6e" and "#9fe0ac" = 57.36
  • Distance between "#0aaa6e" and "#297f3a" = 26.46

And the closest color is the one which has smallest distance and hence #297f3a is closest to #0aaa6e

What is Euclidean distance?

The euclidean distance formula is used for calculating the distance between two points in a multidimensional space.

d=(x2x1)2+(y2y1)2+(z2z1)2d = \sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2 + (z_2 - z_1)^2}

In our case, we're using it to figure out the distance between two colors in three-dimensional RGB color space. So using r,g,b notation instead

d=(r2r1)2+(g2g1)2+(b2b1)2d = \sqrt{(r_2 - r_1)^2 + (g_2 - g_1)^2 + (b_2 - b_1)^2}

Now that we've learned the fundamentals. Let's solve the actual problem.

Converting CSS Hex Color to RGB

CSS Hex Code is just the combination of RGB in hexadecimal.

For example, #297F3A === 29 (Red) + 7F (Green) + 3A (Blue)

In Javascript, we can use parseInt to convert a hex string to a number.

function hexToRGB(hex) {
  // Remove the # character from the beginning of the hex code
  hex = hex.replace("#", "");
  
  // Convert the red, green, and blue components from hex to decimal
  // you can substring instead of slice as well
  const r = parseInt(hex.slice(0, 2), 16);
  const g = parseInt(hex.slice(2, 4), 16);
  const b = parseInt(hex.slice(4, 6), 16);
  
  // Return the RGB value as an object with properties r, g, and b
  return {r, g, b};
}

// Example usage
const color1 = "#297F3A";
const color2 = "#0AAA6E";
const rgb1 = hexToRGB(color1); // Output: {r: 41, g: 127, b: 58}
const rgb2 = hexToRGB(color2); // Output: {r: 10, g: 170, b: 110}

Finding Closest of Two Colors - #BackToSchool 😅

By converting the two colors to RGB and applying Euclidean distance

d=(R2R1)2+(G2G1)2+(B2B1)2d = \sqrt{(R_2 - R_1)^2 + (G_2 - G_1)^2 + (B_2 - B_1)^2}

R1=41R_1 = 41, G1=127G_1 = 127, B1=58B_1 = 58

R2=10R_2 = 10, G2=170G_2 = 170, B2=110B_2 = 110

d=(R2R1)2+(G2G1)2+(B2B1)2d = \sqrt{(R_2 - R_1)^2 + (G_2 - G_1)^2 + (B_2 - B_1)^2}

d=(4110)2+(127170)2+(58110)2d = \sqrt{(41-10)^2 + (127-170)^2 + (58-110)^2}

d=312+(43)2+(52)2d = \sqrt{31^2 + (-43)^2 + (-52)^2}

d=2886d = \sqrt{2886}

d=53.704d = 53.704

Finding Closest using Javascript 😇

const closestColor = (targetColor, colorArray) => {
  let closestDistance = null;
  let closestColor = null;
  
  // Convert target color from hex string to RGB values
  const [r1, g1, b1] = hexToRGB(targetColor);
  
  // Loop through the array of colors
  colorArray.forEach((color) => {
    // Convert current color from hex string to RGB values
    const [r2, g2, b2] = hexToRGB(color);
    
    // Calculate the Euclidean distance between the target color and current color
    const distance = Math.sqrt(
      (r1 - r2) ** 2 +
      (g1 - g2) ** 2 +
      (b1 - b2) ** 2
    );
    
    // Update closest color and distance if the current distance is smaller than the closest distance
    if (distance < closestDistance) {
      closestDistance = distance;
      closestColor = color;
    }
  });
  
  return closestColor;
}

Finding Closest using Javascript - Entire Code 😍


/**
 * Converts a hex color code to an RGB color object.
 * @param {string} hex - The hex color code to convert.
 * @returns {object} An object with the red, green, and blue components of the RGB color.
 */
function hexToRGB(hex) {
  // Remove the # character from the beginning of the hex code
  hex = hex.replace("#", "");
  
  // Convert the red, green, and blue components from hex to decimal
  // you can substring instead of slice as well
  const r = parseInt(hex.slice(0, 2), 16);
  const g = parseInt(hex.slice(2, 4), 16);
  const b = parseInt(hex.slice(4, 6), 16);
  
  // Return the RGB value as an object with properties r, g, and b
  return {r, g, b};
}


/**
 * Finds the color in the given array that is closest to the target color.
 * @param {string} targetColor - The target color in hex string format (#RRGGBB).
 * @param {string[]} colorArray - An array of colors to compare against the target color.
 * @returns {string} The color in the array that is closest to the target color.
 */
const closestColor = (targetColor, colorArray) => {
  let closestDistance = null;
  let closestColor = null;
  
  // Convert target color from hex string to RGB values
  const [r1, g1, b1] = hexToRGB(targetColor);
  
  // Loop through the array of colors
  colorArray.forEach((color) => {
    // Convert current color from hex string to RGB values
    const [r2, g2, b2] = hexToRGB(color);
    
    // Calculate the Euclidean distance between the target color and current color
    const distance = Math.sqrt(
      (r1 - r2) ** 2 +
      (g1 - g2) ** 2 +
      (b1 - b2) ** 2
    );
    
    // Update closest color and distance if the current distance is smaller than the closest distance
    if (distance < closestDistance) {
      closestDistance = distance;
      closestColor = color;
    }
  });
  
  return closestColor;
}

const targetColor = "#0aaa6e";
const colorArray = ["#38ad4f", "#9fe0ac", "#297f3a"];
const closestColor = closestColor(targetColor, colorArray);
console.log(closestColor); // Output: "#38ad4f"

Happy finding distances!