Color Harmonies in JavaScript
Tags: javascript, electron, canvas, react
Published: 2019-02-28 05:25:57

---
title: Color Harmonies in JavaScript
published: true
description: Short summary of the math i used in a recent color picker
tags: javascript, electron, canvas, react
cover_image: https://thepracticaldev.s3.amazonaws.com/i/q0jyieg71449m5nrqti4.jpeg
---

### Intro

I recently designed a color utility application using **Electron**. [Electron](https://electronjs.org/) 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 benjaminadk/color-tool-remix %}

### 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](https://medium.com/@valgaze/the-hidden-purple-memorial-in-your-web-browser-7d84813bb416). 

![rebeccapurple](https://thepracticaldev.s3.amazonaws.com/i/odwqbpo6dom05ebhld3f.png)

|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](https://thepracticaldev.s3.amazonaws.com/i/dxqwg4bnqg869nvpnroh.png)


### 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. 

```javascript
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
}
```

### 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|![](https://thepracticaldev.s3.amazonaws.com/i/nncdr4ll5z63welpqoq8.png)|![](https://thepracticaldev.s3.amazonaws.com/i/71s6yg0fgwhxruzfpwxg.png)|
|split complementary|![](https://thepracticaldev.s3.amazonaws.com/i/lzyjikce1ll1p24k6yno.png)|![](https://thepracticaldev.s3.amazonaws.com/i/hub5fy6s7yz1a7hqaxbn.png)|
|triadic|![](https://thepracticaldev.s3.amazonaws.com/i/x1vvw1dqg7nen2ft81su.png)|![](https://thepracticaldev.s3.amazonaws.com/i/m7nlm3hp0ozvy8ht6n2h.png)|
|tetradic|![](https://thepracticaldev.s3.amazonaws.com/i/8g9k7d42id0yzl0h545a.png)|![](https://thepracticaldev.s3.amazonaws.com/i/i30lyttqhm1u3tloomvh.png)|
|analogous|![](https://thepracticaldev.s3.amazonaws.com/i/6013q3reymhdlbqhbnd6.png)|![](https://thepracticaldev.s3.amazonaws.com/i/76nfxtwpenlkm06tr1s0.png)|

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. 

```javascript
const complementaryHue = (baseHue + 180) % 360
```  

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.

```javascript
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)
```

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

![](https://camo.githubusercontent.com/5d49cc9403085caf7a6114ee4f2eb3b906045558/68747470733a2f2f73332d75732d776573742d312e616d617a6f6e6177732e636f6d2f636f6c6f722d746f6f6c2d72656d69782f636f6c6f722d746f6f6c2d372e676966)

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.👌