Skip to content

Instantly share code, notes, and snippets.

@sc0ttj
Last active November 14, 2021 00:57
Show Gist options
  • Save sc0ttj/a024df2b447ae3f655658354c44f3b9d to your computer and use it in GitHub Desktop.
Save sc0ttj/a024df2b447ae3f655658354c44f3b9d to your computer and use it in GitHub Desktop.
javascript colour conversion stuff

colour stuff

General

canvas specific

// random HSL colour
c.fillStyle = 'hsl('+ 360*Math.random() +',100%,50%)';

See

HSL

Red is 0°, green is 120° and blue is 240°.

See

Animating between colours

See

// pass in hex colours, without the #
function interpolateColor(c0, c1, f){
    c0 = c0.match(/.{1,2}/g).map((oct)=>parseInt(oct, 16) * (1-f))
    c1 = c1.match(/.{1,2}/g).map((oct)=>parseInt(oct, 16) * f)
    let ci = [0,1,2].map(i => Math.min(Math.round(c0[i]+c1[i]), 255))
    return ci.reduce((a,v) => ((a << 8) + v), 0).toString(16).padStart(6, "0")
}
// using HSL colors
function percentageToColor(percentage, maxHue = 120, minHue = 0) {
  const hue = percentage * (maxHue - minHue) + minHue;
  return `hsl(${hue}, 100%, 50%)`;
}
// usage: lerpColor('#000000', '#ffffff', 0.5)
function lerpColor(a, b, amount) { 

    var ah = +a.replace('#', '0x'),
        ar = ah >> 16, ag = ah >> 8 & 0xff, ab = ah & 0xff,
        bh = +b.replace('#', '0x'),
        br = bh >> 16, bg = bh >> 8 & 0xff, bb = bh & 0xff,
        rr = ar + amount * (br - ar),
        rg = ag + amount * (bg - ag),
        rb = ab + amount * (bb - ab);

    return '#' + ((1 << 24) + (rr << 16) + (rg << 8) + rb | 0).toString(16).slice(1);
}
// here's a version that just deals with hex literals
// usage: lerpColor(0x000000, 0xffffff, 0.5)
const lerpColor = function(pFrom, pTo, pRatio) {
  const ar = (pFrom & 0xFF0000) >> 16,
    ag = (pFrom & 0x00FF00) >> 8,
    ab = (pFrom & 0x0000FF),

    br = (pTo & 0xFF0000) >> 16,
    bg = (pTo & 0x00FF00) >> 8,
    bb = (pTo & 0x0000FF),

    rr = ar + pRatio * (br - ar),
    rg = ag + pRatio * (bg - ag),
    rb = ab + pRatio * (bb - ab);

  return (rr << 16) + (rg << 8) + (rb | 0);
};

Generate random colours

// RGB from string
const [r, g, b] = rgbStr.slice(4, -1).split(',').map(Number)


const randomHsl = () => `hsla(${Math.random() * 360}, 100%, 50%, 1)`
const randomRGB = () => `rgb(${[1,2,3].map(x=>Math.random()*256|0)})`
const randomHex = () => '#'+(Math.random()*0xFFFFFF<<0).toString(16);
const randomHex = () => Math.floor(Math.random()*16777215).toString(16);


// https://helderesteves.com/generating-random-colors-js/
function generateRandomColorHex() {
  return "#" + ("00000" + Math.floor(Math.random() * Math.pow(16, 6)).toString(16)).slice(-6);
}

function generateRandomColorRgb() {
  const red = Math.floor(Math.random() * 256);
  const green = Math.floor(Math.random() * 256);
  const blue = Math.floor(Math.random() * 256);
  return "rgb(" + red + ", " + green + ", " + blue + ")";
}

function generateRandomColorHsl() {
  const hue = Math.floor(Math.random() * 360);
  const saturation = Math.floor(Math.random() * (100 + 1)) + "%";
  const lightness = Math.floor(Math.random() * (100 + 1)) + "%";
  return "hsl(" + hue + ", " + saturation + ", " + lightness + ")";
}


function get_random_hsl() {
  const rand = (min, max) => min + Math.random() * (max - min),
        h = rand(1, 360),
        s = rand(0, 100),
        l = rand(0, 100);

  return 'hsl(' + h + ',' + s + '%,' + l + '%)';
}

const pastels = () => {
  const round = Math.round,
        randm = Math.random,
        n = 127,
        r = (round(randm()* n)+n).toString(16),
        g = (round(randm()* n)+n).toString(16),
        b = (round(randm()* n)+n).toString(16);
  return '#'+r+g+b;
}

// Generate vibrant, "evenly spaced" colours (no clustering).
// This is ideal for creating easily distinguishable vibrant colours.
// 30 random hues with step of 12 degrees
const 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)}`;
}

const rainbow = (deg=12) => {
  const hue = Math.floor(Math.random() * 30) * deg;
  return hslToHex(hue, 0.9, 0.6);
};

// another version: by Adam Cole, 2011-Sept-14
function rainbow(numOfSteps, step) {
  // HSV to RBG adapted from: http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
  const h = step / numOfSteps;
  let r, g, b;
  let i = ~~(h * 6);
  let f = h * 6 - i;
  let q = 1 - f;
  switch(i % 6){
    case 0: r = 1; g = f; b = 0; break;
    case 1: r = q; g = 1; b = 0; break;
    case 2: r = 0; g = 1; b = f; break;
    case 3: r = 0; g = q; b = 1; break;
    case 4: r = f; g = 0; b = 1; break;
    case 5: r = 1; g = 0; b = q; break;
  }
  var c = "#" + ("00" + (~ ~(r * 255)).toString(16)).slice(-2) + ("00" + (~ ~(g * 255)).toString(16)).slice(-2) + ("00" + (~ ~(b * 255)).toString(16)).slice(-2);
  return (c);
}


function randomHex() {
  const letters = '0123456789ABCDEF';
  const color = '#';
  for (let i = 0; i < 6; i++) {
    color += letters[Math.floor(Math.random() * 16)];
  }
  return color;
}


const randomHex = function() {
	let color = '#', i = 5;
	do {
	  color += "0123456789abcdef".substr(Math.random() * 16,1);
	} while (i--);
	return color;
}


function randomRgbaString (alpha) {
  let r = Math.floor(Math.random() * 255)
  let g = Math.floor(Math.random() * 255)
  let b = Math.floor(Math.random() * 255)
  let a = alpha
  return `rgba(${r},${g},${b},${a})`
}


// https://mika-s.github.io/javascript/colors/hsl/2017/12/05/generating-random-colors-in-javascript.html
/
// VERY NICE - even spread of random colours across spectrum!
//           - each colour can then be adjusted by saturation/lightness/transparency later
// Usage:
//
//     let c = generateHslaColors(50, 100, 1.0, 3)
//
function generateHslaColors (saturation, lightness, alpha, amount) {
  let colors = []
  let huedelta = Math.trunc(360 / amount)

  for (let i = 0; i < amount; i++) {
    let hue = i * huedelta
    colors.push(`hsla(${hue},${saturation}%,${lightness}%,${alpha})`)
  }

  return colors
}

Other color things

const toGreyscale(r,g,b) => {
  const brightness = (3*r+4*g+b)>>>3;
    return {
      r: brightness,
      g: brightness,
      b: brightness,
    }
  }
}

Convert color spaces

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

/**
 * 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];
}





// Shortest but precise
// 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)];
}   



// Example:
// hslToHex(360, 100, 50)  // "#ff0000" -> red
//
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)}`;
}





// rgb param is any array [0,0,255]
// returns HSLA colour
function rgbToHsla(rgb) {
  var a, add, b, diff, g, h, hue, l, lum, max, min, r, s, sat;
  r = parseFloat(rgb[0]) / 255;
  g = parseFloat(rgb[1]) / 255;
  b = parseFloat(rgb[2]) / 255;
  max = Math.max(r, g, b);
  min = Math.min(r, g, b);
  diff = max - min;
  add = max + min;
  hue = min === max ? 0 : r === max ? ((60 * (g - b) / diff) + 360) % 360 : g === max ? (60 * (b - r) / diff) + 120 : (60 * (r - g) / diff) + 240;
  lum = 0.5 * add;
  sat = lum === 0 ? 0 : lum === 1 ? 1 : lum <= 0.5 ? diff / add : diff / (2 - add);
  h = Math.round(hue);
  s = Math.round(sat * 100);
  l = Math.round(lum * 100);
  a = parseFloat(rgb[3]) || 1;
  return [h, s, l, a];
}

