Skip to content

Interpolate colors in the current colorMode or in another specified space - #9024

Merged
limzykenneth merged 6 commits into
processing:mainfrom
dontwanttothink:main
Aug 12, 2026
Merged

Interpolate colors in the current colorMode or in another specified space#9024
limzykenneth merged 6 commits into
processing:mainfrom
dontwanttothink:main

Conversation

@dontwanttothink

@dontwanttothink dontwanttothink commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Resolves #8806 and resolves #8883

Changes:

This pull request is an implementation of #8806 (comment):

However, if we bring forward the idea to define lerp space, we can have 1.x behavior while still having enough flexibility for those who need it. We will need to overload lerp() in this case:

lerp(color, amt, mode); // current signature
lerp(color, options); // additional overload signature

The overloads can be differentiated by the type of the second argument. options will be:

{
  amount: number,
  lerpMode: COLOR_MODE, // or would `lerpSpace` be better?
  outputMode: COLOR_MODE
}

And lerpColor() will stay the same in terms of signature while the way it calls lerp() will use the new overload

return c1.lerp(c2, {
  amount: amt,
  lerpMode: this._renderer.states.colorMode,
  outputMode: this._renderer.states.colorMode
});

lerpColor() can also be overloaded with the options object on its third argument if desired:

lerpColor(c1, c2, amt);
lerpColor(c1, c2, options); // Options here will be the same as the one above for `lerp()`

Screenshots of the change:

The following outputs are produced by variations of this code:

p.background(220);
p.noStroke();

const from = p.color("red");
const to = p.color("green");

p.colorMode("rgb")

for (let i = 0; i <= 10; ++i) {
  p.fill(p.lerpColor(from, to, i / 10))
  p.ellipse(i * 30, 200, 100, 100);
}

RGB:
Circles showing RGB colour interpolation

LAB:
Circles showing LAB colour interpolation

HSL:
Circles showing HSL colour interpolation

I also tested the options object overload:

p.background(220);
p.noStroke();

p.colorMode("hsl")
const from = p.color(240, 100, 25);
const to = p.color("white");

p.colorMode("hsb")

for (let i = 0; i <= 10; ++i) {
  p.fill(p.lerpColor(from, to, {
    amount: i / 10,
    lerpMode: "oklab"
  }));
  p.ellipse(i * 30, 200, 100, 100);
}
HSB with OKLAB override

Compare to HSB interpolation:

hsb (dark blue)

PR Checklist

@davepagurek

Copy link
Copy Markdown
Contributor

Nice screenshots @dontwanttothink!

@p5-bot

p5-bot Bot commented Jul 30, 2026

Copy link
Copy Markdown

@dontwanttothink
dontwanttothink force-pushed the main branch 2 times, most recently from bf3701d to 31c831a Compare August 9, 2026 14:33
@dontwanttothink

Copy link
Copy Markdown
Contributor Author

I can't get the tests to run in my computer (I even tried setting up a Linux VM), so I'm not sure why it's failing to write those seemingly unrelated images :(

@dontwanttothink

Copy link
Copy Markdown
Contributor Author

Oh, it looks like it's fixed?

@dontwanttothink

dontwanttothink commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

Apparently the tests passed in my fork 🧚

Or in another specified space
I ran this script:

```ts
import Color from "colorjs.io";

const $ = console.log;

// The tests go:
// 	fromColor = mockP5Prototype.color(218, 165, 32);
// 	toColor = mockP5Prototype.color(72, 61, 139);
// so create the same colours

const fromColor = new Color("rgb(218 165 32)");
const toColor = new Color("rgb(72 61 139)");

$("HSL lerp test:");
$(String(fromColor.range(toColor, {
	space: "hsl",
	outputSpace: "hsl"
})(0.33)));

$(String(fromColor.range(toColor, {
	space: "hsl",
	outputSpace: "hsl"
})(0.66)));

$();
$("HSB lerp test:");
$(String(fromColor.range(toColor, {
	space: "hsv", // see https://en.wikipedia.org/wiki/HSL_and_HSV#:~:text=HSV%20stands%20for%20hue%2C%20saturation%2C%20and%20value%2C%20and%20is%20also%20often%20called%20HSB
})(0.33)));

$(String(fromColor.range(toColor, {
	space: "hsv",
})(0.66)));

const fromColorA = new Color(`rgb(218 165 32 / ${49 / 255})`);
const toColorA = new Color(`rgb(72 61 139 / ${200 / 255})`);

$();
$("HSLA lerp test:");
$(String(fromColorA.range(toColorA, {
	space: "hsl",
})(0.33)));

$(String(fromColorA.range(toColorA, {
	space: "hsl",
})(0.66)));

$();
$("HSBA lerp test");
$(String(fromColorA.range(toColorA, {
	space: "hsv",
})(0.33)));

$(String(fromColorA.range(toColorA, {
	space: "hsv",
})(0.66)));
```

to get this output:

```css
HSL lerp test:
hsl(351.94 62.718% 45.784%)
hsl(300.97 51.036% 42.549%)

HSB lerp test:
color(--hsv 351.94 75.683% 75.267%)
color(--hsv 300.97 66.045% 65.043%)

HSLA lerp test:
hsl(351.94 62.718% 45.784% / 0.38757)
hsl(300.97 51.036% 42.549% / 0.58298)

HSBA lerp test
color(--hsv 351.94 75.683% 75.267% / 0.38757)
color(--hsv 300.97 66.045% 65.043% / 0.58298)
```

@davepagurek davepagurek left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@limzykenneth I think this looks good to me, anything you see that needs updating before merging?

@limzykenneth
limzykenneth merged commit 8b51977 into processing:main Aug 12, 2026
6 checks passed
@limzykenneth

Copy link
Copy Markdown
Member

All looks great, well done!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants