This is a talk put together for the April 2016 DonutJS meetup. You can read it like a blog post or use your ←→ arrow keys to navigate it presentation-like.
The code is at github.com/visnup/hsl.
#ffa500
– that’s orange. #ffd833
– that’s not a lighter orange;
it’s yellow. Of course, you’re thinking, one does not simply add
some GB to RGB to lighten a color.
Ok fine, then how do you lighten orange to get light orange? And wait,
hold on: why can’t I just add Rs, Gs, and Bs to lighten my colors? I
mean, my computer knows 1 + 1 = 2
; it and I can agree on that. Yet
#ffa500
+ #004999
yields some kinda related color, but in a way
that doesn’t exactly make sense.
In what world is that cool? I’ll tell you in what world: in an adding-colors-of-light-together world. You know who lives in that world? LEDs and CRTs, that’s who. And scientists who play with lasers, AKA crazy people.
RGB is not like some single origin, shade-grown, 1,300 meter elevation coffee beans you found in Alajuela, Costa Rica and roasted in small batches to give to your friends in Oakland.
RGB is like that guy in a North Face jacket and hoodie on the 38L Geary headed downtown, doing a line of cocaine off his iPhone. Yeah, RGB is not a “so-uncool-it’s-cool-again” thing.
Three cordless screwdrivers and a microcontroller = perfect small batch coffee roasting. http://t.co/vrEWlzDP9A pic.twitter.com/GbOVyJ1jFh
— Make: (@make) September 15, 2015
Some guy doing lines of cocaine off his iphone on muni... pic.twitter.com/8KgMSljp
— Nolan Evans (@nolman) July 28, 2012
RGB is what we call a color space, which sounds more scary than it really is. I’d define a color space as “a system to specify a color and reason about sets of colors.”
It’s actually really similar to the Cartesian X, Y, Z system to
specify positions in space. So, just like I can say the corner of
the room is { 0, 0, 0 }
and to get to me, you go 20’ along that wall
and 10’ along this other wall, and then another 5’8” upwards to get to
my head, we can do the same thing with color. Instead of X, Y, and Z,
we use R, G, and B.
But what about the case where all I care about is answering “how far away you are from me and in what direction?” If we’re constrainted to XYZ, I have to go back to that corner and figure out your position and then do some vector math to find the distance and angle.
And that’s where polar coordinates come from (if you remember that mind-blowing 6th grade geometry class). It’s just a different way of specifying all the same positions that you could with X,Y, and Z, but re-frames it in a totally different system. And when we shift our perspective that way, some operations become a lot simpler and more intuitive.
How would you describe to the person next to you the color of the sky? How would you compare that to the color of the ocean? How would you compare those colors to each other?
Let’s pretend we were tasked with representing all the colors in an intuitive way. How would we start?
Why not just take the rainbow (ROY G. BIV!) and stick it in a kinda big array:
Let’s divide the rainbow up into colors:
That’s kinda intuitive-looking already. We’ve got lots of the colors we care about in an array indexed from 0 to {{buckets.h}}. So, if I want red I just grab the color at 0 and if I want magenta, I grab the one at {{buckets.h}}. {{buckets.h/3}}ish is green and {{buckets.h*2/3}}ish is blue; it’s just a mapping and as we move up and down the scale we get a predictable color from the rainbow.
We want to have more crayons in our box though, so increase that number to something high, like 99. Now we’re getting a proper rainbow.
So now that we have a box of crayons to work with, what if I don’t want to use like all a color at once? What about those duskier, more washed-out colors we get on a rainy day, like steel blue?
For each color we have in the rainbow, let’s create a scale for “colorfulness”:
Picking from our rainbow,
a hue of , let’s bucket its colorfulness on a scale from 0 to :
Neat! So we’ve got all the colors in the rainbow plus a measure of colorfulness to get us everthing between those colors and gray.
But what happened to black, white?
What if we add one more dimension that is about how much light we have that’s falling on our color?
How much light is falling on our color?
Play with the sliders above and see how different values affect the resulting color.
A few things to look out for:
This should be pretty obvious since we’ve been doing it now a bunch throughout this last section. But just to drive it home, above you can pick whatever hue in HSL and then adjust the lightness value to be darker or lighter to your whims of fancy. This is exactly the problem we wanted to solve in the first place with lightening that orange.
Make me a palette with colors!
So, I know I just told you HSL was like the new Kanye album, but there are some problems with HSL.
Luckily, there are some people who really care about this type of stuff. They call themselves the International Commission on Illumination or if you like French, Commission internationale de l’éclairage, or if you think that’s a mouthful, CIE for short.
They’ve published an even better colour space definition that takes into account how the human eye perceives colour along with what lighting conditions you’re in and how long you’ve been fermenting that kombucha you’re drinking. It’s called CIELAB and it’s crazy.
Created by @visnup and hosted at https://visnup.github.io/hsl.
These slides are a hackery of Pug and Markdown in a (hopefully) entertaining one-file implementation. Presentation logic is ~35 lines of Angular. three.js RGB color space took another ~60 lines.
If that’s interesting to you, come work with me at Opendoor.