Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
RGB, HSV, and HSL color conversion algorithms in JavaScript
/**
* 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 ];
}
/**
* 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 {
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 [ r * 255, g * 255, b * 255 ];
}
/**
* Converts an RGB color value to HSV. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSV_color_space.
* Assumes r, g, and b are contained in the set [0, 255] and
* returns h, s, and v 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 HSV representation
*/
function rgbToHsv(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, v = max;
var d = max - min;
s = max == 0 ? 0 : d / max;
if (max == min) {
h = 0; // achromatic
} else {
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, v ];
}
/**
* Converts an HSV color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSV_color_space.
* Assumes h, s, and v 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 v The value
* @return Array The RGB representation
*/
function hsvToRgb(h, s, v) {
var r, g, b;
var i = Math.floor(h * 6);
var f = h * 6 - i;
var p = v * (1 - s);
var q = v * (1 - f * s);
var t = v * (1 - (1 - f) * s);
switch (i % 6) {
case 0: r = v, g = t, b = p; break;
case 1: r = q, g = v, b = p; break;
case 2: r = p, g = v, b = t; break;
case 3: r = p, g = q, b = v; break;
case 4: r = t, g = p, b = v; break;
case 5: r = v, g = p, b = q; break;
}
return [ r * 255, g * 255, b * 255 ];
}
@stephencweiss
Copy link

stephencweiss commented Dec 18, 2018

@isbyerley, came here to say the same thing. I think there's a mistake in the hsvToRgb. Here's my version:

function rgbFromHSV(h,s,v) {
  /**
   * I: An array of three elements hue (h) ∈ [0, 360], and saturation (s) and value (v) which are ∈ [0, 1]
   * O: An array of red (r), green (g), blue (b), all ∈ [0, 255]
   * Derived from https://en.wikipedia.org/wiki/HSL_and_HSV
   * This stackexchange was the clearest derivation I found to reimplement https://cs.stackexchange.com/questions/64549/convert-hsv-to-rgb-colors
   */

  hprime = h / 60;
  const c = v * s;
  const x = c * (1 - Math.abs(hprime % 2 - 1)); 
  const m = v - c;
  let r, g, b;
  if (!hprime) {r = 0; g = 0; b = 0; }
  if (hprime >= 0 && hprime < 1) { r = c; g = x; b = 0}
  if (hprime >= 1 && hprime < 2) { r = x; g = c; b = 0}
  if (hprime >= 2 && hprime < 3) { r = 0; g = c; b = x}
  if (hprime >= 3 && hprime < 4) { r = 0; g = x; b = c}
  if (hprime >= 4 && hprime < 5) { r = x; g = 0; b = c}
  if (hprime >= 5 && hprime < 6) { r = c; g = 0; b = x}
  
  r = Math.round( (r + m)* 255);
  g = Math.round( (g + m)* 255);
  b = Math.round( (b + m)* 255);

  return [r, g, b]
}

@david-tabor
Copy link

+1, used this for a coding bootcamp assignment, with attribution

@ashleedawg
Copy link

This minified variation takes integer r,g,b values (0 to 255) and returns an object of integers (°,%,%) like {h:360, s:100, l:50} :

function rgbToHsl(a,r,e){a/=255,r/=255,e/=255;var t,h,n=Math.max(a,r,e),s=Math.min(a,r,e),c=(n+s)/2;if(n==s)t=h=0;else{var o=n-s;switch(h=.5<c?o/(2-n-s):o/(n+s),n){case a:t=(r-e)/o+(r<e?6:0);break;case r:t=(e-a)/o+2;break;case e:t=(a-r)/o+4}t/=6}return{h:Math.round(360*t),s:Math.round(100*h),l:Math.round(100*c)}}

@gntlechaos
Copy link

If you have been looking for a variation that converts RGB to HSV with the hue ranging from 0 to 360 instead of 0 to 1, and the saturation and value ranging from 0 to 100, I have adapted this function, with this range re-mapping algorithm.

function mapMinMax(value,oldMin,oldMax,newMin, newMax) {
  return  (newMax-newMin)*(value-oldMin)/(oldMax-oldMin)+newMin;
}

function rgbToHsv(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, v = max;

    var d = max - min;
    s = max == 0 ? 0 : d / max;
    if (max == min) {
        h = 0; // achromatic
    } else {
        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;
    }

    h = mapMinMax(h,0,1,0,360);
    return [h, s * 100, v * 100];
}

@kigiri
Copy link

kigiri commented Jun 18, 2020

working with css string here did:

const _rgbToHsl = rgbStr => {
  const [r, g, b] = rgbStr.slice(4, -1).split(',').map(Number)
  const max = Math.max(r, g, b)
  const min = Math.min(r, g, b)
  const l = Math.floor((max + min) / ((0xff * 2) / 100))

  if (max === min) return [0, 0, l]
  const d = max - min
  const s = Math.floor((d / (l > 50 ? 0xff * 2 - max - min : max + min)) * 100)

  if (max === r) return [Math.floor(((g - b) / d + (g < b && 6)) * 60), s, l]
  return max === g
    ? [Math.floor(((b - r) / d + 2) * 60), s, l]
    : [Math.floor(((r - g) / d + 4) * 60), s, l]
}

It takes a css rgb string and returns parts in a css ready scale.

I got results similar to chrome conversion.

@avisek
Copy link

avisek commented May 21, 2022

RGB, HSL, XYZ, LAB, LCH color conversion algorithms (ES6 Modules)
https://gist.github.com/avisek/eadfbe7a7a169b1001a2d3affc21052e

@RafiXDD
Copy link

RafiXDD commented Mar 23, 2023

@isbyerley, came here to say the same thing. I think there's a mistake in the hsvToRgb. Here's my version:

function rgbFromHSV(h,s,v) {
  /**
   * I: An array of three elements hue (h) ∈ [0, 360], and saturation (s) and value (v) which are ∈ [0, 1]
   * O: An array of red (r), green (g), blue (b), all ∈ [0, 255]
   * Derived from https://en.wikipedia.org/wiki/HSL_and_HSV
   * This stackexchange was the clearest derivation I found to reimplement https://cs.stackexchange.com/questions/64549/convert-hsv-to-rgb-colors
   */

  hprime = h / 60;
  const c = v * s;
  const x = c * (1 - Math.abs(hprime % 2 - 1)); 
  const m = v - c;
  let r, g, b;
  if (!hprime) {r = 0; g = 0; b = 0; }
  if (hprime >= 0 && hprime < 1) { r = c; g = x; b = 0}
  if (hprime >= 1 && hprime < 2) { r = x; g = c; b = 0}
  if (hprime >= 2 && hprime < 3) { r = 0; g = c; b = x}
  if (hprime >= 3 && hprime < 4) { r = 0; g = x; b = c}
  if (hprime >= 4 && hprime < 5) { r = x; g = 0; b = c}
  if (hprime >= 5 && hprime < 6) { r = c; g = 0; b = x}
  
  r = Math.round( (r + m)* 255);
  g = Math.round( (g + m)* 255);
  b = Math.round( (b + m)* 255);

  return [r, g, b]
}

if h is equal to 360 this code shows NaN because you are not checking hprime for value 6 (360/60 = 6)
if (hprime >= 5 && hprime <= 6) { r = c; g = 0; b = x} "hprime <= 6" instead of "hprime < 6" fixes it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment