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?
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/
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.
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.
Out of curiosity: when you go full throttle on red yellow, dark <-> light goes from red to orange to yellow. Is that really as it is meant to be? I'd (intuitively) expect to be able to go from orange to brown by adjusting the dark/light dimension.
This is a great paper! Thank you for sharing it. I'm not an expert by any means in color theory, but I found the paper both accessible (great diagrams!) and compelling.
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.
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.
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)?
This is great. I have to admit I usually "bake" my colors when using JS so I have not been hit by the performances, but if it can be done in a performent way, it can open new applications in the browser.
As far as testing goes, can't you just test all the colours? the space is pretty small. It should take something like less than 16 seconds in total per implementation, in each direction.
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/ . I haven't found e.g. HSLuv colour picker plugins for Inkscape or the GIMP yet.
I dislike strongly the look and feel of most Thunderforest maps. It may look "modern" but when you want to actually read a map, having light green symbols on a slightly lighter green background is ridiculous.
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.
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.
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.
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.).
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!
if you have a drone or have a friend with one, it's pretty easy to do this with aerial photogrammetry. Something like dronedeploy.com makes this very easy with a DJI drone.
Draw the area you want to capture as a polygon on google maps in DroneDeploy, deploy the drone, off it goes, takes the photos and lands without you needing to do anything. Then just upload the photos, and you get a 3d model processed on their servers an hour or so later.
That's very cool! Could you explain a bit about what the colors mean? I'm especially intrigued by the painted road markings, which are clearly visible.
The colours are based on lidar intensity, i.e. how much light is bounced back from a surface. This depends on the reflectivity. That's why it can see lane markings and even murals on the wall: https://www.youtube.com/watch?v=a0-JaMGivX4
Ah, thanks. So the colors are an aesthetic choice? There's no more information than one might get from greyscale? (Which is a fine thing; I love the colors.)
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.
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/
If you want a real retro NES feel, consider using NES' original 54-color palette.
It lacks deep red and pale orange (suitable for human skins), so the developer of Shovel Knight added 4 colors to it.
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?
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.
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.
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.
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.
Thank you for the advice! I thought the appeal of HSLUV was that it has distorted CIELUV as little as necessary to map it perfectly onto three predictable parameters. Thus it would never require clamping. I was hoping there would be an official way to convert the hue/saturation to Cartesian coordinates without losing that benefit, but perhaps it wouldn’t be too hard to do myself.
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?
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.
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.
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.
By interpolating, you mean like given two colors, you want to generate the intermediate gradient colors, like on this article[1]? If that's the case, I don't see why interpolating two saturated colors would provide a shade of gray. Can you clarify?
Yeah, basically build a linear (whatever that means in a polar color space) gradient between two colors.
If you interpolate a bright red and bright cyan (complementary colors), the midpoint would be gray in most color spaces. You can try it in your link. But in polar color spaces (like HSLUV and most of the ones starting with H) if you just lerp each of the numbers you have to pick a direction for the hue, and your cyan—red has to pass through a saturated green, yellow or blue, magenta. That rainbow effect can be nice but doesn’t feel linear. It also creates discontinuities when you change a color stop enough.
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.
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.
I've never figured out why LAB and LUV both exist, they were created around the same time to solve the same problem. LAB is definitely more popular though.
CIE recommend the use of LAB for paints/dyes/surfaces, and LUV for displays.
I don't know why. I just know they do.
If I had to guess, it would be that LUV is designed for additive mixing, and LAB is not, but that is a guess educated by some dimly remembered early highschool physics along with 4th grade art lessons.
This is a common misconception; both were recommended because they performed equally well on available data at the time [1]. While CIELUV does have the advantage that additive mixtures fall on straight lines in its chromaticity diagram, it does poorly at predicting color differences and has a flawed adaptation transform, so CIELAB is generally preferred.
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....
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.
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...
[2] https://www.cs.utah.edu/~bratkova/research/projects/orgb/org...