5

I'm trying to convert a number between 1 and 16,777,215 to any colour format (RGB/HSL/HEX) that increments through the colour spectrum using Javascript/jQuery.

The number 16,777,215 is the total possible combinations of RGB(255,255,255) which is 32 bit colour.

I initially thought converting the value to a hex value using toString(16) would increment through the spectrum, however as the number increases it seems to work through different lightness values instead and flashes. An example of this unwanted behaviour is here http://jsfiddle.net/2z82auka/

var colour = 16777215;
window.setInterval(function(){
    colour -= 1000;
    $('body').css({background:'#' + colour.toString(16)});
}, 50);

How can I convert a value between 1 and 16777215 to a colour on the colour spectrum shown below?

Wanted outcome

CMYJ
  • 172
  • 1
  • 2
  • 7
  • Do you really want to go through *all* colors of the spectrum? (Your image only shows pretty saturated colors). That would include very bright and dark colors as well (which is why you have the flashes in the first place). Or do you just want the nicely saturated ones? – nils Apr 24 '15 at 15:48
  • 1
    Correct me if I'm wrong, but your spectrum is what's called "hue", which typically is measured in 360°. so you just have 360 color, plus additionally saturation and brightness... All these together add up, depending on bit depth, to those 16mio colors. But it seems like all you want to do is rotating the hue. – Daniel Apr 24 '15 at 15:55
  • See http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion and https://bgrins.github.io/TinyColor/ – Ruan Mendes Apr 24 '15 at 15:56
  • @Daniel The number 16,777,215 is the total possible combinations of RGB(255,255,255) which is 32 bit colour so I would like more definition than between 1 and 360 on a fixed saturation and lightness. – CMYJ Apr 24 '15 at 16:05
  • 3
    But that's not how color work. The spectrum youve posted is just displaying hue with 100% saturation and 100% brightnes. Which leaves you with 360 color tones to choose from with HSL (which some answers are referring to). If you really would iterate over all the 16mio colors, it would still go from dark to bright and back and forth. – Daniel Apr 24 '15 at 16:11
  • 2
    For the record, RGB(255,255,255) is 24bit, not 32bit. – digijim Apr 24 '15 at 17:08
  • @CMYJ Finally, I found a solution that fits your exact requirement with vibrant colors from 1 to 16,777,215, if you will indulge my solution below. – Drakes Apr 24 '15 at 17:29

10 Answers10

7

The code below will do exactly what you want - it'll give you vibrant colors of the color spectrum exactly as the image below, and to prove it, the demo will print out the integer values beside the color. The result will look like this. Please use the rainbow function in your setInterval code.

spectrum

var colours = 16777215;

function rainbow(numOfSteps, step) {
 var r, g, b;
 var h = 1 - (step / numOfSteps);
 var i = ~~(h * 6);
 var f = h * 6 - i;
 var 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 * 235)).toString(16)).slice(-2) + ("00" + (~ ~(g * 235)).toString(16)).slice(-2) + ("00" + (~ ~(b * 235)).toString(16)).slice(-2);
 return (c);
}

function render(i) {
 var item = "<li style='background-color:" + rainbow(colours, i) + "'>" + i + "</li>";
 $("ul").append(item);
}

function repeat(fn, times) {
 for (var i = 0; i < times; i+=10000) fn(i);
}

