HUSL is a human-friendly alternative to the HSL color space. HSL was designed back in the 70s to be computationally cheap. It is a clever geometric transformation of the RGB color space and it does not take into account the complexities of human color vision.
There have long existed color spaces designed for perceptual uniformity. One of these color spaces is CIELUV (and its cylindrically shaped brother CIE LChuv). Like HSL, it defines hue and lightness, but instead of saturation it defines chroma. The problem with its chroma component is that it doesn't fit into a specific range. This makes it very hard to define colors programmatically. HUSL is a modified version of the CIE LChuv color space with a new saturation component.
| HSL | CIE LChuv | HUSL | HUSLp | |
|---|---|---|---|---|
| Lightness | Relative | Absolute | Absolute | Absolute |
| Saturation* | Relative | Absolute | Relative | Absolute |
| Hue uniformity | Poor | Good | Good | Good |
| Component ranges | Defined | Undefined | Defined | Defined |
| Saturated colors | Yes | Yes | Yes | No |
| * Saturation and chroma are actually distinct concepts | ||||
The chroma component of CIE LChuv is absolute. Unlike HSL's saturation you can effectively use it to compare two different colors. The dips in the graph represent impossible colors (such as dark saturated yellow). CIE LChuv doesn't warn you about them, making it unsuitable for generating colors.
HUSL preserves the lightness and hue components of CIE LChuv and stretches its chroma vertically so that every color has the same range, defined as a percentage.
HUSLp takes as many colors as it can from CIE LChuv without distorting the chroma. As you can see, the resulting color space is smooth, but only "pastel" colors can be included.
Both HSL and HUSL have a distorted chroma map, this is a trade-off of their convenient shape. HUSL's chroma has more sudden shifts, perhaps it can be improved with a better scaling function. Fork my code!
Have you ever tried to use HSL to work out contrast? Define background and text colors? I know I have, with horrible results.
This is why. Two different colors with the same lightness as defined by HSL can actually have very different lightness. For this demo I am using CIE's definition of lightness. Both CIE LChuv and HUSL use this component, so their picture would be an even grey. This definition of lightness is based on experimental results, you will find that it works very well.
Let's try to generate some random background colors:
function randomHue() {
return Math.floor(Math.random() * 360);
}
// HUSL demo
$(...).css('background-color', $.husl.husl(randomHue(), 90, 60));
// HSL demo
$(...).css('background-color', hslToHex(randomHue(), 90, 60));
HUSL's uniform hue means random colors will be truly random. Iterating over colors will also produce better results.
// HUSL demo
$('#rainbow-husl div').each(function(index) {
$(this).css('background-color', $.husl.husl(index * 36, 90, 60));
});
// HSL demo
$('#rainbow-hsl div').each(function(index) {
$(this).css('background-color', hslToHex(index * 36, 90, 60));
});
HUSL is a work in progress. It works well already, but has some problems too. The project uses semantic versioning, so any backwards-incompatible change will be marked clearly (by incrementing the x in x.y.z). I am not an expert on colors, so I greatly welcome anyone's insight. You can fork this project on GitHub or talk to me via email. The code is released under the MIT license, the math behind it is in the public domain.
I think HUSL could benefit from a better scaling function, I haven't yet found one that is simple and would work well. Another option for further development is to use CIECAM02 as the base color space, since it is more recent that CIELUV.
HUSL is defined as a conversion to and from CIE LChuv. This conversion is a two step process.
HUSL:
Finding the maximum chroma involves building three functions that take CIE LChuv's components as input and return RGB channel values, one for each channel. We equate each to 1 and 0, and solve the 6 resulting equations for C. When we plot the 6 solutions with a fixed lightness and hue for x, we get the picture on the right. As expected, it looks like CIE LChuv.
HUSLp:
To find the extrema we differentiate the curves.
I used Maxima to build and solve these equations, and a wxMaxima file is bundled with the code.
HUSL is written in CoffeeScript, you can use it server-side with npm install husl or client-side as a jQuery plugin ($.husl).
Use husl.p.toHex, husl.p.toRGB, husl.p.fromHex and husl.p.fromRGB for the "pastel" variant.
HUSL can also be used as a Stylus plugin. To use from command line, run stylus like this:
stylus < input.styl > output.css -u /path/to/husl.js
To use programmatically, do this:
var husl = require('husl');
app.use(stylus.middleware({
src: __dirname + '/app/views',
dest: __dirname + '/public',
compile: function(str, path) {
return stylus(str)
.set('filename', path);
.use(husl());
}
});
Now you can use HUSL in your stylesheets!
.tinted(hue)
background-color husl(hue, 90, 80)
color husl(hue, 90, 10)