Andrew Deniszczyc Andrew Deniszczyc - 4 months ago 69
Javascript Question

Javascript - Sort rgb values

Using javascript/jquery, I want to sort an array of rgba values to the colours of the visible spectrum. By doing this, like shades should be bunched together. Is there a plugin to do this or how would I go about doing it?

Spectrum image: http://www.gamonline.com/catalog/colortheory/images/spectrum.gif

Answer

If your array of colors is like this:

var rgbArr = [c1, c2, c3, ...]

where each color ci is an array of three numbers between 0 and 255

ci = [red, green, blue]

then, you can use this function to convert the colors to HSL

function rgbToHsl(c) {
  var r = c[0]/255, g = c[1]/255, b = c[2]/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 new Array(h * 360, s * 100, l * 100);
}

and sort them by hue

var sortedRgbArr = rgbArr.map(function(c, i) {
  // Convert to HSL and keep track of original indices
  return {color: rgbToHsl(c), index: i};
}).sort(function(c1, c2) {
  // Sort by hue
  return c1.color[0] - c2.color[0];
}).map(function(data) {
  // Retrieve original RGB color
  return rgbArr[data.index];
});

Here is a runnable example (thanks Ionică Bizău):

function display(container, arr) {
  container = document.querySelector(container);
  arr.forEach(function(c) {
    var el = document.createElement("div");
    el.style.backgroundColor = "rgb(" + c.join(", ") + ")";
    container.appendChild(el);
  })
}
function rgbToHsl(c) {
  var r = c[0] / 255,
      g = c[1] / 255,
      b = c[2] / 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 new Array(h * 360, s * 100, l * 100);
}

var rgbArr = [];
for (var i = 0; i < 100; ++i) {
  rgbArr.push([
    Math.floor(Math.random() * 256),
    Math.floor(Math.random() * 256),
    Math.floor(Math.random() * 256)
  ]);
}
display("#before", rgbArr);

var sortedRgbArr = rgbArr.map(function(c, i) {
  // Convert to HSL and keep track of original indices
  return {color: rgbToHsl(c), index: i};
}).sort(function(c1, c2) {
  // Sort by hue
  return c1.color[0] - c2.color[0];
}).map(function(data) {
  // Retrieve original RGB color
  return rgbArr[data.index];
});
display("#after", sortedRgbArr);
#before > div,
#after > div {
  width: 1%;
  height: 20px;
  display: inline-block;
}
Random colors: <div id="before"></div>
Same colors, sorted by hue: <div id="after"></div>

sortedRgbArr will contain the rgb colors of rgbArr sorted more or less like the colors of the visible spectrum.

The problem is that the HSL spectrum looks like this:

HSL spectrum

Your spectrum is weird because it doesn't have all colors, such as pink.

I guess that's because pink doesn't exist in the nature, it's a combination of the colors of the opposite extremes of light's spectrum. But we have it in rgb, so you have to decide where do you want it.

Moreover, it seems that your spectrum goes from lower to higher wavelength, not frequency. But then your spectrum is a reverse of HSL's spectrum.

Replace c1.color[0] - c2.color[0] with c2.color[0] - c1.color[0] if you want it like your spectrum.