HSL to RGB

by Lamik

HTML

<div>
  <button class="btn" onclick="setColIndexChart(0)">Chart R</button>
  <button class="btn" onclick="setColIndexChart(1)">Chart G</button>
  <button class="btn" onclick="setColIndexChart(2)">Chart B</button>
</div>
<div>Luminance <input type="range" min="0" max="100" value="50" oninput="setLum(event)"></div>
<div>Saturation <input type="range" min="0" max="100" value="100" oninput="setSat(event)"></div>

<div>Wiki-Opt max <span id="error"></span> </div>

<div id="labelOpt">Optimized</div>
<svg viewBox="0 0 360 60" class="chart">    
  <text x="0" y="59" class="axis">0</text>
  <text x="0" y="10" class="axis">1</text>
  <text x="320" y="59" class="axis">360 hue</text>
  <polyline id="chartA" fill="none" stroke="#0074d9" stroke-width="1" points=""/>
</svg>

<div id="labelWiki">Wiki</div>
<svg viewBox="0 0 360 60" class="chart">    
  <text x="0" y="59" class="axis">0</text>
  <text x="0" y="10" class="axis">1</text>
  <text x="320" y="59" class="axis">360 hue</text>
  <polyline id="chartB" fill="none" stroke="#0074d9" stroke-width="1" points=""/> 
</svg>

<div class="test">  
  <div id="colA" class="col"></div>
  <div id="colB" class="col"></div>
</div>

CSS

.col { margin: 5px; width: 50px; height: 50px; }
#btn { display: block; margin: 10px; }
.test { display: flex; height: 400px}
.color { width: 50px; height: 1px; }
.chart { border: 1px solid black; }
.axis { font: 10px sans-serif;}
#labelOpt { margin-top: 20px }

JavaScript

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

function hsl2rgb_wiki(h,s,l) {            
  let c = (1-Math.abs(2*l-1))*s; 
  let k = h/60;
  let x = c*(1 - Math.abs(k%2-1));
 
  let r1 = g1 = b1 = 0;
    
  if(k>=0 && k<=1) { r1=c; g1=x }
  if(k>1 && k<=2)  { r1=x; g1=c }
  if(k>2 && k<=3)  { g1=c; b1=x }
  if(k>3 && k<=4)  { g1=x; b1=c }
  if(k>4 && k<=5)  { r1=x; b1=c }
  if(k>5 && k<=6)  { r1=c; b1=x }
  
  let m = l - c/2; 

  return [r1+m,g1+m,b1+m]  
} 

// ----------------------------
// TEST code
// ----------------------------

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

function calcMaxError() {
		s = colorSat;
  	l = colorLum;
		err = [];
    
    for(let h=0; h<361; h++) {
      o = hsl2rgb(h,s,l);
      w = hsl2rgb_wiki(h,s,l); 
      e = Math.max( Math.abs(o[0]-w[0]), Math.abs(o[1]-w[1]), Math.abs(o[2]-w[2]));
      err.push({ h, e });
    }
    
    err.sort((a,b)=> b.e-a.e);
    $('#error').innerHTML = `err=${err[0].e} for hue=${err[0].h}&deg`
}

function setColors(h,s,l) {    
    opt = hsl2rgb(h,s,l);
    wiki = hsl2rgb_wiki(h,s,l); 
    
    
    chartAA( h, opt[colorIdx]); 
    chartBB( h, wiki[colorIdx]);    
    $('#colA').insertAdjacentHTML('beforeend', `<div class="color" style="background: ${rgbToStyle(opt)}"</div>`);
    $('#colB').insertAdjacentHTML('beforeend',`<div class="color" style="background: ${rgbToStyle(wiki)}"</div>`);    
}

function refresh() {  
  s = colorSat;
  l = colorLum;
  $('#colA').innerHTML = "opt";
  $('#colB').innerHTML = "wiki";
  chartAA(0,0,1); chartBB(0,0,1);                 // clean charts
  let info =  ['R','G','B'][colorIdx] 
   + ` (lum: ${colorLum}, sat: ${colorSat})`;  
  $('#labelWiki').innerHTML = "Wiki " + info;
  $('#labelOpt').innerHTML = "Optimized " + info;
  
  calcMaxError();
  for(let h=0; h<361; h++) setColors(h,s,l);    
}

function rgbToStyle(c) {
    return...