How do I cycle/lerp between multiple colors

Hi

I’m new to the world of Processing and I’ve turned to the community to get some help on a very specific issue I’ve been trying to solve.

I’m creating a “BMP clock”( or a "circular BPM visualizer), so to speak, and I’m trying to get the hand that shows the beat to cycle between different colors/moods. Right now I’ve only been able to, by looking at examples on the old forums, get it to cycle between two colors. However, I would like to cycle between at least four.

Here’s the code that I’ve cobbled together from different examples. I’d reckon it’s not the “neatest” code in the world, but it represents what I’m trying to do.

int cx, cy;

float secondsRadius;
float minutesRadius;
float hoursRadius;
float clockDiameter;

float colorAngle = 0.0;
 
float xRed = 62, xGreen = 20, xBlue = 173;
float yRed = 207, yGreen = 1, yBlue = 106;

 
float redX = xRed, greenX = xGreen, blueX = xBlue;
float speed = 0.02;

float BPM = 60;     // beats per minute
float millisPerBeat = 1000/(BPM/60.0);

void setup() {
  size(960, 540);
  stroke(255);
  //colorMode(HSB);
  
  int radius = min(width, height) / 2;
  secondsRadius = radius * 0.9;
  minutesRadius = radius * 0.60;
  hoursRadius = radius * 0.50;
  clockDiameter = radius * 1.8;
  
  cx = width / 2;
  cy = height / 2;
}

//float c;

void draw() {
  background(0);
  
  // Draw the clock background
  fill(0);
  stroke(255);
  strokeWeight(0);
  ellipse(cx, cy, clockDiameter, clockDiameter);
  // Angles for sin() and cos() start at 3 o'clock;
  // subtract HALF_PI to make them start at the top
  float s = millis() / millisPerBeat - HALF_PI;
  // float m = map(minute() + norm(second(), 0, 60), 0, 60, 0, TWO_PI) - HALF_PI; 
  // float h = map(hour() + norm(minute(), 0, 60), 0, 24, 0, TWO_PI * 2) - HALF_PI;
  
  // Draw the hands of the clock
  stroke(redX, greenX, blueX);
   float sinval1 = sin(colorAngle);
   println(sinval1);
   redX = map(sinval1, -1, 1, xRed, yRed);
   greenX = map(sinval1, -1, 1, xGreen, yGreen);
   blueX = map(sinval1, -1, 1, xBlue, yBlue);
   
   //ellipse(width/2, height/2, 300, 300);
   colorAngle += speed;
   strokeWeight(24);
   
  line(cx, cy, cx + cos(s) * secondsRadius, cy + sin(s) * secondsRadius);
  // strokeWeight(2);
  // line(cx, cy, cx + cos(m) * minutesRadius, cy + sin(m) * minutesRadius);
  // strokeWeight(4);
  //line(cx, cy, cx + cos(h) * hoursRadius, cy + sin(h) * hoursRadius);
  

}

Any tips to what I can do?

Did you see lerpColor?

Chrisir

2 Likes

Yes, but isn’t that only for two colors? I need at least four.

1 Like

Some references:
https://processing.org/tutorials/color/

size(500, 500, P2D); 

colorMode(HSB, 100);
float x;
float y;
float th;
float r = 200;

background(0);
translate(width/2, height/2);
for(int i = 0; i < 100; i++)
  {
  th = i*TAU/100;  
  x = r*sin(th);
  y = r*cos(th);
  stroke(i, 100, 100, 100);
  strokeWeight(10);
  point(x, y);
  }

:slight_smile:

3 Likes

For a quick, off-the-shelf multicolor extension to lerpColor, define a function lerpColors:

color lerpColors(float amt, color... colors) {
  if(colors.length==1){ return colors[0]; }
  float cunit = 1.0/(colors.length-1);
  return lerpColor(colors[floor(amt / cunit)], colors[ceil(amt / cunit)], amt%cunit/cunit);
}

Examples of how to use this function:

Many past discussions:

4 Likes

Thanks! I’ll take a look at those.

I guess you want to achieve something like this?

you can ‘trick’ lerpColor() to cycle trough between some colors

Yes, but I don’t want it to randomly cycle between colors. I want a slow fade/transition between three colors and I want the transition to last a set amount of time (like, say, a minute)

For example: Blue -> Red -> Yellow -> Blue…etc. I want to define the value for the three colors and leap between them.

You’re good to go.
All you have to do now is to create color switching mechanism.
Instead getting random color, switch to any color you want.

Create an array of them, and cycle it using modulo operator ‘%’.

startColor = color(255, 255, 255);
endColor = color(255, 0, 

final int  COLOR_RANGE = 10; //Resolution
int colorPos;


color getNextColor() {
  colorPos = (colorPos + 1) % COLOR_RANGE;
  if (colorPos == 0) {
    startColor = endColor;
    endColor = _____; // Swicth color
  }
  final float amt = (float) colorPos / COLOR_RANGE;
  return lerpColor(startColor, endColor, amt);
}

Thanks! I’ll give It a shot!

I should also point out that it’s the stroke of the clock/BPM-timer that I want to cycle between colors.

 // Draw the hands of the clock
  stroke(redX, greenX, blueX);
   float sinval1 = sin(colorAngle);
   println(sinval1);
   redX = map(sinval1, -1, 1, xRed, yRed);
   greenX = map(sinval1, -1, 1, xGreen, yGreen);
   blueX = map(sinval1, -1, 1, xBlue, yBlue);
   
   //ellipse(width/2, height/2, 300, 300);
   colorAngle += speed;
   strokeWeight(24);

I want to leave the background black/blank.

Well, whatever is driving your rotation, make it a float range from 0-1, and then map that variable to both your angle of rotation and your lerpColors lookup.

Here it is driven by frameCount:

color WHITE = color(255, 255, 255);
color YELLOW =   color(255,   255,   0);
color CYAN = color(  0, 255,   255);
color MAGENTA =  color(  255,   0, 255);
color[] colors = {WHITE,YELLOW,CYAN,MAGENTA,WHITE};
void setup() {
  size(400, 400);
  strokeWeight(20); 
} 
void draw() {
  background(128);
  // map frameCount to 0-1 sin wave
  float amt = (sin(frameCount*0.02)+1)/2;
  // draw angled / colored line
  translate(width/2,height/2);
  rotate(amt*TWO_PI);
  stroke(lerpColors(amt, colors));
  line(0,0,width*0.4,0);
}
color lerpColors(float amt, color... colors) {
  if(colors.length==1){ return colors[0]; }
  float cunit = 1.0/(colors.length-1);
  return lerpColor(colors[floor(amt / cunit)], colors[ceil(amt / cunit)], amt%cunit/cunit);
}

…or you could change that to the mouse…

  // map mouse to 0-1
  float amt = mouseX/(float)width;

…or with the keyboard…

  // map keypresses to 0-1
  float amt = constrain(int(char(key))/127.0, 0, 1);

…or based on microphone volume, or camera motion et cetera. The key thing is that your color list can be any length, lerpColors takes an input 0-1.0, and it gives you back a color – then how you generate that 0-1 is up to you.

3 Likes

I finally managed something that works. Thanks for all the pointers!

Glad to hear that you figured it out! Willing to share your solution?

Of course:

int durationTransition = 1000;
int durationHold = 2000;



boolean doLerp = false;
long changeTime = 0;
long startTime = 0;
int colorIndex = 0;

color[] colors = {
  #FF0000,
  #666666,
  #653277
};

void draw()
{
  
  if (millis() > changeTime)
  {
    doLerp = !doLerp;   
    changeTime = millis() + (doLerp ? durationTransition : durationHold);
    startTime = millis();
    
    if (!doLerp)
    {
      colorIndex = (colorIndex + 1) % colors.length;
    }
  }
  
  
  color col = colors[colorIndex];
  
  if (doLerp)
  {
    color color1 = colors[colorIndex];
    color color2 = colors[(colorIndex + 1) % colors.length];
    float time = (millis() - (float) startTime) / durationTransition;
    col = lerpColor(color1, color2, time);
  }
  
  background(col);
}

A friend helped me out, so it’s all attributed to him

3 Likes

Hi there,
Thank you very much for sharing your solution, learning form it right now… :slight_smile:
Inside your color lerpColors(float amt, color... colors) function, in the second parameter you added three(3) dots “...” after the variable color. Have not seen that anywhere before, trying to research but cannot find. What do those three “...” specify / do?
I originally thought it was a sort of shorthand that you used to reply to the question, but when I run it in Processing it runs.

Many thanks

1 Like

good question!

The ... just means

  • an array of unknown length OR
  • a series of variables (unknown number of them) separated by comma

Example

see a Sketch with both usages of the SAME function (with ...) here:


color WHITE = color(255, 255, 255);
color YELLOW =   color(255, 255, 0);
color CYAN = color(  0, 255, 255);
color MAGENTA =  color(  255, 0, 255);
color[] colors = {WHITE, YELLOW, CYAN, MAGENTA, WHITE};


void setup() {
  size(400, 400);
  strokeWeight(20);
} 

void draw() {
  background(128);

  for (int a=1; a<5; a++) {
    color c1=lerpColors(a/5.0, WHITE, YELLOW, MAGENTA, CYAN);
    fill(c1); 
    noStroke(); 
    rect(47*a+130, height-20, 
      20, 20);
  }

  // map frameCount to 0-1 sin wave
  float amt = (sin(frameCount*0.02)+1)/2;
  // draw angled / colored line
  translate(width/2, height/2);
  rotate(amt*TWO_PI);
  stroke(lerpColors(amt, colors));
  line(0, 0, width*0.4, 0);
}

color lerpColors(float amt, color... colors) {
  if (colors.length==1) { 
    return colors[0];
  }
  float cunit = 1.0/(colors.length-1);
  return lerpColor(colors[floor(amt / cunit)], 
      colors[ceil(amt / cunit)],
      amt%cunit/cunit);
}

2 Likes

Hello @Gus_Fermin,

See Arbitrary Number of Arguments here:

:)

1 Like