44

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?

10 Answers 10

84

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
Sign up to request clarification or add additional context in comments.

4 Comments

Thank you, this works perfectly. But can you explain the nature of the n variable in the f function?
I can't :(. The wiki article explains it a bit, but this is way too advanced math+trigonometry for me :)
Fair enough, I'm in the same boat. But it does seem to work well, I was just hoping to document the function in case someone needs to tweak it later. Thank for the article link.
The parameter "n" acts as an offset that shifts the hue to calculate each specific RGB component. Values of n for RGB Components: Red (R): n = 0 Green (G): n = 8 Blue (B): n = 4 . 0,8,4 are used becuase on line "const k = (n + h / 30) % 12;" you can see we split k into 12 different angles of 30degree intervals. 0,4, and 8 are the three unqiue and evenly spaced angles for k. 12 is not unqiue becuase that would represent a full 360 which is equivalent to 0.
36

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]=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>

5 Comments

rgb2hex expects values between 0 and 1? Most common formats are 0-255 and % (That is CSS colors)
@Kaiido OP wants to convert hsl to hex - he can easily do it by e.g. rgb2hex(...hsl2rgb(30,1,0.5)) however it is also easy to change that function to process [0-255] values by change Math.ceil(x*255) to Math.ceil(x) in above code.
Sure I got it, my point is that calling it rgb2hex is a bit misleading when you can't pass directly what usual values for rgb are.
This function code is way unreadable. Please prefer to write clean code and let the bundler minify it when it needs to.
@ChristosLytras I used formulas and notations in accordance with wikipedia.
21

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)];
    }


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

Comments

7

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

Comments

2

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

Comments

1

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.

2 Comments

If you want to give people another option besides NPM since this appears to be isomorphic, you could publish it on bower.
Not a solution, it does not work
1

Or you could npm install color-convert and not re-invent the wheel.

const convert = require('color-convert')
const hex = '#' + convert.hsl.hex(96, 48, 59) // convert hsl to hex
const rgb = convert.hsl.rgb(96, 48, 59) // convert hsl to rgb

Life can be simple at times.

Comments

0

from @icl7126 's answer,

you can add another function param to include alpha

function hsl2hex(h,s,l,alpha) {
    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
    };
  //alpha conversion
  alpha = Math.round(alpha * 255).toString(16).padStart(2, '0');

  return `#${f(0)}${f(8)}${f(4)}${alpha}`;
} 

Comments

0

Final solution:

function stringAsColor(string, saturation = 100, lightness = 50) {
    let hash = 0;
    for (let i = 0; i < string.length; i++) {
        hash = string.charCodeAt(i) + ((hash << 5) - hash);
        hash = hash & hash;
    }
    const hslToHex = function (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');
        };
        return ['#', f(0), f(8), f(4)].join('');
    };
    return hslToHex(Math.abs(hash % 360), saturation, lightness);
}

Ussage:

stringAsColor('abc');          // -> '#0019ff'
stringAsColor('abc', 100, 20); // -> '#000a66'

Comments

-1

hex to color picker and hsl to hex convert online color picker

$(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;
        }
    });
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.