In this article I will try to explain the math behind converting RGB values to HSL and back.
RGB is a way to describe a color in a cube, where Red, Green and Blue are on the different axis.
HSL is another way to describe a color. Here we use Hue (the angle on the color wheel), Saturation (the amount of color/chroma) and Lightness (how bright the color is).
In HSL it’s described like H=193° S=67% L=28%. This tells us it’s quite saturated and dark. When we look at the color wheel we see that 193° is very close to cyan.
So how is this conversion done?
RGB – HSL
Convert the RGB values to the range 0-1, this can be done by dividing the value by 255 for 8-
bit color depth
R = 24 / 255 = 0.09
G = 98 / 255 = 0.38
B = 118 / 255 = 0.46
Find the minimum and maximum values of R, G and B.
min = 0.09 (the R value)
max = 0.46 (the B value)
Now calculate the Luminace value by adding the max and min values and
divide by 2.
L = (0.09 + 0.46)/2 = 0.275 which rounded up is equal to 28%
The next step is to find the Saturation.
If the min and max value are the same, it means that there is no saturation. If all RGB values are equal you have a shade of grey. Depending on how bright it’s somewhere between black and white. If there is no Saturation, we don’t need to calculate the Hue. So we set it to 0 degrees.
But in our case min and max are not equal which means there is Saturation.
Now we know that there is Saturation we need to do check the level of the Luminance in order to select the correct formula.
If Luminance is less or equal to 0.5, then Saturation = (max-min)/(max+min)
If Luminance is bigger then 0.5. then Saturation = ( max-min)/(2.0-max-min)
In our case Luminance is smaller then 0.5, so we use the first formula.
S = (0.46 – 0.09) / (0.46 + 0.09) = 0.37 / 0.55 = 0.672 which is rounded down equal to 67%
Two done, one to go. We still need to calculate the Hue.
The Hue formula is depending on what RGB color channel is the max value. The three different formulas are:
If Red is max, then Hue = (G-B)/(max-min)
If Green is max, then Hue = 2.0 + (B-R)/(max-min)
If Blue is max, then Hue = 4.0 + (R-G)/(max-min)
The Hue value you get needs to be multiplied by 60 to convert it to degrees on the color circle
If Hue becomes negative you need to add 360 to, because a circle has 360 degrees.
Blue is our max, we have to use the third formula.
H = 4.0 + (0.09 – 0.38) / (0.46 – 0.09) = 4.0 + (-0.29)/0.37 = 3.21
Now convert it to degrees
H = 3.21 x 60 = 192.9 which rounded up is 193 degrees
This is how simple it is to calculate HSL from RGB, what about the other way around?
HSL – RGB
If there is no Saturation it means that it’s a shade of grey. So in that case we just need to convert the Luminance and set R,G and B to that level.
For example H = 0, S = 0 and L = 40%, we get 0.4 * 255 = 102, so R = 102, G = 102 and B = 102
In our case S = 67% which means we have saturation. In that case we need to go ahead.
We need to create some temporary variables. The variables are used to store temporary values which makes the formulas easier to read.
There are two formulas to choose from in the first step.
If Luminance is smaller then 0.5 (50%) then temporary_1 = Luminance x (1.0+Saturation)
If Luminance is equal or larger then 0.5 (50%) then temporary_1 = Luminance + Saturation – Luminance x Saturation
Our Luminance is 28%, so we use the first formula.
temporary_1 = 0.28 x (1.0 +0.67) = 0.28 x 1.67 = 0.4676
We need one more temporary variable, temporary_2
temporary_2 = 2 x Luminance – temporary _1
temporary_2 = 2 x 0.28 – 0.4676 = 0.0924
The next step is to convert the 360 degrees in a circle to 1 by dividing the angle by 360.
Hue = 193/360 = 0.536
And now we need another temporary variable for each color channel, temporary_R, temporary_G and temporary_B.
temporary_R = Hue + 0.333 = 0.536 + 0.333 = 0.869
temporary_G = Hue = 0.536
temporary_B = Hue – 0.333 = 0.536 – 0.333 = 0.203
All values need to be between 0 and 1. In our case all the values are between 0 and 1
If you get a negative value you need to add 1 to it.
If you get a value above 1 you need to subtract 1 from it.
Now we need to do up to 3 tests to select the correct formula for each color channel. Let’s start with Red.
test 1 – If 6 x temporary_R is smaller then 1, Red = temporary_2 + (temporary_1 – temporary_2) x 6 x temporary_R
In the case the first test is larger then 1 check the following
test 2 – If 2 x temporary_R is smaller then 1, Red = temporary_1
In the case the second test also is larger then 1 do the following
test 3 – If 3 x temporary_R is smaller then 2, Red = temporary_2 + (temporary_1 – temporary_2) x (0.666 – temporary_R) x 6
In the case the third test also is larger then 2 you do the following
Red = temporary_2
Ok lets do it for our Red value
6 x temporary_R = 6 x 0.869 = 5.214, so it’s larger then 1, we need to do test 2
2 x temporary_R = 2 x 0.869 = 1.738, it’s also larger then 1, we need to do test 3
3 x temporary_R = 3 x 0.869 = 2.607, it’s larger then 2, so we go for the last formula
Red = temporary_2 = 0.0924 which rounded down is 0.09, which is a number we recognize from the RGB to HSL conversion
Now let’s do Green
6 x temporary_G = 6 x 0.536 = 3.216, to large
2 x 0.536 = 1.072, also to large
3 x 0.536 = 1.608, which is below 2, so
Green = temporary_2 + (temporary_1 – temporary_2) x (0.666 – temporary_G) x 6
Green = 0.0924 + (0.4676 – 0.0924) x (0.666 – 0.536) x 6 = 0.0924 + 0.3752 x 0.13 x 6 = 0.3850 which rounded down is equal to 0.38, another number we recognize.
And finally Blue
6 x temporary_B = 6 x 0.203 = 1,218, to large
2 x 0.203 = 0.406, below one, so we go with this formula
Blue = temporary_1
Blue = 0.4676, which rounded down is 0.46, and a another number we know.
Convert them to 8-bit by multiply them with 255 and we are done
R = 0.0924 x 255 = 23,56 round up to 24
G = 0.3850 x 255 = 98.17 round down to 98
B = 0.4676 x 255 = 119,23 round down to 119, this is not 118 because we rounded the numbers when converting from RGB to HSL. It shows that when you do conversions you need to be as accurate as possible and not round the numbers.
© 2011 Nikolai Waldman