Convert HSB/HSV color to HSL

Ho do I convert HSB color to HSL?

Photoshop shows HSB color in its color picker. HSL color can be used in CSS.

I tried this JS:

function hsb2hsl(h, s, b) {
  return {
    h: h,
    s: s,
    l: b-s/2
  }
}

But hsb2hsl(0, 100, 50).l == 0 instead of 25

Update: Can I do that without converting HSB → RGB → HSL?


Solution 1:

Short but precise

Try this (s,v,l in [0,1], more: hsv2rgb rgb2hsv and hsl2rgb rgb2hsl)

let hsl2hsv = (h,s,l,v=s*Math.min(l,1-l)+l) => [h, v?2-2*l/v:0, v];

let hsv2hsl = (h,s,v,l=v-v*s/2, m=Math.min(l,1-l)) => [h,m?(v-l)/m:0,l];

let hsv2hsl = (h,s,v,l=v-v*s/2,m=Math.min(l,1-l)) => [h,m?(v-l)/m:0,l];
let hsl2hsv = (h,s,l,v=s*Math.min(l,1-l)+l) => [h, v?2-2*l/v:0, v];

console.log("hsv:["+ hsl2hsv(30,1,0.6) +"] hsl:["+ hsv2hsl(30,0.8,1) +"]");


// -------------------
// UI code
// -------------------

let $ = x => document.querySelector(x);
let c = (x,s) => $(x).style.backgroundColor=s;
let hsl=[0,1,0.5];
let hsv=hsl2hsv(...hsl);

let refreshHSV =(i,e) => {
   hsv[i]= e.target.value/(i?100:1);
   hsl=hsv2hsl(...hsv);
   refreshView();
}

let refreshHSL =(i,e) => {
   hsl[i]= e.target.value/(i?100:1);
   hsv=hsl2hsv(...hsl);  
   refreshView();
}

let hsv2rgb = (h,s,v) => {                              
  let f= (n,k=(n+h/60)%6) => v - v*s*Math.max( Math.min(k,4-k,1), 0);     
  return [f(5),f(3),f(1)];       
}

let refreshView = () => {
   let a= [hsl[0], (hsl[1]*100).toFixed(2), (hsl[2]*100).toFixed(2)]; 
   let b= [hsv[0], (hsv[1]*100).toFixed(2), (hsv[2]*100).toFixed(2)]; 
   
   let r= hsv2rgb(...hsv).map(x=>x*255|0);
   let ta= `hsl(${a[0]},${a[1]}%,${a[2]}%)`
   let tb= `hsv(${b[0]},${b[1]}%,${b[2]}%)`
   let tr= `rgb(${r[0]},${r[1]},${r[2]})`
   
   c('.hsl', tr);   
   $('#sv').value=hsv[1]*100;
   $('#v').value =hsv[2]*100;
   $('#sl').value=hsl[1]*100;
   $('#l').value =hsl[2]*100;
   $('.info').innerHTML=`${tr}\n${tb}\n${ta.padEnd(25)}`;   
}



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

body {
    display: flex;
    background: white;
}
<div>
<input id="h" type="range" min="0" max="360" value="0" oninput="refreshHSV(0,event)">Hue<br>
<div class="box hsl"></div>
<pre class="info"></pre>
</div> 

<div>
<input id="sv" type="range" min="0" max="100" value="0" oninput="refreshHSV(1,event)">HSV Saturation<br>
<input id="v" type="range" min="0" max="100" value="100" oninput="refreshHSV(2,event)">HSV Value<br><br><br>
<input id="sl" type="range" min="0" max="100" value="0" oninput="refreshHSL(1,event)">HSL Saturation<br>
<input id="l" type="range" min="0" max="100" value="100" oninput="refreshHSL(2,event)">HSL Lightness<br>
</div>

This code based on formulas which I discover and write on wiki

enter image description here

Solution 2:

I think this is the most precise:

function hsv_to_hsl(h, s, v) {
    // both hsv and hsl values are in [0, 1]
    var l = (2 - s) * v / 2;

    if (l != 0) {
        if (l == 1) {
            s = 0;
        } else if (l < 0.5) {
            s = s * v / (l * 2);
        } else {
            s = s * v / (2 - l * 2);
        }
    }

    return [h, s, l];
}