HSL to RGB color conversion

AlgorithmColorsRgbHsl

Algorithm Problem Overview


I am looking for an algorithm to convert between HSL color to RGB.

It seems to me that HSL is not very widely used so I am not having much luck searching for a converter.

Algorithm Solutions


Solution 1 - Algorithm

Garry Tan posted a Javascript solution on his blog (which he attributes to a now defunct mjijackson.com, but is archived here and the original author has a gist - thanks to user2441511).

The code is re-posted below:

HSL to RGB:

/**
 * Converts an HSL color value to RGB. Conversion formula
 * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
 * Assumes h, s, and l are contained in the set [0, 1] and
 * returns r, g, and b in the set [0, 255].
 *
 * @param   {number}  h       The hue
 * @param   {number}  s       The saturation
 * @param   {number}  l       The lightness
 * @return  {Array}           The RGB representation
 */
function hslToRgb(h, s, l){
    var r, g, b;

    if(s == 0){
        r = g = b = l; // achromatic
    }else{
        var hue2rgb = function hue2rgb(p, q, t){
            if(t < 0) t += 1;
            if(t > 1) t -= 1;
            if(t < 1/6) return p + (q - p) * 6 * t;
            if(t < 1/2) return q;
            if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
            return p;
        }

        var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
        var p = 2 * l - q;
        r = hue2rgb(p, q, h + 1/3);
        g = hue2rgb(p, q, h);
        b = hue2rgb(p, q, h - 1/3);
    }

    return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}

##RGB to HSL:

/**
 * Converts an RGB color value to HSL. Conversion formula
 * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
 * Assumes r, g, and b are contained in the set [0, 255] and
 * returns h, s, and l in the set [0, 1].
 *
 * @param   {number}  r       The red color value
 * @param   {number}  g       The green color value
 * @param   {number}  b       The blue color value
 * @return  {Array}           The HSL representation
 */
function rgbToHsl(r, g, b){
    r /= 255, g /= 255, b /= 255;
    var max = Math.max(r, g, b), min = Math.min(r, g, b);
    var h, s, l = (max + min) / 2;

    if(max == min){
        h = s = 0; // achromatic
    }else{
        var d = max - min;
        s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
        switch(max){
            case r: h = (g - b) / d + (g < b ? 6 : 0); break;
            case g: h = (b - r) / d + 2; break;
            case b: h = (r - g) / d + 4; break;
        }
        h /= 6;
    }

    return [h, s, l];
}

Solution 2 - Algorithm

Found the easiest way, python to the rescue :D

>colorsys.hls_to_rgb(h, l, s) > >Convert the color from HLS coordinates to RGB coordinates.

Solution 3 - Algorithm

Java implementation of Mohsen's code

Note that all integer are declared as float (i.e 1f) and must be float, else you will optain grey colors.

HSL to RGB

 /**
 * Converts an HSL color value to RGB. Conversion formula
 * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
 * Assumes h, s, and l are contained in the set [0, 1] and
 * returns r, g, and b in the set [0, 255].
 *
 * @param h       The hue
 * @param s       The saturation
 * @param l       The lightness
 * @return int array, the RGB representation
 */
public static int[] hslToRgb(float h, float s, float l){
    float r, g, b;

    if (s == 0f) {
        r = g = b = l; // achromatic
    } else {
        float q = l < 0.5f ? l * (1 + s) : l + s - l * s;
        float p = 2 * l - q;
        r = hueToRgb(p, q, h + 1f/3f);
        g = hueToRgb(p, q, h);
        b = hueToRgb(p, q, h - 1f/3f);
    }
    int[] rgb = {to255(r), to255(g), to255(b)};
    return rgb;
}
public static int to255(float v) { return (int)Math.min(255,256*v); }

/** Helper method that converts hue to rgb */
public static float hueToRgb(float p, float q, float t) {
    if (t < 0f)
        t += 1f;
    if (t > 1f)
        t -= 1f;
    if (t < 1f/6f)
        return p + (q - p) * 6f * t;
    if (t < 1f/2f)
        return q;
    if (t < 2f/3f)
        return p + (q - p) * (2f/3f - t) * 6f;
    return p;
}

RGB to HSL

/**
 * Converts an RGB color value to HSL. Conversion formula
 * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
 * Assumes pR, pG, and bpBare contained in the set [0, 255] and
 * returns h, s, and l in the set [0, 1].
 *
 * @param pR       The red color value
 * @param pG       The green color value
 * @param pB       The blue color value
 * @return float array, the HSL representation
 */
public static float[] rgbToHsl(int pR, int pG, int pB) {
    float r = pR / 255f;
    float g = pG / 255f;
    float b = pB / 255f;

    float max = (r > g && r > b) ? r : (g > b) ? g : b;
    float min = (r < g && r < b) ? r : (g < b) ? g : b;

    float h, s, l;
    l = (max + min) / 2.0f;

    if (max == min) {
        h = s = 0.0f;
    } else {
        float d = max - min;
        s = (l > 0.5f) ? d / (2.0f - max - min) : d / (max + min);

        if (r > g && r > b)
            h = (g - b) / d + (g < b ? 6.0f : 0.0f);

        else if (g > b)
            h = (b - r) / d + 2.0f;

        else
            h = (r - g) / d + 4.0f;

        h /= 6.0f;
    }

    float[] hsl = {h, s, l};
    return hsl;
}

Solution 4 - Algorithm

The article for HSL and HSV on wikipedia contains some formulas. The calculations are a bit tricky, so it might be useful to take a look at existing implementations.

Solution 5 - Algorithm

If you're looking for something that definitely conforms with the CSS semantics for HSL and RGB, you could use the algorithm specified in the CSS 3 specification, which reads:

HOW TO RETURN hsl.to.rgb(h, s, l): 
   SELECT: 
      l<=0.5: PUT l*(s+1) IN m2
      ELSE: PUT l+s-l*s IN m2
   PUT l*2-m2 IN m1
   PUT hue.to.rgb(m1, m2, h+1/3) IN r
   PUT hue.to.rgb(m1, m2, h    ) IN g
   PUT hue.to.rgb(m1, m2, h-1/3) IN b
   RETURN (r, g, b)

HOW TO RETURN hue.to.rgb(m1, m2, h): 
   IF h<0: PUT h+1 IN h
   IF h>1: PUT h-1 IN h
   IF h*6<1: RETURN m1+(m2-m1)*h*6
   IF h*2<1: RETURN m2
   IF h*3<2: RETURN m1+(m2-m1)*(2/3-h)*6
   RETURN m1

I believe this is the source for some of the other answers here.

Solution 6 - Algorithm

Shor but precise - JS

Use this JS code (more: rgb2hsl, hsv2rgb rgb2hsv and hsl2hsv) - php version here

// input: h as an angle in [0,360] and s,l in [0,1] - output: r,g,b in [0,1]
function hsl2rgb(h,s,l) 
{
   let a=s*Math.min(l,1-l);
   let f= (n,k=(n+h/30)%12) => l - a*Math.max(Math.min(k-3,9-k,1),-1);
   return [f(0),f(8),f(4)];
}   

// oneliner version
let hsl2rgb = (h,s,l, a=s*Math.min(l,1-l), f= (n,k=(n+h/30)%12) => l - a*Math.max(Math.min(k-3,9-k,1),-1)) => [f(0),f(8),f(4)];

// r,g,b are in [0-1], result e.g. #0812fa.
let rgb2hex = (r,g,b) => "#" + [r,g,b].map(x=>Math.round(x*255).toString(16).padStart(2,0) ).join('');


console.log(`hsl: (30,0.2,0.3) --> rgb: (${hsl2rgb(30,0.2,0.3)}) --> hex: ${rgb2hex(...hsl2rgb(30,0.2,0.3))}`);



// ---------------
// UX
// ---------------

rgb= [0,0,0];
hs= [0,0,0];

let $ = x => document.querySelector(x);

function changeRGB(i,e) {
  rgb[i]=e.target.value/255;
  hs = rgb2hsl(...rgb);
  refresh();
}

function changeHS(i,e) {
  hs[i]=e.target.value/(i?255:1);
  rgb= hsl2rgb(...hs);
  refresh();
}

function refresh() {
  rr = rgb.map(x=>x*255|0).join(', ')
  hh = rgb2hex(...rgb);
  tr = `RGB: ${rr}`
  th = `HSL: ${hs.map((x,i)=>i? (x*100).toFixed(2)+'%':x|0).join(', ')}`
  thh= `HEX: ${hh}`
  $('.box').style.backgroundColor=`rgb(${rr})`;  
  $('.infoRGB').innerHTML=`${tr}`;  
  $('.infoHS').innerHTML =`${th}\n${thh}`;  
  
  $('#r').value=rgb[0]*255;
  $('#g').value=rgb[1]*255;
  $('#b').value=rgb[2]*255;
  
  $('#h').value=hs[0];
  $('#s').value=hs[1]*255;
  $('#l').value=hs[2]*255;  
}

function rgb2hsl(r,g,b) {
  let a=Math.max(r,g,b), n=a-Math.min(r,g,b), f=(1-Math.abs(a+a-n-1)); 
  let h= n && ((a==r) ? (g-b)/n : ((a==g) ? 2+(b-r)/n : 4+(r-g)/n)); 
  return [60*(h<0?h+6:h), f ? n/f : 0, (a+a-n)/2];
}

refresh();

.box {
  width: 50px;
  height: 50px;
  margin: 20px;
}

body {
    display: flex;
}

<div>
<input id="r" type="range" min="0" max="255" oninput="changeRGB(0,event)">R<br>
<input id="g" type="range" min="0" max="255" oninput="changeRGB(1,event)">G<br>
<input id="b" type="range" min="0" max="255" oninput="changeRGB(2,event)">B<br>
<pre class="infoRGB"></pre>
</div> 

<div>
<div class="box hsl"></div>

</div>

<div>
<input id="h" type="range" min="0" max="360" oninput="changeHS(0,event)">H<br>
<input id="s" type="range" min="0" max="255" oninput="changeHS(1,event)">S<br>
<input id="l" type="range" min="0" max="255" oninput="changeHS(2,event)">L<br>
<pre class="infoHS"></pre><br>
</div>

Here is formula which I discover and precisely describe in wiki + error analysis,

enter image description here

Solution 7 - Algorithm

C# Code from Mohsen's answer.

Here is the code from Mohsen's answer in C# if anyone else wants it. Note: Color is a custom class and Vector4 is from OpenTK. Both are easy to replace with something else of your choosing.

Hsl To Rgba

/// <summary>
/// Converts an HSL color value to RGB.
/// Input: Vector4 ( X: [0.0, 1.0], Y: [0.0, 1.0], Z: [0.0, 1.0], W: [0.0, 1.0] )
/// Output: Color ( R: [0, 255], G: [0, 255], B: [0, 255], A: [0, 255] )
/// </summary>
/// <param name="hsl">Vector4 defining X = h, Y = s, Z = l, W = a. Ranges [0, 1.0]</param>
/// <returns>RGBA Color. Ranges [0, 255]</returns>
public static Color HslToRgba(Vector4 hsl)
{
    float r, g, b;

    if (hsl.Y == 0.0f)
        r = g = b = hsl.Z;

    else
    {
        var q = hsl.Z < 0.5f ? hsl.Z * (1.0f + hsl.Y) : hsl.Z + hsl.Y - hsl.Z * hsl.Y;
        var p = 2.0f * hsl.Z - q;
        r = HueToRgb(p, q, hsl.X + 1.0f / 3.0f);
        g = HueToRgb(p, q, hsl.X);
        b = HueToRgb(p, q, hsl.X - 1.0f / 3.0f);
    }

    return new Color((int)(r * 255), (int)(g * 255), (int)(b * 255), (int)(hsl.W * 255));
}

// Helper for HslToRgba
private static float HueToRgb(float p, float q, float t)
{
    if (t < 0.0f) t += 1.0f;
    if (t > 1.0f) t -= 1.0f;
    if (t < 1.0f / 6.0f) return p + (q - p) * 6.0f * t;
    if (t < 1.0f / 2.0f) return q;
    if (t < 2.0f / 3.0f) return p + (q - p) * (2.0f / 3.0f - t) * 6.0f;
    return p;
}

Rgba To Hsl

/// <summary>
/// Converts an RGB color value to HSL.
/// Input: Color ( R: [0, 255], G: [0, 255], B: [0, 255], A: [0, 255] )
/// Output: Vector4 ( X: [0.0, 1.0], Y: [0.0, 1.0], Z: [0.0, 1.0], W: [0.0, 1.0] )
/// </summary>
/// <param name="rgba"></param>
/// <returns></returns>
public static Vector4 RgbaToHsl(Color rgba)
{
    float r = rgba.R / 255.0f;
    float g = rgba.G / 255.0f;
    float b = rgba.B / 255.0f;

    float max = (r > g && r > b) ? r : (g > b) ? g : b;
    float min = (r < g && r < b) ? r : (g < b) ? g : b;

    float h, s, l;
    h = s = l = (max + min) / 2.0f;

    if (max == min)
        h = s = 0.0f;

    else
    {
        float d = max - min;
        s = (l > 0.5f) ? d / (2.0f - max - min) : d / (max + min);

        if (r > g && r > b)
            h = (g - b) / d + (g < b ? 6.0f : 0.0f);

        else if (g > b)
            h = (b - r) / d + 2.0f;

        else
            h = (r - g) / d + 4.0f;

        h /= 6.0f;
    }

    return new Vector4(h, s, l, rgba.A / 255.0f);
}

Solution 8 - Algorithm

#Php Implementation of Chris's C# Code

Also from here, which explains the math of it very well.

This is basically a bunch of functions to convert to and from HSL (Hue Saturation Lightness)

Tested and working on PHP 5.6.15

TL;DR: The full code can be found here on Pastebin.


##Hex to HSL Input: Hex color in format: [#]0f4 or [#]00ff44 (pound sign optional)
Output: HSL in Degrees, Percent, Percent

/**
 * Input: hex color
 * Output: hsl(in ranges from 0-1)
 * 
 * Takes the hex, converts it to RGB, and sends
 * it to RGBToHsl.  Returns the output.
 * 
*/
function hexToHsl($hex) {
	$r = "";
	$g = "";
	$b = "";

	$hex = str_replace('#', '', $hex);
	
	if (strlen($hex) == 3) {
		$r = substr($hex, 0, 1);
		$r = $r . $r;
		$g = substr($hex, 1, 1);
		$g = $g . $g;
		$b = substr($hex, 2, 1);
		$b = $b . $b;
	} elseif (strlen($hex) == 6) {
		$r = substr($hex, 0, 2);
		$g = substr($hex, 2, 2);
		$b = substr($hex, 4, 2);
	} else {
		return false;
	}

	$r = hexdec($r);
	$g = hexdec($g);
	$b = hexdec($b);

	$hsl =  rgbToHsl($r,$g,$b);
	return $hsl;
}

RGB to HSL

Input: RGB in range 0-255 Output: HSL in Degrees, Percent, Percent.

/**
 * 
 *Credits:
 * https://stackoverflow.com/questions/4793729/rgb-to-hsl-and-back-calculation-problems
 * http://www.niwa.nu/2013/05/math-behind-colorspace-conversions-rgb-hsl/
 *
 * Called by hexToHsl by default.
 *
 * Converts an RGB color value to HSL. Conversion formula
 * adapted from http://www.niwa.nu/2013/05/math-behind-colorspace-conversions-rgb-hsl/.
 * Assumes r, g, and b are contained in the range [0 - 255] and
 * returns h, s, and l in the format Degrees, Percent, Percent.
 *
 * @param   Number  r       The red color value
 * @param   Number  g       The green color value
 * @param   Number  b       The blue color value
 * @return  Array           The HSL representation
*/
function rgbToHsl($r, $g, $b){  
    //For the calculation, rgb needs to be in the range from 0 to 1. To convert, divide by 255 (ff). 
    $r /= 255;
    $g /= 255;
    $b /= 255;
    
    $myMax = max($r, $g, $b);
    $myMin = min($r, $g, $b);
    
    $maxAdd = ($myMax + $myMin);
    $maxSub = ($myMax - $myMin);
    
	//luminence is (max + min)/2
	$h = 0;
	$s = 0;
    $l = ($maxAdd / 2.0);
    
	//if all the numbers are equal, there is no saturation (greyscale).
    if($myMin != $myMax){
        if ($l < 0.5) {
        	$s = ($maxSub / $maxAdd);
        } else {
        	$s = (2.0 - $myMax - $myMin); //note order of opperations - can't use $maxSub here
        	$s = ($maxSub / $s);
        }
        
        //find hue
        switch($myMax){
            case $r: 
            	$h = ($g - $b);
            	$h = ($h / $maxSub);
            	break;
            case $g: 
            	$h = ($b - $r); 
            	$h = ($h / $maxSub);
            	$h = ($h + 2.0);
            	break;
            case $b: 
            	$h = ($r - $g);
            	$h = ($h / $maxSub); 
            	$h = ($h + 4.0);
            	break;
        } 
    }
    
	$hsl = hslToDegPercPerc($h, $s, $l);
    return $hsl;
}

##HSL (0-1 range) to Degrees, Percent, Percent format

For the math calculations, HSL is easier to deal with in the 0-1 range, but for human readability, it's easier in Degrees, Percent, Percent. This function takes HSL in the ranges 0-1, and returns HSL in Degrees, Percent, Percent.

/**
 * Input: HSL in ranges 0-1.
 * Output: HSL in format Deg, Perc, Perc.
 * 
 * Note: rgbToHsl calls this function by default.
 * 
 * Multiplies $h by 60, and $s and $l by 100.
 */
function hslToDegPercPerc($h, $s, $l) {
	//convert h to degrees
	$h *= 60;
	
	if ($h < 0) {
		$h += 360;
	}
	
	//convert s and l to percentage
	$s *= 100;
	$l *= 100;
	
	$hsl['h'] = $h;
	$hsl['s'] = $s;
	$hsl['l'] = $l;
	return $hsl;
}

##HSL (Degrees, Percent, Percent format) to HSL in range 0-1

This function converts HSL in the format Degrees, Percent, Percent, to the ranges 0-1 for easier computing.

/**
 * Input: HSL in format Deg, Perc, Perc
 * Output: An array containing HSL in ranges 0-1
 * 
 * Divides $h by 60, and $s and $l by 100.
 * 
 * hslToRgb calls this by default.
*/
function degPercPercToHsl($h, $s, $l) { 
	//convert h, s, and l back to the 0-1 range
	
	//convert the hue's 360 degrees in a circle to 1
	$h /= 360;
	
	//convert the saturation and lightness to the 0-1 
	//range by multiplying by 100
	$s /= 100;
	$l /= 100;
	
	$hsl['h'] =  $h;
	$hsl['s'] = $s;
	$hsl['l'] = $l;
	
	return $hsl;
}

##HSL to RGB

Input: HSL in the format Degrees, Percent, Percent Output: RGB in the format 255, 255, 255.

/**
 * Converts an HSL color value to RGB. Conversion formula
 * adapted from http://www.niwa.nu/2013/05/math-behind-colorspace-conversions-rgb-hsl/.
 * Assumes h, s, and l are in the format Degrees,
 * Percent, Percent, and returns r, g, and b in 
 * the range [0 - 255].
 *
 * Called by hslToHex by default.
 *
 * Calls: 
 *   degPercPercToHsl
 *   hueToRgb
 *
 * @param   Number  h       The hue value
 * @param   Number  s       The saturation level
 * @param   Number  l       The luminence
 * @return  Array           The RGB representation
 */
function hslToRgb($h, $s, $l){
	$hsl = degPercPercToHsl($h, $s, $l);
	$h = $hsl['h'];
	$s = $hsl['s'];
	$l = $hsl['l'];

	//If there's no saturation, the color is a greyscale,
	//so all three RGB values can be set to the lightness.
	//(Hue doesn't matter, because it's grey, not color)
	if ($s == 0) {
   		$r = $l * 255;
   		$g = $l * 255;
   		$b = $l * 255;
	}
	else {
		//calculate some temperary variables to make the 
		//calculation eaisier.
   		if ($l < 0.5) {
   			$temp2 = $l * (1 + $s);
   		} else {
   			$temp2 = ($l + $s) - ($s * $l);
   		}
   		$temp1 = 2 * $l - $temp2;
		
		//run the calculated vars through hueToRgb to
		//calculate the RGB value.  Note that for the Red
		//value, we add a third (120 degrees), to adjust 
		//the hue to the correct section of the circle for
		//red.  Simalarly, for blue, we subtract 1/3.
   		$r = 255 * hueToRgb($temp1, $temp2, $h + (1 / 3));
   		$g = 255 * hueToRgb($temp1, $temp2, $h);
   		$b = 255 * hueToRgb($temp1, $temp2, $h - (1 / 3));
	}
		
	$rgb['r'] = $r;
	$rgb['g'] = $g;
	$rgb['b'] = $b;

	return $rgb;
}

###Hue to RGB

This function is called by hslToRgb to convert the hue into the separate RGB values.

/**
 * Converts an HSL hue to it's RGB value.  
 *
 * Input: $temp1 and $temp2 - temperary vars based on 
 * whether the lumanence is less than 0.5, and 
 * calculated using the saturation and luminence
 * values.
 *  $hue - the hue (to be converted to an RGB 
 * value)  For red, add 1/3 to the hue, green 
 * leave it alone, and blue you subtract 1/3 
 * from the hue.
 *
 * Output: One RGB value.
 *
 * Thanks to Easy RGB for this function (Hue_2_RGB).
 * http://www.easyrgb.com/index.php?X=MATH&$h=19#text19
 *
*/
function hueToRgb($temp1, $temp2, $hue) {
   	if ($hue < 0) { 
   		$hue += 1;
   	}
   	if ($hue > 1) {
   		$hue -= 1;
   	}
   	
   	if ((6 * $hue) < 1 ) {
   		return ($temp1 + ($temp2 - $temp1) * 6 * $hue);
   	} elseif ((2 * $hue) < 1 ) {
   		return $temp2;
   	} elseif ((3 * $hue) < 2 ) {
   		return ($temp1 + ($temp2 - $temp1) * ((2 / 3) - $hue) * 6);
   	}
   	return $temp1;
}

##HSL to Hex

Input: HSL in format Degrees, Percent, Percent Output: Hex in format 00ff22 (no pound sign).

Converts to RGB, then converts separately to hex.

/**
 * Converts HSL to Hex by converting it to 
 * RGB, then converting that to hex.
 * 
 * string hslToHex($h, $s, $l[, $prependPound = true]
 * 
 * $h is the Degrees value of the Hue
 * $s is the Percentage value of the Saturation
 * $l is the Percentage value of the Lightness
 * $prependPound is a bool, whether you want a pound 
 *  sign prepended. (optional - default=true)
 *
 * Calls: 
 *   hslToRgb
 *
 * Output: Hex in the format: #00ff88 (with 
 * pound sign).  Rounded to the nearest whole
 * number.
*/
function hslToHex($h, $s, $l, $prependPound = true) {
	//convert hsl to rgb
	$rgb = hslToRgb($h,$s,$l);

	//convert rgb to hex
	$hexR = $rgb['r'];
	$hexG = $rgb['g'];
	$hexB = $rgb['b'];
	
	//round to the nearest whole number
	$hexR = round($hexR);
	$hexG = round($hexG);
	$hexB = round($hexB);
	
	//convert to hex
	$hexR = dechex($hexR);
	$hexG = dechex($hexG);
	$hexB = dechex($hexB);
	
	//check for a non-two string length
	//if it's 1, we can just prepend a
	//0, but if it is anything else non-2,
	//it must return false, as we don't 
	//know what format it is in.
	if (strlen($hexR) != 2) {
		if (strlen($hexR) == 1) {
			//probably in format #0f4, etc.
			$hexR = "0" . $hexR;
		} else {
			//unknown format
			return false;
		}
	}
	if (strlen($hexG) != 2) {
		if (strlen($hexG) == 1) {
			$hexG = "0" . $hexG;
		} else {
			return false;
		}
	}
	if (strlen($hexB) != 2) {
		if (strlen($hexB) == 1) {
			$hexB = "0" . $hexB;
		} else {
			return false;
		}
	}
	
	//if prependPound is set, will prepend a
	//# sign to the beginning of the hex code.
	//(default = true)
	$hex = "";
	if ($prependPound) {
		$hex = "#";
	}
	
	$hex = $hex . $hexR . $hexG . $hexB;
	
	return $hex;
}

Solution 9 - Algorithm

This is how I do it which is easy to remember is to think of RGB as three spokes on a wheel, 120 degrees apart.

H = hue (0-360)
S = saturation (0-1)
L = luminance (0-1)

R1 = SIN( H ) * L 
G1 = SIN( H + 120 ) * L 
B1 = SIN( H + 240 ) * L 

The tricky part is saturation, which is to a scale down to the average of those three.

AVERAGE = (R1 + G1 + B1) / 3 

R2 = ((R1 - AVERAGE) * S) + AVERAGE 
G2 = ((G1 - AVERAGE) * S) + AVERAGE 
B2 = ((B1 - AVERAGE) * S) + AVERAGE 

RED = R2 * 255 
GREEN = G2 * 255 
BLUE = B2 * 255 

Solution 10 - Algorithm

Here's a fast, super-simple, branchless version in GLSL:

vec3 hsl2rgb( vec3 c ) {
    vec3 rgb = clamp(abs(mod(c.x*6.0 + vec3(0.0, 4.0, 2.0), 6.0)-3.0)-1.0, 0.0, 1.0);
    return c.z + c.y * (rgb-0.5)*(1.0-abs(2.0*c.z-1.0));
}

Doesn't get much shorter than that ~


Link to the original proof-of-concept: https://www.shadertoy.com/view/XljGzV

(Disclaimer: not my code!)

Solution 11 - Algorithm

Here is the modified javascript function, it outputs Hue in set 0-360 degrees.

function rgbToHsl(r, g, b) {
      r /= 255, g /= 255, b /= 255;
      var max = Math.max(r, g, b), min = Math.min(r, g, b);
      var h, s, l = (max + min) / 2;

      if(max == min){
          h = s = 0; // achromatic
      } else {
          var d = max - min;
          s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
          switch(max){
              case r: h = (g - b) / d ; break;
              case g: h = 2 + ( (b - r) / d); break;
              case b: h = 4 + ( (r - g) / d); break;
          }
          h*=60;
          if (h < 0) h +=360;
      }
     return([h, s, l]);
  }  
alert(rgbToHsl(125,115,145));

Solution 12 - Algorithm

I got this from Brandon Mathis' HSL Picker source code.

It was originally written in CoffeeScript. I converted it to JavaScript using an online converter, and took out the mechanism to verify the user input was a valid RGB value. This answer worked for my usecase, as the most up-voted answer on this post I found to not produce a valid HSL value.

Note that it returns an hsla value, with a representing opacity/transparency. 0 is completely transparent, and 1 fully opaque.

function rgbToHsl(rgb) {
  var a, add, b, diff, g, h, hue, l, lum, max, min, r, s, sat;
  r = parseFloat(rgb[0]) / 255;
  g = parseFloat(rgb[1]) / 255;
  b = parseFloat(rgb[2]) / 255;
  max = Math.max(r, g, b);
  min = Math.min(r, g, b);
  diff = max - min;
  add = max + min;
  hue = min === max ? 0 : r === max ? ((60 * (g - b) / diff) + 360) % 360 : g === max ? (60 * (b - r) / diff) + 120 : (60 * (r - g) / diff) + 240;
  lum = 0.5 * add;
  sat = lum === 0 ? 0 : lum === 1 ? 1 : lum <= 0.5 ? diff / add : diff / (2 - add);
  h = Math.round(hue);
  s = Math.round(sat * 100);
  l = Math.round(lum * 100);
  a = parseFloat(rgb[3]) || 1;
  return [h, s, l, a];
}

Solution 13 - Algorithm

An hsl|a color value, set in javascript, will be instantly converted to rgb|a All you need to do then is access the computed style value

document.body.style.color = 'hsla(44, 100%, 50%, 0.8)';

console.log(window.getComputedStyle(document.body).color);

// displays: rgba(255, 187, 0, 0.8)

Technically, I guess, this isn't even any lines of code - it's just done automatically. So, depending on your environment, you might be able to get away with just this. Not that there aren't a lot of very thoughtful responses here. I don't know what your goal is.

Now, what if you want to convert from rbg|a to hsl|a?

Solution 14 - Algorithm

With H, S,and L in [0,1] range:

ConvertHslToRgb: function (iHsl)
{
    var min, sv, sextant, fract, vsf;

    var v = (iHsl.l <= 0.5) ? (iHsl.l * (1 + iHsl.s)) : (iHsl.l + iHsl.s - iHsl.l * iHsl.s);
    if (v === 0)
        return { Red: 0, Green: 0, Blue: 0 };

    min = 2 * iHsl.l - v;
    sv = (v - min) / v;
    var h = (6 * iHsl.h) % 6;
    sextant = Math.floor(h);
    fract = h - sextant;
    vsf = v * sv * fract;

    switch (sextant)
    {
        case 0: return { r: v, g: min + vsf, b: min };
        case 1: return { r: v - vsf, g: v, b: min };
        case 2: return { r: min, g: v, b: min + vsf };
        case 3: return { r: min, g: v - vsf, b: v };
        case 4: return { r: min + vsf, g: min, b: v };
        case 5: return { r: v, g: min, b: v - vsf };
    }
}

Solution 15 - Algorithm

For when you need RGB to HSV and vice versa instead:

function rgbToHsv(r, g, b)
{
    r /= 255, g /= 255, b /= 255;

    var min = Math.min(r, g, b),
    max = Math.max(r, g, b),
    delta = max - min,
    h = 0, s = 0, v = max;

    if (min != max)
    {
        s = (delta / max);

        switch (max)
        {
            case r: h = (g - b) / delta + (g < b ? 6 : 0); break;
            case g: h = (b - r) / delta + 2; break;
            case b: h = (r - g) / delta + 4; break;
        }

        h /= 6;
    }

    return [h, s, v];
}

function hsvToRgb(h, s, v)
{
    var step = h / (1 / 6),
    pos = step - Math.floor(step), // the hue position within the current step
    m = (Math.floor(step) % 2) ? (1 - pos) * v : pos * v, // mix color value adjusted to the brightness(v)
    max = 1 * v,
    min = (1 - s) * v,
    med = m + ((1 - s) * (v - m)),
    r, g, b;

    switch (Math.floor(step))
    {
        case 0:
            r = max;
            g = med;
            b = min;
            break;
        case 1:
            r = med;
            g = max;
            b = min;
            break;
        case 2:
            r = min;
            g = max;
            b = med;
            break;
        case 3:
            r = min;
            g = med;
            b = max;
            break;
        case 4:
            r = med;
            g = min;
            b = max;
            break;
        case 5:
            r = max;
            g = min;
            b = med;
            break;
    }

    return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}

Solution 16 - Algorithm

Unity3D C# Code from Mohsen's answer.

Here is the code from Mohsen's answer in C# targeted specifically for Unity3D. It was adapted from the C# answer given by Alec Thilenius above.

using UnityEngine;
using System.Collections;

public class ColorTools {

	/// <summary>
	/// Converts an HSL color value to RGB.
	/// Input: Vector4 ( X: [0.0, 1.0], Y: [0.0, 1.0], Z: [0.0, 1.0], W: [0.0, 1.0] )**strong text**
	/// Output: Color ( R: [0.0, 1.0], G: [0.0, 1.0], B: [0.0, 1.0], A: [0.0, 1.0] )
	/// </summary>
	/// <param name="hsl">Vector4 defining X = h, Y = s, Z = l, W = a. Ranges [0, 1.0]</param>
	/// <returns>RGBA Color. Ranges [0.0, 1.0]</returns>
	public static Color HslToRgba(Vector4 hsl)
	{
		float r, g, b;
		
		if (hsl.y == 0.0f)
			r = g = b = hsl.z;
		else
		{
			var q = hsl.z < 0.5f ? hsl.z * (1.0f + hsl.y) : hsl.z + hsl.y - hsl.z * hsl.y;
			var p = 2.0f * hsl.z - q;
			r = HueToRgb(p, q, hsl.x + 1.0f / 3.0f);
			g = HueToRgb(p, q, hsl.x);
			b = HueToRgb(p, q, hsl.x - 1.0f / 3.0f);
		}
		
		return new Color(r, g, b, hsl.w);
	}
	
	// Helper for HslToRgba
	private static float HueToRgb(float p, float q, float t)
	{
		if (t < 0.0f) t += 1.0f;
		if (t > 1.0f) t -= 1.0f;
		if (t < 1.0f / 6.0f) return p + (q - p) * 6.0f * t;
		if (t < 1.0f / 2.0f) return q;
		if (t < 2.0f / 3.0f) return p + (q - p) * (2.0f / 3.0f - t) * 6.0f;
		return p;
	}

