
HSLuv, a developer friendly perceptual color space - kuon
https://www.kuon.ch/post/2020-03-08-hsluv/
======
boulos
Interesting! I hadn't heard of HSLuv. The original writeup / blog post [1]
seems more informative though on "why" (hsluv.org is not a very interesting
landing page).

I'm a little sad (disclosure: co-author) that we didn't make oRGB [2] more
popular. Perhaps we should put some source code online?

[1] [https://www.boronine.com/2012/03/26/Color-Spaces-for-
Human-B...](https://www.boronine.com/2012/03/26/Color-Spaces-for-Human-
Beings/)

[2]
[https://www.cs.utah.edu/~bratkova/research/projects/orgb/org...](https://www.cs.utah.edu/~bratkova/research/projects/orgb/orgb.pdf)

~~~
tommysavaria
I was curious to see how oRGB worked in practice so I made some code to test
this, and uploaded to a JS fiddle:
[https://jsfiddle.net/phv7x39q/1/](https://jsfiddle.net/phv7x39q/1/)

It is a very naive implementation, more work could be put in optimizing it.

Once I got around to fiddling with it I understood what the paper explained.
I'm not new to colorspaces or matrix transformations but it is hard sometimes
to grasp the concept from the paper.

~~~
jes5199
I think you have a bug - the rotation is supposed to be in the opposite
direction if the red-green value is below zero. The paper doesn't make that
very clear.

------
leeoniya
> HSLuv is already available for many languages

btw, the JS port is not perf-optimized (it's a straight compilation from haxe
to js).

i hand-ported the C version [1] locally that avoids heap-allocating any
objects or arrays, which makes it about 40% faster. i'm hoping it gets to
replace the "official" js port at some point, but i still need to run it
through all the tests [2]. if anyone is interested in helping out with this
(i'm tight on time right now), i'll publish it.

initial benchmark of 100k random RGB triplets completes in 100ms in Chrome 80
and 60ms in Firefox 73, on a pretty average Core i5-7500T @ 2.70GHz. 1M
triplets is 680ms in chrome and 421ms in firefox. the official JS port gets
934ms in Chrome and 545ms in Firefox for 1M triplets.

For reference, a typical jpeg image contains < 300k unique colors.

[1] [https://github.com/hsluv/hsluv-c](https://github.com/hsluv/hsluv-c)

[2]
[https://github.com/hsluv/hsluv-c/blob/master/tests/scripts/s...](https://github.com/hsluv/hsluv-c/blob/master/tests/scripts/snapshot-
rev4.json)

~~~
tobr
Not sure how much help I would be, but if it’s any encouragement I would very
much appreciate a “real” JS version. I looked at the JS version of HSLUV just
the other week and was bummed that it was compiled from Haxe.

~~~
throwaway3157
> was bummed that it was compiled from Haxe

Why do you need a "real" JS version? Is it that you want to be able to
read/edit from a human-source, rather than a Haxe-compiled source (which I
assume is less legible)?

------
gravitystorm
I use HSLuv for designing our maps at Thunderforest. The map stylesheet
language we use is CartoCSS, and it has built-in support for HSLuv since 2016.

[https://cartocss.readthedocs.io/en/latest/language_elements....](https://cartocss.readthedocs.io/en/latest/language_elements.html#color)

I can really recommend using HSLuv for this kind of thing. I used to use
regular RGB values (like #f2cdaa), but it's hard to make features on the map
"a bit less blue" without accidentally changing other properties. For example,
changing #f2cdaa to #f2cd88 makes it less blue, but also darker.

So a few years ago I switched to using HSL in our stylesheets (and using the
HSL tab in the Inkscape colour picker) which makes it easier to reason about
the colour changes. But there's still some problems with HSL, as the article
describes. For example when choosing road colours, I often want to keep the
saturation and lightness the same, but when I make minor roads yellow the
change in hue really changes the perceived brightness in HSL.

So HSLuv is great for what I do, since I know that if I get the brightness and
saturation of the roads the way I want, I can mess around with the hue without
any side effects. Or if I like the colour of the forests but want them
slightly less saturated, again no side effects when I make changes.

The big drawback is that there aren't many colour pickers available in HSLuv,
mainly just the one on [https://www.hsluv.org/](https://www.hsluv.org/) . I
haven't found e.g. HSLuv colour picker plugins for Inkscape or the GIMP yet.

~~~
jpxw
The "outdoors" demo image on
[https://www.thunderforest.com/](https://www.thunderforest.com/) is literally
2 minutes away from where I grew up!

------
jordansmithnz
Tangential - this reminds me of a tricky bug I once fixed.

I had this code that lets you choose a brighter or darker variant of a color
palette, and initially bright yellow looked awfully close to white, so I
decided to do this conversion through CIELAB (similar to HSLuv). Some
unrelated code was storing these colors in the cloud using hex format, and
clients would sync these colors back and forth with the cloud.

Usually, everything worked great. I had no problems, however a couple of users
started emailing to complain that their colors had ‘disappeared’! As in - the
stored colors had become transparent. I spent at least a few hours auditing
syncing code, but found nothing.

A few weeks later I stumbled on the issue by chance: if you select dark green
(this specific color configuration only), the color would eventually become
transparent.

The thing is - some platforms (e.g. iOS) support wide color, which use RGB
values outside the 0 to 1 range. Green would go through the CIELAB brightness
conversion, and come out with a negative number for one of the RGB values.
This is OK, until you convert it to hex and send it to the cloud for
syncing... naive color -> hex conversion will freak out with negative numbers.
This got sent to the cloud, and later on, the cloud would send this back to
the client, which would use it as a source of truth (and so dark green colors
would disappear).

The fix was to write a less naive hex converter. This was a few years back,
perhaps by now most color -> hex libraries consider this case.

~~~
smohare
This sounds more like a fundamental misunderstanding of color spaces (and
gamuts) than anything.

~~~
jordansmithnz
Do you mind explaining that? If you increase the brightness of a color space
like RGB or HSL, the perceived brightness color increase is different
depending on hue.

This is why CIELAB was used, because like the linked HSLuv color space, you
can increase brightness across hues with the same perceived brightness shift.

The fact that CIELAB can covert to RGB values outside the usual range is fine,
e.g. negative values are fine in wide RGB color space.

~~~
mark-r
Wide RGB color support is almost non-existent.

~~~
jordansmithnz
Every iPhone since the iPhone 7 supports wide color RGB by default...

~~~
mark-r
It depends on how you define "wide color". Yes they use a color space with a
wider gamut, but if you're trying to display a regular JPEG image it won't
look any different.

~~~
jordansmithnz
Yep, most JPEG images might not look different, however the OS allows RGB
values outside the regular range for any color specific API. When working
directly with colors, the wide color support is still relevant (and for me,
led to the bug I described - devices prior to iPhone 7 didn’t exhibit the bug,
because the OS clamped RGB values to the standard range.).

------
Tade0
Somewhat related:

A friend of mine designed a color palette for terminals based on the Solarized
set, but with some improvements:

[https://github.com/jan-warchol/selenized](https://github.com/jan-
warchol/selenized)

------
dllu
I am a big fan of HSLuv and I use it for generating colourschemes for lidar
point clouds:
[https://daniel.lawrence.lu/public/colortransform/#0_2584_964...](https://daniel.lawrence.lu/public/colortransform/#0_2584_964_49_6_2624_722_363_10_3248_906_559_15_415_961_731)

which is used for videos such as this:
[https://www.youtube.com/watch?v=eNt8D1mfwwo](https://www.youtube.com/watch?v=eNt8D1mfwwo)

~~~
pottertheotter
That's really cool! I've always wanted to map my yard and house like that, but
I imagine it would be quite expensive. I'm on a hill and it would make it so
easy to play around with landscaping ideas without having to measure a ton.

Also, it's crazy how easy it is to recognize homes from SF!

~~~
rebuilder
Photogrammetry should be accurate enough for that purpose. Just make sure to
include some reference object for scale.

A drone would probably be the best way to shoot the photos for this.

------
azhenley
I really like this. I’ve been working on a retro game for my students that
only allows for 256 colors. I was planning on using the 216 web safe colors,
but I think using a perfectly spaced out set of 256 colors using HSLuv would
be more logical.

Any other ideas?

~~~
jmiskovic
Web safe colors are completely useless for graphics. Really poor choice of
colors.

256 is far too many to pick from :) People do their best work when they are
under severe constraints. I would recommend a palette that was put together
manually without algorithms because someone made sure that colors actually fit
well together. My go to source for such project is this site:
[https://lospec.com/palette-list/](https://lospec.com/palette-list/)

------
sberder
I'm really not a specialist in color theory but have been looking for a
library that could consistently return colors for multi charts with no defined
number of lines. Most color palettes are a set size (5, 6, 8 colors) but I
currently need to chart things with an unknown number of lines. Any suggestion
from the mind hive?

~~~
kevin_thibedeau
Rotate hues with an increment of phi (1.618...). It maximizes average distance
between colors.

It is discussed at the end of the sinebow article:

[https://basecase.org/env/on-rainbows](https://basecase.org/env/on-rainbows)

~~~
mark-r
I love the Sinebow! It's a natural solution to a problem nobody ever talks
about - the colors made by combining two primaries are unnaturally bright
compared to a single primary. The way our monitors work, those colors emit
twice as many photons. That's OK for yellow because we expect it to be bright,
but magenta and cyan just look too garish.

------
monokai_nl
Colors are hard. Mixing colors is harder. Yesterday I launched a new tool [1]
to automatically create pleasing gradients aimed at web devs.

It uses ideas similar to the ones described in the article. But the hard part
with dark-to-light color gradients is determining what's a pleasing darker or
lighter variant of a color. There's a lot of subjectivity involved. You cannot
objectively determine a certain color that lies "between" two other colors.

Anyway, I hope my tool helps with quickly creating a nice CSS color gradient.

[1] [https://mybrandnewlogo.com/color-gradient-
generator](https://mybrandnewlogo.com/color-gradient-generator)

------
tobr
Maybe someone in this thread would have an answer to this question: how do you
interpolate between two colors in HSLUV? Just interpolating each parameter
doesn’t give the right result, since eg mixing two saturated complementary
colors would create an equally saturated color, rather than some shade of
gray.

Converting to CIELUV and mixing there doesn’t seem safe since I don’t think
it’s a “convex” color space; you run the risk of ending up with colors that
can’t be reproduced properly on a screen.

~~~
anchpop
You almost always want to convert to CIELAB when doing things like averaging
or interpolating colors. It is true that the sRGB gamut in CIELAB is not
convex - this is less of an issue if you can use bigger color spaces like
scRGB but that is often not the case. My advice in this case is to do as much
as you can in LAB and then clamp your output to the range of values supported
by your color space - i.e. if you get RGB(100, 300, -10) convert that to
RGB(100, 255, 0). There are more sophisticated things you can do if you have
to but this situation doesn't come up super often so it's not really worth the
trouble in most cases.

~~~
jfkebwjsbx
Why not convert to linear sRGB (with higher bits per sample) instead?
Something like what PNG supports for 16-bits. Does that give incorrect results
too compared to going through CIELAB?

~~~
anchpop
For interpolation, averaging, etc. anything based on RGB is not going to give
good results. For example, if you average pure red and pure blue, you should
get something like pure purple. But if you do that in RGB you'll get #7f007f,
a much darker color than either pure red or pure blue. If you do it with LAB,
you'll get #ca0088, which is perceptually about as bright as either of the
colors you started with.

~~~
ThJ
It helps to use linear gamma when manipulating RGB values. Linear gamma fixes
most issues with interpolating and mixing RGB values and that’s also why most
modern CG pipelines use it. It produces the most natural results.

~~~
mark-r
Yes linear gamma generates the most natural results, because that's the way
physical light combines. Gamma was a great invention for display and
transmission but it sure wrecked our ability to mix colors.

------
pansa2
Isn’t CIELUV more-or-less deprecated now, in favour of CIELAB?

Is there a reason that HSLuv is based on the former (and isn’t “HSLab”
instead)?

~~~
deltron3030
AB coordinates are a bit unintuitive, it's easier to visualize hues around a
circle and have separate values for luminance and chroma, that's why people
who work with color usually prefer LCH/HCL (Luminance, Chroma, Hue).

Chroma values in LCH differ between Hues, because each Hue has a different
potential and max value. That's where HSLuv comes in, where chroma is a
percentage value and therefore relative to the Hue, instead of an absolute
one. This gives HCL similar usage ergonmics like HSL, thus the name HSLuv.

~~~
pansa2
But it’s possible to construct an LCh color space from either CIELAB or
CIELUV. Why choose the one that’s less widely-used?

~~~
deltron3030
Sure, but chroma is absolute in LCh, which makes it hard to use for color
generation. 100% chroma in HSLuv is the highest value of a particular hue in
LCh.

------
Grustaf
Well written and very interesting article, but I think it would benefit from
being more visually pleasing, especially the palette examples! Seeing that it
deals with aesthetics....

------
0xdeadb00f
Maybe tangential, but what a great looking website!

------
qwerty456127
Green certainly looks darker than pink and blue at some point.

~~~
kuon
Yeah, it's not perfect, and I suspect we don't see colors the same way
(monitor not calibrated the same way, and even our vision might be different),
but it is "good enough" to avoid common pitfalls like having white text on
yellow.

