DEV Community

Cover image for Color Harmonies in JavaScript
benjaminadk
benjaminadk

Posted on

Color Harmonies in JavaScript

Intro

I recently designed a color utility application using Electron. Electron is a JavaScript framework for building cross-platform desktop apps. I learned that when it comes to coding, color and math are joined at the hip. This article summarizes some of my takeaways.

GitHub logo benjaminadk / color-tool-remix

Color Tool Remix 🎨 A cross platform color utility app

Color Tool Remix

An HSL color picker, dropper, color analyzer and palette generator. The do it all color tool.

🧐 Looking for a contributor to help testing on macOS.

GitHub release Github All Releases

Platform AppVeyor Platform Travis

Contents

Installation

Latest Release

Platform Auto Updates Exexutable
Windows yes electron-picker-remix-setup-[VERSION].exe
macOS no electron-picker-remix-setup-[VERSION].dmg
Linux N/A N/A

System warnings are normal due to lack of Code Signing

Usage

Picker

Create a color quickly, via a variety of input methods

  • Color Bars
    • Adjust Hue, Saturation, Lightness and Alpha Color Bars
  • Fine Tuning
    • Adjust Hue, Saturation, Lightness and Alpha Input Values
  • Color String Parser
    • Parses hsl, rgb, hex and css named colors
  • Random Color
    • Generates a random color excluding white, blacks and greys
  • Dropper
    • Select any pixel from the screen
  • Generators
    • Mathematically generate colors from one base color

Dropper

Select a single pixel from

Formats

There is no shortage of color formats floating around. In CSS alone we can chose between 4 formats. Note that 3 of these formats also have an optional alpha, or opacity setting. To learn about the touching story of rebeccapurple, checkout this short article.

rebeccapurple

Format Abbreviation Example
Named Colors n/a rebeccapurple
Hexadecimal HEX #663498
Red, Green, Blue RGB rgb(102,52,152)
Hue, Saturation, Lightness HSL hsl(270,49%,40%)

In my day-to-day work I tend to favor HEX or RGB over the others. However, when designing Color Tool I used HSL as my base format.

HSL Benefits

I found that HSL was the most human-understandable color format. Why? Take a closer look at each component of this format. Hue is a number between 0 and 360, and represents degrees on a color wheel. In no other format does one value give us some much information about what the final color might look like. Red is at 0/360. Saturation is the amount of color vs grey, with 100% being all color and no grey and 0% being all grey and no color. Lightness is another scale between 0 and 100. 0 is totally black and 100 is totally white, therefore 50 is the happy medium.

wheel

Parsing HSL String

In order to work with HSL values we need to be able to take an initial string value, such as hsl(270, 49%, 40%), and break it down into individual H, S, L, and/or A pieces.

function parseHSL(str) {
  var hsl, h, s, l
  hsl = str.replace(/[^\d,]/g, '').split(',')   // strip non digits ('%')  
  h = Number(hsl[0])                            // convert to number
  s = Number(hsl[1])
  l = Number(hsl[2])
  return [h, s, l]                              // return parts
}
Enter fullscreen mode Exit fullscreen mode

Color Harmonies

Color Harmonies are combinations of colors that relate to each other mathematically. Some of the more commonly used harmonies include Complementary, Split Complementary, Triadic, Tetradic and Analogous. Lets take it back to rebeccapurple to see what these look like.

Harmony Colors Wheel
complementary
split complementary
triadic
tetradic
analogous

Finding all of these values boils down to math. Using HSL is handy here. We are mostly concerned with the Hue in these calculations. When finding harmonies is these cases we will leave the Saturation and Lightness unchanged. To gain a visual understanding of where on the color wheel harmonies lie take a look at the small icons in the table. These are set patterns and if we know the Hue of our base color and that a circle has 360 degrees we can find our harmonies. For example, a colors complement is directly on the opposite side, or 180 degrees away.

const complementaryHue = (baseHue + 180) % 360
Enter fullscreen mode Exit fullscreen mode

Split complementary produces two colors from the complement +- 30 degrees. At this point I start to think about a function that can work in real life. This function needs to take an actual color string e.g. hsl(34, 100%, 50%) and return an array with all colors in the harmony. We won't deal with other color formats that require conversion since that is another article itself. We already have our parseHSL function above so we are off to a good start. The key to this function is that we can look at the split complement as the baseHue + 150 and the baseHue + 210. In fact all the multiple color harmonies start at a base value(in this case 150) and make x more colors (in this case 1) at a set interval (in this case 60..gets us to 210😱).

So here we go.

const rp = 'hsl(270, 49%, 40%)'

function harmonize(color, start, end, interval) {
    const colors = [color]
    const [h, s, l] = parseHSL(color)

    for(let i = start; i <= end; i += interval) {
        const h1 = (h + i) % 360
        const c1 = `hsl(${h1}, ${s}%, ${l}%)`
        colors.push(c1)
    }

    return colors
}

const complement = harmonize(rp, 180, 180, 1)
const split = harmonize(rp, 150, 210, 60)
const triad = harmonize(rp, 120, 240, 120)
const tetrad = harmonize(rp, 90, 270, 90)
const analogous = harmonize(rp, 30, 90, 30)
Enter fullscreen mode Exit fullscreen mode

Now that we armed with some code we can adapt it as an extra feature to spruce up a color picker app.

There are all kinds of interesting functionality combining math and color. A lot of it isn't as complicated as it might seem. I have been playing around with the app in the GIF and I am looking to attract contributors to help with testing and improving the macOS version and improve the product overall. Thanks.👌

Oldest comments (2)

Collapse
 
nyancodeid profile image
Ryan Aunur Rassyid

Nice share, thank you

Collapse
 
sysmaya profile image
sysmaya

ERROR

hsl = str.replace(/[^\d,]/g, '').split(',');

No recoignize decimals