repeat(render, colours);
li {
  font-size:8px;
  height:10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<ul></ul>

(I can't take credit for this code, but I can take credit for not giving up and settling for jerky color changes. Ref: https://gist.github.com/ruiwen/6163115)

Alberto Schiabel
  • 1,049
  • 10
  • 23
Drakes
  • 23,254
  • 3
  • 51
  • 94
  • This doesn't solve the OP's problem. They specifically mentioned that using the bytes gives them the ugly brown/black intermediate colors and want to scale their value into the color's hue only. – ssube Apr 24 '15 at 16:01
  • You certainly addressed the problems with your answer, so I'll happily retract the downvote and leave the OP to decide which is best. – ssube Apr 24 '15 at 17:33
  • @CMYJ Were you able to produce the colors you expected? – Drakes Jun 08 '15 at 04:13
2

Sticking to RGB: Always incrementing by one will not result in a steady grade through the spectrum. For example, when you go from #0000ff (which is blue) to that +1, you end up at #000100, which is essentially black.

Instead, you will probably want to do something more like incrementing each of the three values (the R value, the G value, and the B value) by one. However, that will omit many, many colors. But if smoothness is what you value over comprehensiveness, that's a simple way to get there.

@nada points out that this will give you an awful lot of grey. If you want to avoid that, you can try variations like: increment R until it can't be incremented anymore. Leave it at max value while you increment G until it hits max, then increment B to max. Now reverse it: Decrement R to minimum, then G, then B. This will still miss a ton of colors (in fact, it will miss most colors), but it should be smooth and it should avoid being nothing but grey.

Although this will work (if you don't mind missing most colors), I'm sure there is a better solution. I hope someone weighs in with it. I'm very curious.

Trott
  • 66,479
  • 23
  • 173
  • 212
2

Convert to range the initial value from 1 > 16777216 from 0 > 360

Technique here: Convert a number range to another range, maintaining ratio

Then use the HSL colour model, and increment from H0 S100 L100 > H360 S100 L100

Community
  • 1
  • 1
Jack Wild
  • 2,072
  • 6
  • 27
  • 39
2

You have the hue value, so you need to turn that into the various color formats using fixed brightness and saturation.

To properly scale the hue from [1, 16777215] to a [0, 1] scale, you'll need to do (x - 1) / 16777215. Take this number and feed it into hsl2rgb (here's a JS implementation) with a high lum and relatively high sat.

Something like so:

// From this answer: https://stackoverflow.com/a/9493060/129032
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)];
}

function scaleHue(hue) {
  return ((hue - 1) / 16777215);
}

var colour = 0;
window.setInterval(function() {
  colour = (colour + 100000) % 16777215;
  var hue = scaleHue(colour);
  var current = hslToRgb(hue, 0.8, 0.8);
  $('body').css({
    background: '#' + current[0].toString(16) + current[1].toString(16) + current[2].toString(16)
  });
}, 50);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

I increased the step from 1000 to 100000 to make the demo more obvious.

Community
  • 1
  • 1
ssube
  • 47,010
  • 7
  • 103
  • 140
  • Thanks, but I'd like more definition than a hue between 1 and 360. 16,777,215 is the total possible combinations of RGB 255 255 255 so I would like to use it all. – CMYJ Apr 24 '15 at 16:07
  • @CMYJ Your spectrum shows fully saturated color with full brightness (i.e., in your image, only the hue changes). This code takes your 16.7m values and converts them to, roughly, 16.7m different variations of hue. Anything else will include the black and brown values that you want to avoid. – ssube Apr 24 '15 at 16:28
  • There are no 16.7m different hues. They only difffer in saturation and brightness so that is how you end up with 16.7m. You said you do not want to see changes in brightness and saturation though. – xuma202 Apr 24 '15 at 16:52
1

This works for me...

export function intToHex(colorNumber)
{
    function toHex(n) {
      n = n.toString(16) + '';
      return n.length >= 2 ? n : new Array(2 - n.length + 1).join('0') + n;
    }

    var r = toHex(Math.floor( Math.floor(colorNumber / 256) / 256 ) % 256),
        g = toHex(Math.floor( colorNumber / 256 ) % 256),
        b = toHex(colorNumber % 256);
    return '#' + r + g + b; 
}
DeNitE Appz
  • 1,003
  • 2
  • 12
  • 18
0

Generally, this is the formula for converting an integer to rgba

r = (val)&0xFF;
g = (val>>8)&0xFF;
b = (val>>16)&0xFF;
a = (val>>24)&0xFF;

Expressed as javascript

function ToRGBA(val){
   var r = (val)&0xFF;
   var g = (val>>8)&0xFF;
   var b = (val>>16)&0xFF;
   var a = (val>>24)&0xFF;   
   return "rgb(" + r + "," + g + "," + b + ")";
}

Updated fiddle: http://jsfiddle.net/2z82auka/2/

Jamiec
  • 133,658
  • 13
  • 134
  • 193
0

Something like that ?

<script>
function intToHex(colorNumber) {
    var R = (colorNumber - (colorNumber%65536)) / 65536;
    var G = ((colorNumber - R*65536) - ((colorNumber - R*65536)%256)) / 256;
    var B = colorNumber - R*65536 - G*256;
    var RGB = R.toString(16) + G.toString(16) + B.toString(16);
    return RGB;
}
</script>
legui
  • 194
  • 1
  • 5
0

Marrying this answer with Drake's:

function colorNumberToHex(colorNumber) {
    function toHex(n) {
      n = n.toString(16) + '';
      return n.length >= 2 ? n : new Array(2 - n.length + 1).join('0') + n;
    }

    var r = toHex(colorNumber % 256),
        g = toHex(Math.floor( colorNumber / 256 ) % 256),
        b = toHex(Math.floor( Math.floor(colorNumber / 256) / 256 ) % 256);
    return '#' + r + g + b; 
}
Community
  • 1
  • 1
mostlydev
  • 715
  • 6
  • 16
0

The picture you've shown suggests you really just want to rotate through a set of continuous colors, not every possible rgb color (since many of them essentially look white or black). I would suggest using HSV as a base instead of RGB. Trying to increment a number that represents an RGB value will lead to the stuttering you see (like @Trott pointed out, going from 0000ff to 000100 jumps from a blue to a black).

Try something like this (Fiddle):

$(document).ready(function(){

    var h = 0;
    window.setInterval(function(){
        h += .01;
        if (h >= 1) h-=1;

        var rgbColor = HSVtoRGB(h, 1, 1);
        var colorString = '#' + convertComponentToHex(rgbColor.r)
                                + convertComponentToHex(rgbColor.g) 
                                + convertComponentToHex(rgbColor.b);
        $('body').css({background:colorString});
    }, 50);
});
function convertComponentToHex(v) {
    return ("00" + v.toString(16)).substr(-2);
}
function HSVtoRGB(h, s, v) {
    var r, g, b, i, f, p, q, t;
    if (h && s === undefined && v === undefined) {
        s = h.s, v = h.v, h = h.h;
    }
    i = Math.floor(h * 6);
    f = h * 6 - i;
    p = v * (1 - s);
    q = v * (1 - f * s);
    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: Math.floor(r * 255),
        g: Math.floor(g * 255),
        b: Math.floor(b * 255)
    };
}

(Thanks to this SO answer for the conversion code. I was too lazy to go figure it out for myself.)

Community
  • 1
  • 1
Becuzz
  • 6,846
  • 26
  • 39
0

My implementation....

var r = 255;
var g = 0;
var b = 0;
var stage = 1;
var step = 5;

var int = setInterval(function () {
    if (stage == 1) {
        g += step;
        if (g >= 255) {
            g = 255;
            stage = 2;
        }
    } else if (stage == 2) {
        r -= step;
        if (r <= 0) {
            r = 0;
            stage = 3;
        }
    } else if (stage == 3) {
        b += step;
        if (b >= 255) {
            b = 255;
            stage = 4;
        }
    } else if (stage == 4) {
        g -= step;
        if (g <= 0) {
            g = 0
            stage = 5;
        }
    } else if (stage == 5) {
        r += step;
        if (r >= 255) {
            r = 255;
            stage = 6;
        }
    } else if (stage == 6) {
        b -= step;
        if (b <= 0) {
            b = 0;
            clearInterval(int);
        }
    }

    //console.log(r,g,b);
    $('body').css('background-color', 'RGB('+r+','+g+','+b+')');
}, 10);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Populus
  • 7,470
  • 3
  • 38
  • 54