	/// <summary>
	/// Converts an RGB color value to HSL.
	/// Input: Color ( R: [0.0, 1.0], G: [0.0, 1.0], B: [0.0, 1.0], A: [0.0, 1.0] )
	/// Output: Vector4 ( X: [0.0, 1.0], Y: [0.0, 1.0], Z: [0.0, 1.0], W: [0.0, 1.0] )
	/// </summary>
	/// <param name="rgba"></param>
	/// <returns></returns>
	public static Vector4 RgbaToHsl(Color rgba)
	{
		
		float max = (rgba.r > rgba.g && rgba.r > rgba.b) ? rgba.r : 
			(rgba.g > rgba.b) ? rgba.g : rgba.b;
		float min = (rgba.r < rgba.g && rgba.r < rgba.b) ? rgba.r : 
			(rgba.g < rgba.b) ? rgba.g : rgba.b;
		
		float h, s, l;
		h = s = l = (max + min) / 2.0f;
		
		if (max == min)
			h = s = 0.0f;
		else
		{
			float d = max - min;
			s = (l > 0.5f) ? d / (2.0f - max - min) : d / (max + min);
			
			if (rgba.r > rgba.g && rgba.r > rgba.b)
				h = (rgba.g - rgba.b) / d + (rgba.g < rgba.b ? 6.0f : 0.0f);
			
			else if (rgba.g > rgba.b)
				h = (rgba.b - rgba.r) / d + 2.0f;
			
			else
				h = (rgba.r - rgba.g) / d + 4.0f;
			
			h /= 6.0f;
		}
		
		return new Vector4(h, s, l, rgba.a);
	}

}

Solution 17 - Algorithm

For all who said that Garry Tan solution converting incorrect from RGB to HSL and back. It because he left out fraction part of number in his code. I corrected his code (javascript). Sorry for link on russian languadge, but on english absent - HSL-wiki

function toHsl(r, g, b)
{
	r /= 255.0;
	g /= 255.0;
	b /= 255.0;
	var max = Math.max(r, g, b);
	var min = Math.min(r, g, b);
	var h, s, l = (max + min) / 2.0;

	if(max == min)
	{
		h = s = 0; 
	}
	else
	{
		var d = max - min;
		s = (l > 0.5 ? d / (2.0 - max - min) : d / (max + min));
		
		if(max == r && g >= b)
		{
			h = 1.0472 * (g - b) / d ;
		}
		else if(max == r && g < b)
		{
			h = 1.0472 * (g - b) / d + 6.2832;
		}
		else if(max == g)
		{
			h = 1.0472 * (b - r) / d + 2.0944;
		}
		else if(max == b)
		{
			h = 1.0472 * (r - g) / d + 4.1888;
		}
	}
	return {
		str: 'hsl(' + parseInt(h / 6.2832 * 360.0 + 0.5) + ',' + parseInt(s * 100.0 + 0.5) + '%,' + parseInt(l * 100.0 + 0.5) + '%)',
		obj: { h: parseInt(h / 6.2832 * 360.0 + 0.5), s: parseInt(s * 100.0 + 0.5), l: parseInt(l * 100.0 + 0.5) }
	};
};

Solution 18 - Algorithm

PHP implementation of @Mohsen's code (including Test!)

Sorry to re-post this. But I really haven't seen any other implementation that gives the quality I needed.

/**
 * Converts an HSL color value to RGB. Conversion formula
 * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
 * Assumes h, s, and l are contained in the set [0, 1] and
 * returns r, g, and b in the set [0, 255].
 *
 * @param   {number}  h       The hue
 * @param   {number}  s       The saturation
 * @param   {number}  l       The lightness
 * @return  {Array}           The RGB representation
 */
  
function hue2rgb($p, $q, $t){
            if($t < 0) $t += 1;
            if($t > 1) $t -= 1;
            if($t < 1/6) return $p + ($q - $p) * 6 * $t;
            if($t < 1/2) return $q;
            if($t < 2/3) return $p + ($q - $p) * (2/3 - $t) * 6;
            return $p;
        }
function hslToRgb($h, $s, $l){
    if($s == 0){
        $r = $l;
        $g = $l;
        $b = $l; // achromatic
    }else{
        $q = $l < 0.5 ? $l * (1 + $s) : $l + $s - $l * $s;
        $p = 2 * $l - $q;
        $r = hue2rgb($p, $q, $h + 1/3);
        $g = hue2rgb($p, $q, $h);
        $b = hue2rgb($p, $q, $h - 1/3);
    }

    return array(round($r * 255), round($g * 255), round($b * 255));
}

/* Uncomment to test * /
for ($i=0;$i<360;$i++) {
  $rgb=hslToRgb($i/360, 1, .9);
  echo '<div style="background-color:rgb(' .$rgb[0] . ', ' . $rgb[1] . ', ' . $rgb[2] . ');padding:2px;"></div>';
}
/* End Test */

Solution 19 - Algorithm

C++ implementation with probably better performance than @Mohsen code. It uses a [0-6] range for the hue, avoiding the division and multiplication by 6. S and L range is [0,1]

void fromRGBtoHSL(float rgb[], float hsl[])
{
     const float maxRGB = max(rgb[0], max(rgb[1], rgb[2]));
     const float minRGB = min(rgb[0], min(rgb[1], rgb[2]));
     const float delta2 = maxRGB + minRGB;
     hsl[2] = delta2 * 0.5f;

     const float delta = maxRGB - minRGB;
     if (delta < FLT_MIN)
         hsl[0] = hsl[1] = 0.0f;
     else
     {
         hsl[1] = delta / (hsl[2] > 0.5f ? 2.0f - delta2 : delta2);
         if (rgb[0] >= maxRGB)
         {
             hsl[0] = (rgb[1] - rgb[2]) / delta;
             if (hsl[0] < 0.0f)
                 hsl[0] += 6.0f;
         }
         else if (rgb[1] >= maxRGB)
             hsl[0] = 2.0f + (rgb[2] - rgb[0]) / delta;
         else
             hsl[0] = 4.0f + (rgb[0] - rgb[1]) / delta;
     }
}

void fromHSLtoRGB(const float hsl[], float rgb[])
{
    if(hsl[1] < FLT_MIN)
        rgb[0] = rgb[1] = rgb[2] = hsl[2];
    else if(hsl[2] < FLT_MIN)
        rgb[0] = rgb[1] = rgb[2] = 0.0f;
    else
    {
        const float q = hsl[2] < 0.5f ? hsl[2] * (1.0f + hsl[1]) : hsl[2] + hsl[1] - hsl[2] * hsl[1];
        const float p = 2.0f * hsl[2] - q;
        float t[] = {hsl[0] + 2.0f, hsl[0], hsl[0] - 2.0f};

        for(int i=0; i<3; ++i)
        {
            if(t[i] < 0.0f)
                t[i] += 6.0f;
            else if(t[i] > 6.0f)
                t[i] -= 6.0f;

            if(t[i] < 1.0f)
                rgb[i] = p + (q - p) * t[i];
            else if(t[i] < 3.0f)
                rgb[i] = q;
            else if(t[i] < 4.0f)
                rgb[i] = p + (q - p) * (4.0f - t[i]);
            else
                rgb[i] = p;
          }
      }
}

Solution 20 - Algorithm

HSL to RGB in Typescript

All the options above didn't work on my code in TS.

I tweak one of those and now it works as a charm:

type HslType = { h: number; s: number; l: number }

const hslToRgb = (hsl: HslType): RgbType => {
  let { h, s, l } = hsl

  // IMPORTANT if s and l between 0,1 remove the next two lines:
  s /= 100
  l /= 100

  const k = (n: number) => (n + h / 30) % 12
  const a = s * Math.min(l, 1 - l)
  const f = (n: number) =>
    l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)))
  return {
    r: Math.round(255 * f(0)),
    g: Math.round(255 * f(8)),
    b: Math.round(255 * f(4)),
  }
}

Solution 21 - Algorithm

I needed a really light weight one, Its not 100%, but it gets close enough for some usecases.

float3 Hue(float h, float s, float l)
{
    float r = max(cos(h * 2 * UNITY_PI) * 0.5 + 0.5, 0);
	float g = max(cos((h + 0.666666) * 2 * UNITY_PI) * 0.5 + 0.5, 0);
	float b = max(cos((h + 0.333333) * 2 * UNITY_PI) * 0.5 + 0.5, 0);
	float gray = 0.2989 * r + 0.5870 * g + 0.1140 * b;
	return lerp(gray, float3(r, g, b), s) * smoothstep(0, 0.5, l) + 1 * smoothstep(0.5, 1, l);
}

Solution 22 - Algorithm

PHP - shortest but precise

Here I rewrite my JS answer (math details are there) to PHP - you can run it here

function hsl2rgb($h,$s,$l) 
{
  $a = $s * min($l, 1-$l);
  $k = function($n,$h) { return ($n+$h/30)%12;};
  $f = function($n) use ($h,$s,$l,$a,$k) { 
      return $l - $a * max( min($k($n,$h)-3, 9-$k($n,$h), 1),-1);
  };
  return [ $f(0), $f(8), $f(4) ];
}   

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionhhafezView Question on Stackoverflow
Solution 1 - AlgorithmMohsenView Answer on Stackoverflow
Solution 2 - AlgorithmhhafezView Answer on Stackoverflow
Solution 3 - AlgorithmPdroidView Answer on Stackoverflow
Solution 4 - AlgorithmOtto AllmendingerView Answer on Stackoverflow
Solution 5 - AlgorithmjameshfisherView Answer on Stackoverflow
Solution 6 - AlgorithmKamil KiełczewskiView Answer on Stackoverflow
Solution 7 - AlgorithmAlec ThileniusView Answer on Stackoverflow
Solution 8 - AlgorithmCullubView Answer on Stackoverflow
Solution 9 - AlgorithmKen JinksView Answer on Stackoverflow
Solution 10 - AlgorithmTerra AshleyView Answer on Stackoverflow
Solution 11 - Algorithmzeros-and-onesView Answer on Stackoverflow
Solution 12 - AlgorithmernbrnView Answer on Stackoverflow
Solution 13 - Algorithmstoke motorView Answer on Stackoverflow
Solution 14 - AlgorithmsboisseView Answer on Stackoverflow
Solution 15 - AlgorithmErikView Answer on Stackoverflow
Solution 16 - AlgorithmJames AllenView Answer on Stackoverflow
Solution 17 - Algorithm0x000fView Answer on Stackoverflow
Solution 18 - AlgorithmSwiss MisterView Answer on Stackoverflow
Solution 19 - AlgorithmAdriel JrView Answer on Stackoverflow
Solution 20 - AlgorithmBru Mas RiberaView Answer on Stackoverflow
Solution 21 - AlgorithmBjarkeCKView Answer on Stackoverflow
Solution 22 - AlgorithmKamil KiełczewskiView Answer on Stackoverflow