A digital color wheel consists of a mix of the colors red, green and blue (RGB) with varying intensity for each color. It is an additive color model for light waves, where the more color you add, the closer you get to white. (The CMYK model is subtractive) The scale for each RGB color is between 0 (black) and 255 (white). The RGB color combinations can be adjusted further in terms of shade, tint, tones, and hue.
The seven major color schemes or pallets are monochromatic, analogous, complementary, split complementary, triadic, square, and tetradic (rectangle). These are different presentation of the color possibilities that are commonly presented to a user to aid in color selection.
A color mixed with white is called a tint or a pastel. A color mixed with black is a shade. A color mixed with gray is a tone.
The RGB color format can be converted to a cylindrical coordinate system known as HSL for hue, stauration, and luminance. Hue is basically the color on the color (hue) wheel with RGB being the primary colors. The saturation is the intensity or purity of the color, varying from 100% transparent and 0% saturation to 0% transparent and 100% saturation. Luminance is the luminosity or brightness varying between full shadow / 0% brightness (black) and full light / 100% brightness.
Saturation and luminance affect each other, resolving to a greyscale. White is 0% saturation and 100% luminance. Gray is 0% saturation combined with various levels of luminance. Black is 0% luminance and is not influenced by saturation.
Math behind colorspace conversions, RGB-HSL
/*
RGB / HSL conversions
Inspired and derived from:
https://www.niwa.nu/2013/05/math-behind-colorspace-conversions-rgb-hsl/
Written by: Mark W Kiehl
http://mechatronicsolutionsllc.com/
http://www.savvysolutions.info/savvycodesolutions/
MIT License
Copyright (c) 2023 Mark W Kiehl / Mechatronic Solutions LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
typedef struct HSL_t {
double h; // angle in degrees
double s; // saturation % between 0 and 1
double l; // luminance % between 0 and 1
};
typedef struct RGB_t {
int r; // red, 0 to 255
int g; // green, 0 to 255
int b; // blue, 0 to 255
};
HSL_t rgb2hsl(uint8_t red, uint8_t green, uint8_t blue) {
// Returns HSL value calculated from red, green, blue values where:
// h = hue in degrees, 0 to 360
// s = saturation, 0.0 to 1.0
// l = luminance, 0.0 to 1.0
HSL_t result;
// Scale red, blue, green from integer range 0-255 to float 0-1
double r = static_cast<double>(red / 255.0);
double g = static_cast<double>(green / 255.0);
double b = static_cast<double>(blue / 255.0);
float c_max = max(max(r,g),b);
float c_min = min(min(r,g),b);
// Luminance
result.l = (c_max + c_min) / 2.0;
// Saturation
if (c_max == c_min) {
// No saturation, achromatic
result.h = 0.0;
result.s = 0.0;
return result;
} else {
if (result.l > 0.5) {
// Saturation = ( max-min)/(2.0-max-min)
result.s = (c_max - c_min) / (2.0 - c_max - c_min);
} else {
// Saturation = (max-min)/(max+min)
result.s = (c_max - c_min) / (c_max + c_min);
}
}
// Hue
if (r == max(max(r,g),b)) {
// red is the maximum value
result.h = (g - b) / (c_max - c_min);
} else if (g == max(max(r,g),b)) {
// green is the maximum value
result.h = 2.0 + (b - r) / (c_max - c_min);
} else if (b == max(max(r,g),b)) {
// blue is the maximum value
result.h = 4.0 + (r - g) / (c_max - c_min);
} else {
// ERROR
result.h = 0.0;
}
result.h *= 60.0;
if (result.h < 0) {
result.h += 360.0;
}
return result;
} //rgb2hsl()
RGB_t hsl2rgb(double h, double s, double l) {
RGB_t result;
float t_1 = 0.0;
float t_2 = 0.0;
float t_r = 0.0;
float t_g = 0.0;
float t_b = 0.0;
if (h == 0.0 && s == 0.0) {
// No saturation, so it is a shade of gray.
// Covert the luminance and set r, g, b to that level.
result.r = static_cast<int>(l * 255.0);
result.g = static_cast<int>(l * 255.0);
result.b = static_cast<int>(l * 255.0);
} else {
if (l < 0.5) {
t_1 = l * (1.0 + s);
} else {
t_1 = l + s - l * s;
}
t_2 = 2.0 * l - t_1;
// convert hue in degress to 1 by diving by 360 degrees
h /= 360.0;
t_r = h + (1.0/3.0);
if (t_r < 0.0) {
t_r += 1.0;
}
if (t_r > 1.0) {
t_r -= 1.0;
}
t_g = h;
if (t_g < 0.0) {
t_g += 1.0;
}
if (t_g > 1.0) {
t_g -= 1.0;
}
t_b = h - (1.0/3.0);
if (t_b < 0.0) {
t_b += 1.0;
}
if (t_b > 1.0) {
t_b -= 1.0;
}
// red
if (6.0*t_r < 1.0) {
result.r = static_cast<int>(round((t_2 + (t_1 - t_2) * 6.0 * t_r) * 255.0));
} else if (2.0*t_r < 1.0) {
result.r = static_cast<int>(round(t_1 * 255.0));
} else if (3.0*t_r < 2.0) {
result.r = static_cast<int>(round((t_2 + (t_1 - t_2) * (2.0/3.0 - t_r) * 6.0) * 255.0));
} else if (3.0*t_r > 2.0) {
result.r = static_cast<int>(round(t_2 * 255.0));
}
// green
if (6.0*t_g < 1.0) {
result.g = static_cast<int>(round((t_2 + (t_1 - t_2) * 6.0 * t_g) * 255.0));
} else if (2.0*t_g < 1.0) {
result.g = static_cast<int>(round(t_1 * 255.0));
} else if (3.0*t_g < 2.0) {
result.g = static_cast<int>(round((t_2 + (t_1 - t_2) * (2.0/3.0 - t_g) * 6.0) * 255.0));
} else if (3.0*t_g > 2.0) {
result.g = static_cast<int>(round(t_2 * 255.0));
}
// blue
if (6.0*t_b < 1.0) {
result.b = static_cast<int>(round((t_2 + (t_1 - t_2) * 6.0 * t_b) * 255.0));
} else if (2.0*t_b < 1.0) {
result.b = static_cast<int>(round(t_1 * 255.0));
} else if (3.0*t_b < 2.0) {
result.b = static_cast<int>(round((t_2 + (t_1 - t_2) * (2.0/3.0 - t_b) * 6.0) * 255.0));
} else if (3.0*t_b > 2.0) {
result.b = static_cast<int>(round(t_2 * 255.0));
}
}
return result;
} // hsl2rgb()
void setup() {
delay(2000);
Serial.begin(115200);
while (!Serial) {
delay(1);
}
Serial.println("\nSerial ready");
delay(1000);
/*
int r = 24; int g = 98; int b = 118;
HSL_t hsl = rgb2hsl(r, g, b);
Serial.print("r = "); Serial.print(r); Serial.print("\tg = "); Serial.print(g); Serial.print("\tb = "); Serial.println(b);
Serial.print("hsl.h = "); Serial.println(hsl.h);
Serial.print("hsl.s = "); Serial.println(hsl.s);
Serial.print("hsl.l = "); Serial.println(hsl.l);
Serial.println("");
RGB_t rgb = hsl2rgb(hsl.h, hsl.s, hsl.l);
Serial.print("h = "); Serial.print(hsl.h); Serial.print("\ts = "); Serial.print(hsl.s); Serial.print("\tl = "); Serial.println(hsl.l);
Serial.print("rgb.r = "); Serial.println(rgb.r);
Serial.print("rgb.g = "); Serial.println(rgb.g);
Serial.print("rgb.b = "); Serial.println(rgb.b);
Serial.println("");
Serial.println("----------------------------");
r = 255; g = 128; b = 5;
hsl = rgb2hsl(r, g, b);
Serial.print("r = "); Serial.print(r); Serial.print("\tg = "); Serial.print(g); Serial.print("\tb = "); Serial.println(b);
Serial.print("my_hs1.h = "); Serial.println(hsl.h);
Serial.print("my_hs1.s = "); Serial.println(hsl.s);
Serial.print("my_hs1.l = "); Serial.println(hsl.l);
Serial.println("");
rgb = hsl2rgb(hsl.h, hsl.s, hsl.l);
Serial.print("h = "); Serial.print(hsl.h); Serial.print("\ts = "); Serial.print(hsl.s); Serial.print("\tl = "); Serial.println(hsl.l);
Serial.print("rgb.r = "); Serial.println(rgb.r);
Serial.print("rgb.g = "); Serial.println(rgb.g);
Serial.print("rgb.b = "); Serial.println(rgb.b);
Serial.println("");
Serial.println("----------------------------");
// red
r = 255; g = 0; b = 0;
hsl = rgb2hsl(r, g, b);
Serial.print("r = "); Serial.print(r); Serial.print("\tg = "); Serial.print(g); Serial.print("\tb = "); Serial.println(b);
Serial.print("my_hs1.h = "); Serial.println(hsl.h);
Serial.print("my_hs1.s = "); Serial.println(hsl.s);
Serial.print("my_hs1.l = "); Serial.println(hsl.l);
Serial.println("");
rgb = hsl2rgb(hsl.h, hsl.s, hsl.l);
Serial.print("h = "); Serial.print(hsl.h); Serial.print("\ts = "); Serial.print(hsl.s); Serial.print("\tl = "); Serial.println(hsl.l);
Serial.print("rgb.r = "); Serial.println(rgb.r);
Serial.print("rgb.g = "); Serial.println(rgb.g);
Serial.print("rgb.b = "); Serial.println(rgb.b);
Serial.println("");
Serial.println("----------------------------");
// green
r = 0; g = 255; b = 0;
hsl = rgb2hsl(r, g, b);
Serial.print("r = "); Serial.print(r); Serial.print("\tg = "); Serial.print(g); Serial.print("\tb = "); Serial.println(b);
Serial.print("my_hs1.h = "); Serial.println(hsl.h);
Serial.print("my_hs1.s = "); Serial.println(hsl.s);
Serial.print("my_hs1.l = "); Serial.println(hsl.l);
Serial.println("");
rgb = hsl2rgb(hsl.h, hsl.s, hsl.l);
Serial.print("h = "); Serial.print(hsl.h); Serial.print("\ts = "); Serial.print(hsl.s); Serial.print("\tl = "); Serial.println(hsl.l);
Serial.print("rgb.r = "); Serial.println(rgb.r);
Serial.print("rgb.g = "); Serial.println(rgb.g);
Serial.print("rgb.b = "); Serial.println(rgb.b);
Serial.println("");
Serial.println("----------------------------");
// blue
r = 0; g = 0; b = 255;
hsl = rgb2hsl(r, g, b);
Serial.print("r = "); Serial.print(r); Serial.print("\tg = "); Serial.print(g); Serial.print("\tb = "); Serial.println(b);
Serial.print("my_hs1.h = "); Serial.println(hsl.h);
Serial.print("my_hs1.s = "); Serial.println(hsl.s);
Serial.print("my_hs1.l = "); Serial.println(hsl.l);
Serial.println("");
rgb = hsl2rgb(hsl.h, hsl.s, hsl.l);
Serial.print("h = "); Serial.print(hsl.h); Serial.print("\ts = "); Serial.print(hsl.s); Serial.print("\tl = "); Serial.println(hsl.l);
Serial.print("rgb.r = "); Serial.println(rgb.r);
Serial.print("rgb.g = "); Serial.println(rgb.g);
Serial.print("rgb.b = "); Serial.println(rgb.b);
Serial.println("");
Serial.println("----------------------------");
// yellow
r = 255; g = 255; b = 0;
hsl = rgb2hsl(r, g, b);
Serial.print("r = "); Serial.print(r); Serial.print("\tg = "); Serial.print(g); Serial.print("\tb = "); Serial.println(b);
Serial.print("my_hs1.h = "); Serial.println(hsl.h);
Serial.print("my_hs1.s = "); Serial.println(hsl.s);
Serial.print("my_hs1.l = "); Serial.println(hsl.l);
Serial.println("");
rgb = hsl2rgb(hsl.h, hsl.s, hsl.l);
Serial.print("h = "); Serial.print(hsl.h); Serial.print("\ts = "); Serial.print(hsl.s); Serial.print("\tl = "); Serial.println(hsl.l);
Serial.print("rgb.r = "); Serial.println(rgb.r);
Serial.print("rgb.g = "); Serial.println(rgb.g);
Serial.print("rgb.b = "); Serial.println(rgb.b);
Serial.println("");
Serial.println("----------------------------");
// magenta
r = 255; g = 0; b = 255;
hsl = rgb2hsl(r, g, b);
Serial.print("r = "); Serial.print(r); Serial.print("\tg = "); Serial.print(g); Serial.print("\tb = "); Serial.println(b);
Serial.print("my_hs1.h = "); Serial.println(hsl.h);
Serial.print("my_hs1.s = "); Serial.println(hsl.s);
Serial.print("my_hs1.l = "); Serial.println(hsl.l);
Serial.println("");
rgb = hsl2rgb(hsl.h, hsl.s, hsl.l);
Serial.print("h = "); Serial.print(hsl.h); Serial.print("\ts = "); Serial.print(hsl.s); Serial.print("\tl = "); Serial.println(hsl.l);
Serial.print("rgb.r = "); Serial.println(rgb.r);
Serial.print("rgb.g = "); Serial.println(rgb.g);
Serial.print("rgb.b = "); Serial.println(rgb.b);
Serial.println("");
Serial.println("----------------------------");
// cyan
r = 0; g = 255; b = 255;
hsl = rgb2hsl(r, g, b);
Serial.print("r = "); Serial.print(r); Serial.print("\tg = "); Serial.print(g); Serial.print("\tb = "); Serial.println(b);
Serial.print("my_hs1.h = "); Serial.println(hsl.h);
Serial.print("my_hs1.s = "); Serial.println(hsl.s);
Serial.print("my_hs1.l = "); Serial.println(hsl.l);
Serial.println("");
rgb = hsl2rgb(hsl.h, hsl.s, hsl.l);
Serial.print("h = "); Serial.print(hsl.h); Serial.print("\ts = "); Serial.print(hsl.s); Serial.print("\tl = "); Serial.println(hsl.l);
Serial.print("rgb.r = "); Serial.println(rgb.r);
Serial.print("rgb.g = "); Serial.println(rgb.g);
Serial.print("rgb.b = "); Serial.println(rgb.b);
Serial.println("");
Serial.println("----------------------------");
// dark red
r = 128; g = 0; b = 0;
hsl = rgb2hsl(r, g, b);
Serial.print("r = "); Serial.print(r); Serial.print("\tg = "); Serial.print(g); Serial.print("\tb = "); Serial.println(b);
Serial.print("my_hs1.h = "); Serial.println(hsl.h);
Serial.print("my_hs1.s = "); Serial.println(hsl.s);
Serial.print("my_hs1.l = "); Serial.println(hsl.l);
Serial.println("");
rgb = hsl2rgb(hsl.h, hsl.s, hsl.l);
Serial.print("h = "); Serial.print(hsl.h); Serial.print("\ts = "); Serial.print(hsl.s); Serial.print("\tl = "); Serial.println(hsl.l);
Serial.print("rgb.r = "); Serial.println(rgb.r);
Serial.print("rgb.g = "); Serial.println(rgb.g);
Serial.print("rgb.b = "); Serial.println(rgb.b);
Serial.println("");
Serial.println("----------------------------");
// pink
r = 255; g = 128; b = 192;
hsl = rgb2hsl(r, g, b);
Serial.print("r = "); Serial.print(r); Serial.print("\tg = "); Serial.print(g); Serial.print("\tb = "); Serial.println(b);
Serial.print("my_hs1.h = "); Serial.println(hsl.h);
Serial.print("my_hs1.s = "); Serial.println(hsl.s);
Serial.print("my_hs1.l = "); Serial.println(hsl.l);
Serial.println("");
rgb = hsl2rgb(hsl.h, hsl.s, hsl.l);
Serial.print("h = "); Serial.print(hsl.h); Serial.print("\ts = "); Serial.print(hsl.s); Serial.print("\tl = "); Serial.println(hsl.l);
Serial.print("rgb.r = "); Serial.println(rgb.r);
Serial.print("rgb.g = "); Serial.println(rgb.g);
Serial.print("rgb.b = "); Serial.println(rgb.b);
Serial.println("");
Serial.println("----------------------------");
// very dark gray
r = 16; g = 16; b = 16;
hsl = rgb2hsl(r, g, b);
Serial.print("r = "); Serial.print(r); Serial.print("\tg = "); Serial.print(g); Serial.print("\tb = "); Serial.println(b);
Serial.print("my_hs1.h = "); Serial.println(hsl.h);
Serial.print("my_hs1.s = "); Serial.println(hsl.s);
Serial.print("my_hs1.l = "); Serial.println(hsl.l);
Serial.println("");
rgb = hsl2rgb(hsl.h, hsl.s, hsl.l);
Serial.print("h = "); Serial.print(hsl.h); Serial.print("\ts = "); Serial.print(hsl.s); Serial.print("\tl = "); Serial.println(hsl.l);
Serial.print("rgb.r = "); Serial.println(rgb.r);
Serial.print("rgb.g = "); Serial.println(rgb.g);
Serial.print("rgb.b = "); Serial.println(rgb.b);
Serial.println("");
Serial.println("----------------------------");
// dark grey
r = 64; g = 64; b = 64;
hsl = rgb2hsl(r, g, b);
Serial.print("r = "); Serial.print(r); Serial.print("\tg = "); Serial.print(g); Serial.print("\tb = "); Serial.println(b);
Serial.print("my_hs1.h = "); Serial.println(hsl.h);
Serial.print("my_hs1.s = "); Serial.println(hsl.s);
Serial.print("my_hs1.l = "); Serial.println(hsl.l);
Serial.println("");
rgb = hsl2rgb(hsl.h, hsl.s, hsl.l);
Serial.print("h = "); Serial.print(hsl.h); Serial.print("\ts = "); Serial.print(hsl.s); Serial.print("\tl = "); Serial.println(hsl.l);
Serial.print("rgb.r = "); Serial.println(rgb.r);
Serial.print("rgb.g = "); Serial.println(rgb.g);
Serial.print("rgb.b = "); Serial.println(rgb.b);
Serial.println("");
Serial.println("----------------------------");
// bright green
r = 128; g = 255; b = 128;
hsl = rgb2hsl(r, g, b);
Serial.print("r = "); Serial.print(r); Serial.print("\tg = "); Serial.print(g); Serial.print("\tb = "); Serial.println(b);
Serial.print("my_hs1.h = "); Serial.println(hsl.h);
Serial.print("my_hs1.s = "); Serial.println(hsl.s);
Serial.print("my_hs1.l = "); Serial.println(hsl.l);
Serial.println("");
rgb = hsl2rgb(hsl.h, hsl.s, hsl.l);
Serial.print("h = "); Serial.print(hsl.h); Serial.print("\ts = "); Serial.print(hsl.s); Serial.print("\tl = "); Serial.println(hsl.l);
Serial.print("rgb.r = "); Serial.println(rgb.r);
Serial.print("rgb.g = "); Serial.println(rgb.g);
Serial.print("rgb.b = "); Serial.println(rgb.b);
Serial.println("");
Serial.println("----------------------------");
// bright blue
r = 128; g = 128; b = 255;
hsl = rgb2hsl(r, g, b);
Serial.print("r = "); Serial.print(r); Serial.print("\tg = "); Serial.print(g); Serial.print("\tb = "); Serial.println(b);
Serial.print("my_hs1.h = "); Serial.println(hsl.h);
Serial.print("my_hs1.s = "); Serial.println(hsl.s);
Serial.print("my_hs1.l = "); Serial.println(hsl.l);
Serial.println("");
rgb = hsl2rgb(hsl.h, hsl.s, hsl.l);
Serial.print("h = "); Serial.print(hsl.h); Serial.print("\ts = "); Serial.print(hsl.s); Serial.print("\tl = "); Serial.println(hsl.l);
Serial.print("rgb.r = "); Serial.println(rgb.r);
Serial.print("rgb.g = "); Serial.println(rgb.g);
Serial.print("rgb.b = "); Serial.println(rgb.b);
Serial.println("");
Serial.println("----------------------------");
*/
int r = 0; int g = 0; int b = 0;
HSL_t hsl = rgb2hsl(r, g, b);
RGB_t rgb = hsl2rgb(hsl.h, hsl.s, hsl.l);
Serial.println("\nTesting rgb2hsl() & hsl2rgb() ..");
for (r = 0; r < 256; r+=15) {
for (g = 0; g < 256; g+=15) {
for (b = 0; b < 256; b+=15) {
hsl = rgb2hsl(r, g, b);
//Serial.print(r); Serial.print("\t"); Serial.print(g); Serial.print("\t"); Serial.println(b);
rgb = hsl2rgb(hsl.h, hsl.s, hsl.l);
if (rgb.r != r || rgb.g != g || rgb.b != b) {
Serial.print(r); Serial.print("\t"); Serial.print(g); Serial.print("\t"); Serial.println(b);
Serial.print(hsl.h); Serial.print("\t"); Serial.print(hsl.s); Serial.print("\t"); Serial.println(hsl.l);
Serial.print(rgb.r); Serial.print("\t"); Serial.print(rgb.g); Serial.print("\t"); Serial.println(rgb.b);
Serial.println("");
}
} // b
} // g
} // r
// Test changing the luminance to lighten / darken the color
r = 36; g = 196; b = 142;
hsl = rgb2hsl(r, g, b);
Serial.print(hsl.h); Serial.print("\t"); Serial.print(hsl.s); Serial.print("\t"); Serial.println(hsl.l);
rgb = hsl2rgb(hsl.h, hsl.s, 0.1);
Serial.print(rgb.r); Serial.print("\t"); Serial.print(rgb.g); Serial.print("\t"); Serial.println(rgb.b);
Serial.println("");
r = 36; g = 196; b = 142;
hsl = rgb2hsl(r, g, b);
Serial.print(hsl.h); Serial.print("\t"); Serial.print(hsl.s); Serial.print("\t"); Serial.println(hsl.l);
rgb = hsl2rgb(hsl.h, hsl.s, 0.6);
Serial.print(rgb.r); Serial.print("\t"); Serial.print(rgb.g); Serial.print("\t"); Serial.println(rgb.b);
Serial.println("");
Serial.println("Setup complete");
} // setup()
void loop() {
} // loop()
Do you need help developing or customizing a IoT product for your needs? Send me an email requesting a free one hour phone / web share consultation.
The information presented on this website is for the author's use only. Use of this information by anyone other than the author is offered as guidelines and non-professional advice only. No liability is assumed by the author or this web site.