lyft / coloralgorithm

Javacript function to produce color sets
Apache License 2.0
231 stars 45 forks source link

Support HSLuv and perceptually uniform hue/staturation #12

Open arniebradfo opened 5 years ago

arniebradfo commented 5 years ago

Color box is super helpful but could be more helpful if you added the option to transform colors along HSLuv interface instead of only HSL. Maybe add a toggle somewhere.

Read more: Perceptually Uniform Color Spaces

Specifying your own cubic bezier would be nice too...

...also publishing this to npm would be nice 😄

Thanks!

Myndex commented 5 years ago

...but could be more helpful if you added the option to transform colors along HSLuv interface instead of only HSL. Maybe add a toggle somewhere. Read more: Perceptually Uniform Color Spaces

Hi @arniebradfo

I'd think it'd be pretty easy to add as this is using the Chroma.js library which includes CIELAB and CIELAB LCh — and to note, CSS4 is going to be including an LAB LCh color method.

BUT: to continue, some perceptually uniform spaces like LAB or LUV may or may not always be what you want for a gradient if you are changing HUE in addition to LUMINANCE. Sometimes sRGB is just fine, others you want hue rotation, etc.

CIELAB is good (ish) for determining the distance between two colors (it is just the euclidian distance because LAB is a cartesian space). But despite perceptual uniformity, the cartesian coordinates of LAB is not always the best choice for gradients. Converting LAB to polar LCh allows for some extra fun things.

I wrote a few long posts on StackExchange (Part 1) and (Part 2) on the subject with examples, such as this:

Screen Shot 2019-05-24 at 7 54 48 PM

Each of the four gradients are equidistant steps of the colorspace they were created in. From left to right:

xyY

First, on xyY, while linear light (no gamma) spaces are good for things like ray tracing and compositing, linear kinda sucks for gradients (as can be seen). While light is linear, color perception isn't, so using linear math on linear light fails to consider the perceptual qualities of human vision when mixing/averaging between color values.

sRGB & LAB

But do notice the similarity between the sRGB and the LAB — the fact is the in-between colors mix well using linear math (averaging the color values) when those colors are encoded in a non-linear (perceptual) space. Because sRGB is gamma encoded, it is not that different from L*.

But ALSO note that near the middle, the saturation goes away and the gradient becomes GREY. In the case of LAB, this is because the center vertical axis is LIGHTNESS and minimum chroma.

LCh

The last column is the more fun one - the perceptually uniform LAB but converted to Lightness, Chroma, hue. In LAB LCh, the chroma is a specific distance from the center Y axis. So if you maintain constant chroma, then average the L and the HUE, you will get gradients that maintain perceptually equal saturation, while blending between hue and L in equal steps.

An you can reverse the hue direction of course. Here the left is clockwise right is CCW.

Screen Shot 2019-05-25 at 12 59 10 PM

Reversing hue rotation is most useful as the colors get closer to 180° apart.

In this example the sRGB version gets too dark in the middle. The very saturated nature of the start and end favors the hue rotation method of LCh

Screen Shot 2019-05-24 at 9 22 50 PM

And a final example, both sRGB and L* get gray in the middle, though the colors are desaturated it seems natural — here the LCh version may be too colorful.

Screen Shot 2019-06-12 at 11 16 28 PM

Adding a chroma control to adjust the midrange saturation might help. But the point really is that no one colorspace or method is necessarily best for all purposes, more than it's useful to have a wide range of choices for the many different results they generate.

-Andy