Velvet Star Monitor

Standout celebrity highlights with iconic style.

general

convert Hsl to rgb and hex

Writer Andrew Henderson

I need a color converter to convert from hsl to rgb and hex value. I am going to do similar like this. I am using jquery and jquery ui range slider for this. Here is my code:

$(function() { $( "#hsl_hue_range" ).slider({ min: 0, max: 100, value: 0, range: false, animate:"slow", orientation: "horizontal", slide: function( event, ui ) { var hsl_hue = ui.value; } });
});
$(function() { $( "#hsl_saturation_range" ).slider({ min: 0, max: 100, value: 0, range: false, animate:"slow", orientation: "horizontal", slide: function( event, ui ) { var hsl_saturation = ui.value; } });
});
$(function() { $( "#hsl_light_range" ).slider({ min: 0, max: 100, value: 0, range: false, animate:"slow", orientation: "horizontal", slide: function( event, ui ) { var hsl_light = ui.value; } });
});

I want the solution like this:

the input to converter can be given by the variables. like hsl_hue hsl_saturation hsl_light.

Is there any way to do this?
if no way, what can I do?

6 Answers

New approach (inspired by @Kamil-Kiełczewski solution)
Takes degree, percentage, percentage and returns css hex color:

function hslToHex(h, s, l) { l /= 100; const a = s * Math.min(l, 1 - l) / 100; const f = n => { const k = (n + h / 30) % 12; const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1); return Math.round(255 * color).toString(16).padStart(2, '0'); // convert to Hex and prefix "0" if needed }; return `#${f(0)}${f(8)}${f(4)}`;
}

Example:

hslToHex(360, 100, 50) // "#ff0000" -> red

Original version: (still OK, just longer)

Takes degree, percentage, percentage and returns css hex color:

function hslToHex(h, s, l) { h /= 360; s /= 100; l /= 100; let r, g, b; if (s === 0) { r = g = b = l; // achromatic } else { const 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; }; const q = l < 0.5 ? l * (1 + s) : l + s - l * s; const p = 2 * l - q; r = hue2rgb(p, q, h + 1 / 3); g = hue2rgb(p, q, h); b = hue2rgb(p, q, h - 1 / 3); } const toHex = x => { const hex = Math.round(x * 255).toString(16); return hex.length === 1 ? '0' + hex : hex; }; return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
}

Example:

hslToHex(360, 100, 50) // "#ff0000" -> red

Shortest

Try this (wiki, error analysis, more: rgb2hsl, hsv2rgb rgb2hsv and hsl2hsv)

// input: h in [0,360] and s,v 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)];
} 

To calc hsl2hex use rgb2hex(...hsl2rgb(30,1,0.5)). To convert string from format e.g. rgb(255, 255, 255) to hex use rgbStrToHex (which handle empty string case) as follows

