# How do I lerp() colors on a curve? (non linear interpolation)

**URL:** <https://discourse.processing.org/t/how-do-i-lerp-colors-on-a-curve-non-linear-interpolation/24815>\
**Category:** Coding Questions\
**Created:** [October 22, 2020, 10:50am UTC](https://discourse.processing.org/t/how-do-i-lerp-colors-on-a-curve-non-linear-interpolation/24815 "2020-10-22T10:50:02Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![Birb](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/birb/32/3280_2.png) [@Birb](https://discourse.processing.org/u/Birb)\
**Post date:** [October 22, 2020, 10:50am UTC](https://discourse.processing.org/t/how-do-i-lerp-colors-on-a-curve-non-linear-interpolation/24815/1 "2020-10-22T10:50:02Z")

</div>

How do I interpolate two (or maybe even more) colors in a non-linear way along a curve? For example, instead of the mid-point being 50/50, I would want it to be 25/75, or shifted according to the control point.

I tried this, but I guess the problem is where to put the control points for each color? Changing the control points w1, w2 doesn’t seem to do anything unless I set the numbers high, and then the colors are not a smooth transition but introduce weird colors in between, because it’s not interpolating each rgb channel at the same speed.

```auto
function curveColors(col1, col2, w1, w2, t) {

    let r1 = map(col1[0], 0, 255, 0, 1);

    let g1 = map(col1[1], 0, 255, 0, 1);

    let b1 = map(col1[2], 0, 255, 0, 1);

    let r2 = map(col2[0], 0, 255, 0, 1);

    let g2 = map(col2[1], 0, 255, 0, 1);

    let b2 = map(col2[2], 0, 255, 0, 1);

    let rc1 = r1 + abs(r1 - r2) * w1;

    let rc2 = r2 - abs(r1 - r2) * w2;

    let gc1 = g1 + abs(g1 - g2) * w1;

    let gc2 = g2 - abs(g1 - g2) * w2;

    let bc1 = b1 + abs(b1 - b2) * w1;

    let bc2 = b2 - abs(b1 - b2) * w2;

    let lowR = returnLowest(col1[0],col2[0]);

    let lowG = returnLowest(col1[1],col2[1]);

    let lowB = returnLowest(col1[2],col2[2]);

    let r = constrain(map(curvePoint(rc1, r1, r2, rc2, t), 0, 1, 0, 255),lowR[0],lowR[1]);

    let g = constrain(map(curvePoint(gc1, g1, g2, gc2, t), 0, 1, 0, 255),lowG[0],lowG[1]);

    let b = constrain(map(curvePoint(bc1, b1, b2, bc2, t), 0, 1, 0, 255),lowB[0],lowB[1]);

    let finalColor = [r, g, b];

     

    return finalColor;

}

function returnLowest(a, b){

 let low;

 let high;

    if (a<=b){

     low = a;

     high = b;

 } else {low = b; high = a;}

 let both = [low, high];

 return both;

}

```

---

<div class="post-metadata">

**Author:** ![TheWizardBear](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/thewizardbear/32/3552_2.png) [@TheWizardBear](https://discourse.processing.org/u/TheWizardBear)\
**Post date:** [October 22, 2020, 11:00am UTC](https://discourse.processing.org/t/how-do-i-lerp-colors-on-a-curve-non-linear-interpolation/24815/2 "2020-10-22T11:00:39Z")

</div>

You can use the built in [`lerpColor`](https://p5js.org/reference/#/p5/lerpColor) function, like so:

```auto
lerpColor(color0, color1, amount)

```

where `color1` and `color2` are two [p5 Color](https://p5js.org/reference/#/p5.Color) variables, and `amount` is the amount you want to lerp between them (between 0 and 1, with 0 being all color0 and 1 being all color1).

To avoid a linear interpolation, you can map your value between 0 and 1 using some other function (e.g. squaring the value to have a bias towards color1)

---

<div class="post-metadata">

**Author:** ![Chrisir](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/chrisir/32/45_2.png) [@Chrisir](https://discourse.processing.org/u/Chrisir)\
**Post date:** [October 22, 2020, 7:42pm UTC](https://discourse.processing.org/t/how-do-i-lerp-colors-on-a-curve-non-linear-interpolation/24815/3 "2020-10-22T19:42:19Z")

</div>

I find the idea great to get the amt data for lerpColor from a function.

So I made a class where you can

- play with a bezier curve (instead of a function) and
- the bezier curve (its y-value) is fed into the **color**.

Core idea is in the function `testlerpColor()`.

_It’s a bit tricky._

But when somebody wants to know more, I can explain. I received help from the forum for this. I was mixing up curve and bezier. Bad. See reference.

This could be extended with bezierVertex() or curveVertex() I guess.

Chrisir

```auto
// Test for a new bezier Class that can do bezier and bezierpoint (2D) to set a color from bezierPoint y-value 

BezierClass mybezier; 

// ---------------------------------------------------------------------------------------------

void setup() {

  size(1500, 900);

  final int STANDARD_DISTANCE=100;

  mybezier = new BezierClass (
    width/2-31, 33, // coordinates for the first point / anchor
    width/2-STANDARD_DISTANCE, height/2+STANDARD_DISTANCE, // control point (cp1)
    width/2+STANDARD_DISTANCE, height/2+STANDARD_DISTANCE, // control point 
    width/2+31, 33 // coordinates for the second point / anchor 
    );
}

void draw() {
  background(#24B41D);

  fill(0); 
  text("Bezier\n"
    +"Demo for Bezier y value to color\n"
    +"You can drag the two red control points (and the anchor points) with the mouse. \n"
    +"Move the mouse to move the white ball and the color on the bezier (mouseX->amt)", 
    14, 14);

  testbezier();
}

//-------------------------------------------------------------------------------------------------------

void testbezier () {
  mybezier.displaybezier(); // show entire bezier  
  mybezier.testbezierData(); // show circles with texts
  mybezier.moveOnbezierWithMouseX(); // OR moveOnbezier()
  mybezier.testlerpColor(); // lerpColor
  mybezier.drag(); // for dragging with the mouse
}

//-------------------------------------------------------------------------------------------------------

void mousePressed() {
  mybezier.mousePressedClass();
}

void mouseReleased() {
  mybezier.mouseReleasedClass();
}

// ===============================================================================================

class BezierClass {

  final int UNKNOWN = -1; 

  // bezier data 
  PVector anchor1, cp1, cp2, anchor2;

  // the amt for lerp 
  float amt=0;

  // indicates which point we drag (cp1, cp2 or anchor1 or anchor2)
  int drag = -1; 

  // the min and max y for the current bezier
  float minY, maxY; 

  // how strong the cp influences !!! Doesn't work good........
  float factorCP = 1; 

  // constr - Parameters: you call the constr as you would bezier command (2D version) - see reference
  BezierClass ( float f0, float f1, 
    float f2, float f3, 
    float f4, float f5, 
    float f6, float f7 ) {
    // constr 
    anchor1 = new PVector (f0, f1);  
    cp1 = new PVector (f2, f3);
    cp2 = new PVector (f4, f5);
    anchor2 = new PVector (f6, f7);

    setMinYAndMaxY();
  } // constr 

  // ---

  void setMinYAndMaxY () {
    // init 
    minY = 1000000; 
    maxY = -100000;
    // simulate bezier 
    for (int i = 0; i <= 100; i++) {
      float y = bezierPoint(anchor1.y, factorCP * cp1.y, factorCP * cp2.y, anchor2.y, 
        i/100.0);
      if (y<minY) 
        minY = y; 
      if (y>maxY) 
        maxY = y;
    }//for
  }

  // ---

  void displaybezier() {
    stroke(255, 0, 0); //RED 
    strokeWeight(1);
    noFill();
    bezier (
      anchor1.x, anchor1.y, 
      factorCP * cp1.x, factorCP * cp1.y, 
      factorCP * cp2.x, factorCP * cp2.y, 
      anchor2.x, anchor2.y
      );
  }

  // ---

  void moveOnbezier() {
    // AUTO move 
    // resulting point 
    float x, y; 
    x = bezierPoint(anchor1.x, factorCP * cp1.x, factorCP * cp2.x, anchor2.x, amt); 
    y = bezierPoint( anchor1.y, factorCP * cp1.y, factorCP * cp2.y, anchor2.y, amt);
    fill(255); // WHITE
    noStroke(); 
    ellipse(x, y, 5, 5);
    amt+=0.01;
    if (amt>=1) 
      amt=0.0;
  }

  void moveOnbezierWithMouseX() {
    // mouseX move
    float x, y; 
    // mouseX -> amt :
    amt=map(mouseX, 0, width, 
      0, 1); 
    // amt ->bezier point
    x = bezierPoint(anchor1.x, factorCP * cp1.x, factorCP * cp2.x, anchor2.x, amt); 
    y = bezierPoint( anchor1.y, factorCP * cp1.y, factorCP * cp2.y, anchor2.y, amt);

    //x = bezierPoint(factorCP * cp1.x, anchor1.x, anchor2.x, factorCP * cp2.x, amt); 
    //y = bezierPoint(factorCP * cp1.y, anchor1.y, anchor2.y, factorCP * cp2.y, amt);
    fill(255); // WHITE
    noStroke(); 
    ellipse(x, y, 5, 5);
  }

  // ---

  void testlerpColor() {
    // now it gets interesting 

    // mouseX to lerpColor 
    // mouseX -> amt : 
    float amt=map(mouseX, 0, width, 
      0, 1); 
    // amt -> bezierPoint Y 
    float y = bezierPoint( anchor1.y, factorCP * cp1.y, factorCP * cp2.y, anchor2.y, amt);  
    // y-value -> lerp color colorAmt
    float colorAmt = map ( y, minY, maxY, 
      0, 1 );
    // lerp color colorAmt -> color 
    fill( lerpColor( color(255, 0, 0), color(0, 0, 255), colorAmt ) ); 
    rect (100, 100, 66, 66);

    fill(255);
    text ("min and max: " + minY+" "+maxY 
      + " -> " +y 
      + " \ncolor amt = "+colorAmt, 100, 180);
  }

  // ---

  void testbezierData() {
    makeCircleWithText("cp1", cp1, color (255, 0, 0)) ;  
    makeCircleWithText("cp2", cp2, color (255, 0, 0)) ;  
    makeCircleWithText("anchor1", anchor1, color (0, 0, 255)) ;  
    makeCircleWithText("anchor2", anchor2, color (0, 0, 255)) ;
  }//method

  void makeCircleWithText(String txt, PVector pv, color col) {
    noStroke(); 
    fill(col);
    ellipse (pv.x, pv.y, 18, 18);

    fill(255);
    text(txt, 
      pv.x+15, pv.y-8);
  }

  // ---

  void mousePressedClass() {
    if (dist(mouseX, mouseY, cp1.x, cp1.y) < 33) 
      drag=0; 
    else if (dist(mouseX, mouseY, cp2.x, cp2.y) < 33) 
      drag=1;
    else if (dist(mouseX, mouseY, anchor1.x, anchor1.y) < 33) 
      drag=2;
    else if (dist(mouseX, mouseY, anchor2.x, anchor2.y) < 33) 
      drag=3;
  }//method 

  void mouseReleasedClass() {
    drag=UNKNOWN; // reset
    setMinYAndMaxY();
  }

  // ---

  void drag() {
    if (drag==UNKNOWN) 
      return; 

    switch (drag) {
    case 0:
      cp1.x=mouseX; 
      cp1.y=mouseY;
      break; 

    case 1:
      cp2.x=mouseX; 
      cp2.y=mouseY;
      break;

    case 2:
      anchor1.x=mouseX; 
      anchor1.y=mouseY;
      break;

    case 3:
      anchor2.x=mouseX; 
      anchor2.y=mouseY;
      break;

    case UNKNOWN:
      //ignore
      break;

    default:
      // Error
      break;
    }//switch

    setMinYAndMaxY();
  }//method
  //
}//class
//

```

---

<div class="post-metadata">

**Author:** ![Chrisir](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/chrisir/32/45_2.png) [@Chrisir](https://discourse.processing.org/u/Chrisir)\
**Post date:** [October 22, 2020, 8:26pm UTC](https://discourse.processing.org/t/how-do-i-lerp-colors-on-a-curve-non-linear-interpolation/24815/4 "2020-10-22T20:26:52Z")

</div>

and here is the idea of a sin function going into lerpColor

(by the way, since y gets bigger when you go downwards (!) on the screen, the values might be upside down. So when the sin curve is higher, its y-value is lower)

```auto
// Test to set a color from a sin function 

// ---------------------------------------------------------------------------------------------

void setup() {
  size(1500, 900);
  final int STANDARD_DISTANCE=100;
}

void draw() {
  background(#24B41D);

  // show text 
  fill(0); 
  text("\n"
    +"Demo for using sin function as amt for lerp color - move mouse left and right \n", 
    14, 14);

  // show sin
  for (int i=0; i <=360; i++) {
    float result2 = sin(radians(i));
    noStroke(); 
    fill(255);
    ellipse(map(i, 0, 360, 0, width), result2*120+width/2, 
      12, 12);
  }

  // show color rect 
  testlerpColor();
}

// -------------------------------------------------------------------------------

void testlerpColor() {
  // now it gets interesting 

  // mouseX to lerpColor 
  // mouseX -> angle : 
  float angle=map(mouseX, 0, width, 
    0, TWO_PI);
  // angle -> sin 
  float result = sin(angle); // that's the formula 
  // sin -> amt 
  float amt = map( result, -1, 1, 0, 1); 

  // amt -> lerp color: RED BLUE amt 
  fill( lerpColor( color(255, 0, 0), color(0, 0, 255), amt ) ); 
  rect (100, 100, 66, 66);

  fill(255);
  text ("amt: "
    + amt
    + "\n" 
    + nf( amt, 1, 2), 
    100, 180);
}//func 
//

```

* * *

**And a square function**

```auto
// Test to set a color from function 

// ---------------------------------------------------------------------------------------------

void setup() {
  size(1500, 900);
  final int STANDARD_DISTANCE=100;
}

void draw() {
  background(#24B41D);

  // show text 
  fill(0); 
  text("\n"
    +"Demo for using sin function as amt for lerp color - move mouse left and right \n", 
    14, 14);

  // show sin
  for (int i=0; i <= 5; i++) {
    float result2 = i*i;
    noStroke(); 
    fill(255);
    ellipse(map(i, 0, 5, 0, width), result2*10 + 10, 
      12, 12);
  }

  // show color rect 
  testlerpColor();
}

// -------------------------------------------------------------------------------

void testlerpColor() {
  // now it gets interesting 

  // mouseX to lerpColor 
  // mouseX -> xValue : 
  float xValue=map(mouseX, 0, width, 
    0, 5);
  // xValue ->  
  float result = xValue*xValue; // that's the formula 
  // -> amt 
  float amt = map( result, 0, 25, 0, 1); 

  // amt -> lerp color: RED BLUE amt 
  fill( lerpColor( color(255, 0, 0), color(0, 0, 255), amt ) ); 
  rect (100, 100, 66, 66);

  fill(255);
  text ("amt: "
    + amt
    + "\n" 
    + nf( amt, 1, 2), 
    100, 180);
}//func 
//

```
