Loop through all colors

The Problem:

I am trying to create a program that can loop through all colors in order from 0x000000 to 0xFFFFFF. I want to the transition smoothly, such as on this color picker.


My code:

My idea was to loop from 0 to 16777215 in base 10 and convert it to a base 16 hex and use it for the background color of my codepen (below)

(Eplisey warning)

I used accomplished this by creating a setInterval function that can loop between two numbers in a range infinitely:

function backgroundColor(hexStartColor, min, max, add, delay) {
  let currentColor = hexStartColor
  let hitMax = false
  setInterval(() => {
    if (currentColor >= max) {
      hitMax = true
    }
    if (currentColor <= min) {
      hitMax = false
    }
    if (hitMax && currentColor - add < 0x0) {
      hitMax = false
      currentColor = min
    } else if (!hitMax && currentColor + add > 0xFFFFFF) {
      hitMax = true
      currentColor = max
    } 
    hitMax ? currentColor -= add : currentColor += add
    console.log(currentColor)
    let hexString = currentColor.toString(16)
    while (hexString.length < 6) {
      hexString = "0"+hexString
    }
    console.log(hexString)
  }, delay)
}
backgroundColor(0x0, 0x0, 0xFFFFFF, 100, 20); 

You can input the start color, min, max, add (rate of change), and the interval delay.

hitMax is used to check weather you should be counting up or down. If you hit the end of your range (or past 0xFFFFFF), it sets the value to true and counts down. It also has various other checks to make sure you don’t go past or below the possible colors.

In my pen, I use the following function to set the background color (jQuery)

$('body').css('background-color', '#'+currentColor.toString(16))

Issues

One issue is that any hexadecimal values less then or equal to 0x0000FF doesn’t display any color.

Most importantly, the colors also seem to jump and the transition does not seem smooth. It doesn’t go around the edge of the color wheel, it just slices through it.

image

Any way I can fix the logic and code to make it smoother?

for the best effect of transition between pure colors you need to use hsl color, and it seems that is what the line does, going from 0° to 360° in HSV or HSL

2 Likes

I tried to make the same program but using HSL. The code works by starting at zero saturation and light. It increases the saturation by one, but for every value of satuartion it creates 100 values. After saturation is increased 100 times, it increases the degree by one and then starts counting the saturation down (creating 100 lightness values for every value of saturation)

For example:

deg 0, sat 0, light 0
deg 0, sat 0, light 1
…
deg 0, sat 0, light 99
deg 0, sat 0, light 100
deg 0, sat 1, light 99
deg 0, sat 1, light 98
…
etc until sat equals 100, where the deg added and sat starts counting down


One issue I came across is that I used for loops to update the value of light 100 times, which caused it to instantly change for each saturation value. Then the saturation would follow the delay when increased. I want it so the light also follows the same delay as the saturation.

I tried using two setIntervals inside of one another, but since they are async the main setInterval doesn’t wait for the light one to complete before moving on. I then tried researching promises, but the code just doesn’t work at all.

Repl code: https://replit.com/@Michael_Nicol/Loop-Through-All-Colors

async function backgroundColor(startDeg, delay) {
  let deg = startDeg
  let sat = 0;
  let light = 0;
  let increaseSat = true;
  let increaseLight = true;
  setInterval(() => {
    // If the user inputs a startDeg out of the HSL cylinder range
    if (deg > 360 || sat < 0) {
      deg = 0;
    }
    if (sat == 100) {
      increaseSat = false
      deg++
    }
    if (sat == 0) {
      increaseSat = true
    }
    if (light == 0) {
      async function runInterval() {
        return new Promise(resolve => {
          let intervalID = setInterval(() => {
            console.log("Counting up light")
            light++
            let hslString = "hsl(" + deg + "," + sat + "%," + light + "%)"
            console.log(hslString)
            if (light == 100) {
              console.log("Clearing up light")
              clearInterval(intervalID)
              resolve()
            }
          }, delay)
        })
      }
   // if i just do await runInterval(), I get a error 'await only allowed in async function'.
      (async () => { await runInterval() })
    } else if (light == 100) {
      async function runInterval() {
        return new Promise(resolve => {
          let intervalID = setInterval(() => {
            console.log("Counting down light")
            light--
            let hslString = "hsl(" + deg + "," + sat + "%," + light + "%)"
            console.log(hslString)
            if (light == 0) {
              console.log("Clearing down light")
              clearInterval(intervalID)
              resolve()
            }
          }, delay)
        })
      }
      (async () => { await runInterval() })
    }
    console.log("increasing sat")
    increaseSat ? sat++ : sat--
  }, delay)
}
backgroundColor(0, 1000)

You seem to be trying to loop through hues and tints/shades, that doesn’t quite make sense. You can’t really loop through all the colours, what your code will be doing is rotating through the hues then moving the lightness up and down on each one:

  • if done fast, it’ll look like a strobe light
  • if done slowly, the screen will be white or black most of the time, as most of the “colours” will be mainly that.

re your hex attempt:

You’re assuming that the colour wheel is something concrete. It’s not, it’s just an arbitrary picture of some colours to make it easy to understand how they relate to one another. It’s extremely useful, but it’s also extremely naïve. Also, the one you’ve posted is used for illustrating additive colour – ie you are painting a picture and you want to figure out what to mix to get a specific colour.

Hex colours are just RGB colours encoded in a different base – so three digits in the range 0-255, 0-255, 0-255 are encoded as hex. eg:

  • r is 188 in base 10. In base 16, that’s bc
  • g is 227 in base 10. In base 16, that’s e3
  • b is 245 in base 10. In base 16, that’s f5
  • so the hex code is #bce3f5. Three numbers.

Firstly, if you just treat it as a single number then yeah you’ll get weird results because it’s not a single number. It’s like taking a date, say 2021/12/28, treating it as one number (20211228), adding ten to it, and expecting the result to be the date ten days after (2022/01/07 rather than 2021/12/38).

Secondly, this triangle is the colour gamut available for RGB (what colours the model can represent):

Normally it’s displayed as a 2D shape in a colour picker (a rectangle, circle, or as a triangle) but that’s not particularly accurate – 3D representations are more useful. Anyway, it’s not a line, not just a linear series of increasing values. Even if just increasing the hex number as one worked, you can’t just loop over it and get anything sensible back.


So HSL, this makes more sense, ish. It’s the only sensible way to do what you’re trying to do. HSL is a version of RGB where the values are mapped onto a (3D) cylinder. It does look like that colour wheel, the point is that it kinda works more like painting.

The first value is degrees, which moves around the the cylinder. The second is the saturation, which moves along the radius. The third is lightness which moves along the z-axis (depth).

These are all discrete which is why I said what you’re trying to do doesn’t make a lot of sense if you actually mean loop through all the colours.

The picker you posted will likely be just rotating the hue value, not iterating through anything else. Then if a user changes the saturation/lightness in the picker, whatever they pick will stick, and those values will be the same if they change the hue.


Anyway, can do what you’re trying to do easily via the web animations API (same as CSS animations, but doing it in JS makes it easier to loop over things and generate all the transitions). Example:

1 Like

Thank you both @ilena and DanCouper for the help. The responses have made me understand how colors work well in relation to computer values.

As you stated, its best to go with Javascript animations rather then trying to loop through all the colors somehow. I have decided to go with background images instead for the project due to creating a more pleasing to look at and less distracting page.

However, this is interesting and I will use it for the future.

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.