# Interpolation

## Ranges

`color.range()`

and `Color.range()`

are at the core of Color.js’ interpolation engine.
They give you a function that accepts a number and returns a color.
For numbers in the range 0 to 1, the function *interpolates*; for numbers outside that range, the function *extrapolates* (and thus, may not return the results you expect):

```
let color = new Color("p3", [0, 1, 0]);
let redgreen = color.range("red", {
space: "lch", // interpolation space
outputSpace: "srgb"
});
redgreen(.5); // midpoint
```

Percentages (0 to 100%) should be converted to numbers (0 to 1).

The `space`

parameter controls the color space
interpolation occurs in, and defaults to Lab.
Colors do not need to be in that space, they will be converted for interpolation.
The interpolation space can make a big difference in the result:

```
let c1 = new Color("rebeccapurple");
let c2 = new Color("lch", [85, 100, 85]);
c1.range(c2); // lab
c1.range(c2, {space: "lch"});
c1.range(c2, {space: "srgb"}); // gamma encoded sRGB
c1.range(c2, {space: "srgb-linear"}); //linear-light sRGB
c1.range(c2, {space: "xyz"}); // XYZ, same result as linear RGB
c1.range(c2, {space: "hsl"});
c1.range(c2, {space: "hwb"});
```

Note that for color spaces with a hue angle there are multiple ways to interpolate, which can produce drastically different results.
The `hue`

argument is inspired by the hue-adjuster in CSS Color 5.

```
let c1 = new Color("rebeccapurple");
c1.lch;
let c2 = new Color("lch", [85, 85, 85 + 720]);
c1.range(c2, {space: "lch", hue: "longer"});
c1.range(c2, {space: "lch", hue: "shorter"});
c1.range(c2, {space: "lch", hue: "increasing"});
c1.range(c2, {space: "lch", hue: "decreasing"});
c1.range(c2, {space: "lch", hue: "raw"});
c1.range(c2, {space: "lch"}); // default is "shorter"
```

Range interpolates between colors as they were at the time of its creation. If you change the colors afterwards, the range will not be affected:

```
let color = new Color("red");
let color2 = new Color("black");
let gradient = color.range(color2);
color.coords[1] = 1;
color2.coords[2] = 1;
gradient(.5);
gradient = color.range(color2);
gradient(.5);
```

Interpolating between a coordinate and `NaN`

keeps that coordinate constant.
This is useful for achromatic transitions:

```
let lime = new Color("p3", [0, 1, 0]);
let white = new Color("lch", [100, 0, 0]);
let white2 = new Color("lch", [100, 0, NaN]);
let limewhite = lime.range(white, {space: "lch"});
let limewhite2 = lime.range(white2, {space: "lch"});
// Two kinds of fade out to transparent
lime.range(new Color("transparent"));
lime.range(new Color(lime.space, [NaN, NaN, NaN], 0), {space: lime.space});
```

You can use the `progression`

parameter to customize the progression and make it non-linear:

```
let r = new Color("lch(50 50 0)").range("lch(90 50 20)");
Color.range(r, {progression: p => p ** 3});
```

Note that you can use `Color.range(rangeFunction)`

to modify a range after it has been created, as you can see in the example above.
This produces a new range, and leaves the old one unaffected.

## Interpolation by discrete steps

`color.steps()`

and `Color.steps()`

give you an array of discrete steps.

```
let color = new Color("p3", [0, 1, 0]);
color.steps("red", {
space: "lch",
outputSpace: "srgb",
maxDeltaE: 3, // max deltaE between consecutive steps (optional)
steps: 10 // min number of steps
});
```

By default, the deltaE76 function is used.

## Mixing colors

Interpolation can be used to create color mixtures, in any desired proportion, between two colors.

Shortcut for specific points in the range:

```
let color = new Color("p3", [0, 1, 0]);
let redgreen = color.mix("red", .5, {space: "lch", outputSpace: "srgb"});
let reddishGreen = color.mix("red", .25, {space: "lch", outputSpace: "srgb"});
```

Static syntax, for one-off mixing:

```
Color.mix("color(display-p3 0 1 0)", "red", .5);
```