// 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('');
// hexStr e.g #abcdef, result "rgb(171,205,239)"
let hexStr2rgb = (hexStr) => `rgb(${hexStr.substr(1).match(/../g).map(x=>+`0x${x}`)})`;
// rgb - color str e.g."rgb(12,233,43)", result color hex e.g. "#0ce92b"
let rgbStrToHex= rgb=> '#'+rgb.match(/\d+/g).map(x=>(+x).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))}`);
console.log(`rgb: ${hexStr2rgb("#ff647b")} --> hex: ${rgbStrToHex("rgb(255,100, 123)")}`)
// ---------------
// UX
// ---------------
rgb= [0,0,0];
hs= [0,0,0];
let $ = x => document.querySelector(x);
function changeRGB(i,e) { rgb[i]= hs = rgb2hsl(...rgb); refresh();
}
function changeHS(i,e) { hs[i]=(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 type="range" min="0" max="255" oninput="changeRGB(0,event)">R<br>
<input type="range" min="0" max="255" oninput="changeRGB(1,event)">G<br>
<input type="range" min="0" max="255" oninput="changeRGB(2,event)">B<br>
<pre></pre>
</div>
<div>
<div></div>
</div>
<div>
<input type="range" min="0" max="360" oninput="changeHS(0,event)">H<br>
<input type="range" min="0" max="255" oninput="changeHS(1,event)">S<br>
<input type="range" min="0" max="255" oninput="changeHS(2,event)">L<br>
<pre></pre><br>
</div>
3

HSL to RGB:

/** * Converts an HSL color value to RGB. Conversion formula * adapted from * 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)]; }


You can find more information here - HSL to RGB color conversion

Another way to solve this problem is to leverage the window.getComputedStyle capability of modern browsers:

  1. Create an element on the page (it can be hidden, e.g. with display:none, but that appears to suppress output of the opacity / "A" value)

  2. Set a color-valued property of that element using the representation of your choice, e.g. e.style.color = 'hsla(100, 50%, 75%, 0.8)'; (or even named colors like 'rebeccapurple')

  3. Read the value back using window.getComputedStyle(e).color. It will be a string of the form rgb(r,g,b) or rgba(r,g,b,a).

Live demo on CodePen

I've made a small library that can easily convert colors.

This is my HSL to RGB method, which uses a few other utility methods from the library:

Color.hslToRgb = function(hsl, formatted) { var a, b, g, h, l, p, q, r, ref, s; if (isString(hsl)) { if (!hsl.match(Color.HSL_REGEX)) { return; } ref = hsl.match(/hsla?\((.+?)\)/)[1].split(',').map(function(value) { value.trim(); return parseFloat(value); }), h = ref[0], s = ref[1], l = ref[2], a = ref[3]; } else if ((isObject(hsl)) && (hasKeys(hsl, ['h', 's', 'l']))) { h = hsl.h, s = hsl.s, l = hsl.l, a = hsl.a; } else { return; } h /= 360; s /= 100; l /= 100; if (s === 0) { r = g = b = l; } else { q = l < 0.5 ? l * (1 + s) : l + s - l * s; p = 2 * l - q; r = Color.hueToRgb(p, q, h + 1 / 3); g = Color.hueToRgb(p, q, h); b = Color.hueToRgb(p, q, h - 1 / 3); } return getRgb(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), a, formatted);
};

If you don't want to use npm, the lib can also be found on GitHub.

1

I recently had cause to solve this problem and came up with a canvas-based solution. I'm logging it here for posterity only.

In my case I also needed to account for the cumulative effects on the conversion given a range of background colors and a semi-transparent alpha channel...

var HSL2COLOR = function () { return function (hsl, bg) { function checkHex(v) { return 1 === v.length ? '0'+v : v; } var data, r, g, b, a, cnv = document.createElement('canvas'), ctx = cnv.getContext('2d'), alpha = /a\(/.test(hsl), output = {}; return cnv.width = cnv.height = 1, bg && (ctx.fillStyle = bg, ctx.fillRect(0, 0, 1, 1)), ctx.fillStyle = hsl, ctx.fillRect(0, 0, 1, 1), data = ctx.getImageData(0, 0, 1, 1).data, r = data[0], g = data[1], b = data[2], a = (data[3] / 255).toFixed(2), alpha ? (output.hsla = hsl, bg ? output.rgb = 'rgb('+r+','+g+','+b+')' : output.rgba = 'rgb('+r+','+g+','+b+','+a+')') : (output.hsl = hsl, output.rgb = 'rgb('+r+','+g+','+b+')'), output.hex = '#'+checkHex(r.toString(16))+checkHex(g.toString(16))+checkHex(b.toString(16)), output; };
}();
// hsl: no alpha-channel + no background color
console.log(HSL2COLOR('hsl(170, 60%, 45%)'));
/*=> { hsl: "hsl(170, 60%, 45%)", rgb: "rgb(45,183,160)", hex: "#2db7a0" }
*/
// hsla: alpha-channel + no background color
console.log(HSL2COLOR('hsla(170, 60%, 45%, 0.35)'));
/*=> { hsla: "hsla(170, 60%, 45%, 0.35)", rgba: "rgb(42,183,160,0.35)", hex: "#2ab7a0" }
*/
// hsla: alpha-channel + background color
console.log(HSL2COLOR('hsla(170, 60%, 45%, 0.35)','#f00'));
/*=> { hsla: "hsla(170, 60%, 45%, 0.35)", rgb: "rgb(181,64,56)", hex: "#b54038" }
*/

As you can see from the results above, HEX values are not particularly representative when there is an alpha-channel in the input but no background color specified - as the canvas basically sees a transparent background as black. Nonetheless, the rgba value remained coherent.

Anyway, I achieved what I needed to, and perhaps this will be of some use to someone, sometime.

BP

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy