🌬️ 🍂 Gone with the wind
Published on

A failed (and somewhat naive) attempt to write a color manipulation library

Authors
  • avatar
    Name
    ディーン・タリサイ
    Twitter
    @prjctimg

NOTE

Update 28 June 2025

My very first attempt to build something reusable, this color manipulation library made me fall in love with building free software. It wasn't perfect or shiny but it was my first. That alone has enough sentimental weight to make me cherish its legacy.

The first iterations of the project were a bit crude and redundant, I had less than a year's experience in writing JavaScript (or programming in general) back then and honestly didn't know better. I thought that more lines of code meant my function or module was so cool and wrote my code to be unreadable intentionally.

I have come a long way since then.

This post's headings are not ordered in any way.

The best way to learn about how something works is by building small tools to explore focused problems and then piece together the small solutions into a unified whole. I have long been interested in image processing and what better way to get your feet wet by first understanding the components of an image, like color.

Standalone functions or the builder pattern ?

Having previously played around with a few similar projects in the past, I had noticed that they exposed some sort of Color class which took a color token as it's first argument. This allowed me to chain operations until you was ready to call the final output. I also included an implicitReturn boolean argument that allowed me to get the final output from the end of the chain without needing to explicitly call the output() method.

This is done possible by returning the reference to the host object itself (this) which allows you to access the other functions on the prototype chain. To get the value we simply do an output() call at the end of the chain.

So I declared two "wrapper" classes; Color and ColorArray that had all the functions that take a single color token and collection of color tokens as their first arguments respectively:



// Color class wrapper example

There were scenarios were a function returned a collection of colors yet was under the Color class and vice versa. The final idea was to bind the returned collection to ColorArray, which allows the user to use the collection methods directly from the Color class.

Grouping the functions

It was obvious that the functions could be put into distinct each modules, the idea behind this was to unify parameter signatures for functions in the same module. This also made it easier to remove ambiguous and repetitive code by combining somewhat similar routines together and then allowing the function behavior t o

Now that I had these decisions in place, I needed a proper problem description.

What is a color ?

A "color token" is any value that can be mapped to a valid (or within gamut) color in the targeted color space. Color is specified in different formats depending on the API you're consuming color data from. A good example is the nested array of RGB channel values returned by the HTML Canvas's getImageData() method.

Below is a list of the accepted color tokens:

Type safe color token parser

NOTE

The code for token() was inspired from similar projects, such as chroma.js which has support for parsing color from arrays amongst other types. Color conversion is handled by Culori under the hood.

Imagine you wish to parse color in any given format, this includes every JavaScript value.

import { token } from '@prjctimg/huetiful'

let cssNamedColor = 'pink'
let colorNumber = 5000
let colorObject = { l: 50, c: 20, h: 40, mode: 'lch' }
let colorObjectWithAlpha = { l: 50, c: 20, h: 40, alpha: 0.5, mode: 'lch' }
let arrColor = ['rgb', 120, 80, 50]
let arrColorWithAlpha = ['rgb', 120, 80, 50, 0.1]

let allColors = [
  cssNamedColor,
  colorNumber,
  colorObject,
  colorObjectWithAlpha,
  arrColor,
  arrColorWithAlpha,
]

let res = []
for (const color of allColors) 
	res.push(token(color));

console.log(res)

// ['#ffc0cb','#001388','#956d62','#956d6280','#785032',#7850321a]

Open source color maps

Palette generation takes a lot of effort to be done right, lucky enough for us,we have lots of open source projects on the matter such as TailwindCSS and ColorBrewer. By wrapping the color maps in functions it becomes easier to reuse.

These can be useful for mapping out to things like, for example, SVG or anything that can use the <color> CSS data type.

"Color theory inspired" predicate functions

There are certain things that we can do easily, by just looking at a color we can tell if it is dark or light, pale or saturated. However, telling the computer the difference between colors requires some assumptions of what a valid color is supposed to look like.

For example we have two types of colors, achromatic (gray) and chromatic (colorful) ones. We can check if a color is achromatic or not by testing for the value of the chroma channel. Since the hue channel depends on the chroma channel to be true, any color with a false (0, undefined,NaN) chroma channel is achromatic.

In the case of black and white, I assume that they're NOT achromatic because these two are dependent on either extremum of the lightness channel alone.

But what about color temperature ? On this part I admit the approach was to naive (but at least it's based on some assumption 🥲). Using a color chat from , I created a map of ranges for each hue family where the warm and cool variants are found, it may not be as accurate but for design purposes it should suffice (I think 👀).

Working with collections of color tokens

It's common to work with collections of data in programming, I needed some functions that mapped a useful routine over these collections. I liked the way lodash handled collection and array manipulation and wanted a few functions that built from that approach.

  • Filter colors that didn't match a specified criteria for example, colors not within the 120 to 180 hue angle range or below a relative contrast of i.e 12.



  • Distribute factor(s) over the collection, that is, take the candidate color token's specified factors and share them among the rest of the tokens.

Naive palette generators

There's a lot of color schemes out there but sometimes we want the logic behind their creation rather than pre-computed/static values. Maybe we want to keep the palette dynamic and be able to create color scales from even a single base color:

Conclusion

I have yet to really use this project in a real world application and in my head it feels like a WIP but I like to think it will be (somewhat) useful in my future creative coding projects. Time will tell.

The code is open source and free for anyone to use and modify (GNU GPL-3.0) here on GitHub.