// Returns the color as an array of [r, g, b, a] -- all range from 0 - 255
// color must be a valid canvas fillStyle. This will cover most anything
// you'd want to use.
// Examples:
// colorToRGBA('red')  # [255, 0, 0, 255]
// colorToRGBA('#f00') # [255, 0, 0, 255]
function colorToRGBA(color) {
    var cvs, ctx;
    cvs = document.createElement('canvas');
    cvs.height = 1;
    cvs.width = 1;
    ctx = cvs.getContext('2d');
    ctx.fillStyle = color;
    ctx.fillRect(0, 0, 1, 1);
    return ctx.getImageData(0, 0, 1, 1).data;
}


// ...or just
function standardize_color(str) {
    var ctx = document.createElement('canvas').getContext('2d');
    ctx.fillStyle = str;
    return ctx.fillStyle;
}


// Turns a number (0-255) into a 2-character hex number (00-ff)
function byteToHex(num) {
    return ('0'+num.toString(16)).slice(-2);
}


// Convert any CSS color to a hex representation
// Examples:
// colorToHex('red')            # '#ff0000'
// colorToHex('rgb(255, 0, 0)') # '#ff0000'
function colorToHex(color) {
  let rgba, hex;
  rgba = colorToRGBA(color);
  hex = [0,1,2].map(function(idx) { return byteToHex(rgba[idx]); }).join('');
  return "#"+hex;
}



function nameToRGB(name) {
  // Create fake div
  let fakeDiv = document.createElement("div");
  fakeDiv.style.color = name;
  document.body.appendChild(fakeDiv);

  // Get color of div
  let cs = window.getComputedStyle(fakeDiv),
      pv = cs.getPropertyValue("color");

  // Remove div after obtaining desired color value
  document.body.removeChild(fakeDiv);

  return pv;
}

const rgb2hex = (rgb) => {
return (rgb && rgb.length === 3) ? "#" +
  ("0" + parseInt(rgb[0],10).toString(16)).slice(-2) +
  ("0" + parseInt(rgb[1],10).toString(16)).slice(-2) +
  ("0" + parseInt(rgb[2],10).toString(16)).slice(-2) : '';
}

function hexToHSL(hex) {
  var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
    r = parseInt(result[1], 16);
    g = parseInt(result[2], 16);
    b = parseInt(result[3], 16);
    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;
    }
  var HSL = new Object();
  HSL['h']=h;
  HSL['s']=s;
  HSL['l']=l;
  return HSL;
}

function rgbToHsl(r, g, b) {
  r /= 255; g /= 255; b /= 255;
  let max = Math.max(r, g, b);
  let min = Math.min(r, g, b);
  let d = max - min;
  let h;
  if (d === 0) h = 0;
  else if (max === r) h = (g - b) / d % 6;
  else if (max === g) h = (b - r) / d + 2;
  else if (max === b) h = (r - g) / d + 4;
  let l = (min + max) / 2;
  let s = d === 0 ? 0 : d / (1 - Math.abs(2 * l - 1));
  return [h * 60, s, l];
}

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 hslToRgb(h, s, l) {
  let c = (1 - Math.abs(2 * l - 1)) * s;
  let hp = h / 60.0;
  let x = c * (1 - Math.abs((hp % 2) - 1));
  let rgb1;
  if (isNaN(h)) rgb1 = [0, 0, 0];
  else if (hp <= 1) rgb1 = [c, x, 0];
  else if (hp <= 2) rgb1 = [x, c, 0];
  else if (hp <= 3) rgb1 = [0, c, x];
  else if (hp <= 4) rgb1 = [0, x, c];
  else if (hp <= 5) rgb1 = [x, 0, c];
  else if (hp <= 6) rgb1 = [c, 0, x];
  let m = l - c * 0.5;
  return [
    Math.round(255 * (rgb1[0] + m)),
    Math.round(255 * (rgb1[1] + m)),
    Math.round(255 * (rgb1[2] + m))
  ];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment