# Linear Interpolation Functions

Posted on in WebI wrote a blog post some months back on Linear Interpolation. It was a subject I knew very little about at the time, having not done a great deal of animation work. But now I know a little more, I’ve found it’s been one of those techniques I keep coming back to for most projects.

What I’ve learned is that interpolation isn’t just about animation, or even about visual things—it’s about **data conversion**.

Aside: that might sound a bit heavy or dry, but it’s how my brain works! I love how different coding concepts ‘click’ for different people in different ways.

Among more traditional animation-y things, I’ve used these techniques to calculate rotary dial positions on the guitar pedalboard, mapped usernames to fallback avatars on Daisie and plotted typographic graphs on a side project I’m currently building.

## The four functions

```
const lerp = (x, y, a) => x * (1 - a) + y * a;
const clamp = (a, min = 0, max = 1) => Math.min(max, Math.max(min, a));
const invlerp = (x, y, a) => clamp((a - x) / (y - x));
const range = (x1, y1, x2, y2, a) => lerp(x2, y2, invlerp(x1, y1, a));
```

There’s a Typescript version at the bottom of the page, if you’re that way inclined.

## Lerp

A lerp returns the value between two numbers at a specified, decimal midpoint:

```
lerp(20, 80, 0) // 20
lerp(20, 80, 1) // 80
lerp(20, 80, 0.5) // 50
```

It’s great for answering gnarly maths questions like: *“What number is 35% between 56 and 132?"* with elegance: `lerp(56, 132, 0.35)`

. My maths skills aren’t all that, so it’s great to have these up my sleeve.

Here’s an example that converts a range slider set between 0 and 1, to a `hsl()`

colour with hue degrees of 11 through 60.

## Clamp

The clamp method is wonderfully dull. You give it a number and then a minimum & maximum. If your number falls within the bounds of the min & max, it’ll return it. If not, it’ll return either the minimum it’s smaller, or the maximum if it’s bigger.

```
clamp(24, 20, 30) // 24
clamp(12, 20, 30) // 20
clamp(32, 20, 30) // 30
```

It’s really handy for preventing absurd numbers from entering a calculation, stopping an element from rendering off screen, or controlling the edges of a `<canvas>`

.

Here’s an example that lets you add or subtract 10 from the current number, but clamped between 0 and 100.

## Inverse Lerp

This works in the opposite way to the lerp. Instead of passing a decimal midpoint, you pass any value, and it’ll return that decimal, wherever it falls on that spectrum. Internally it also uses a clamp, so you never get unwieldy values back.

```
invlerp(50, 100, 75) // 0.5
invlerp(50, 100, 25) // 0
invlerp(50, 100, 125) // 1
```

This is great for scroll animations. Questions like *“How far through this section has the user scrolled?"* can be neatly answered with code like:

```
const position = el.getBoundingClientRect();
const howFarThrough = invlerp(
position.top,
position.bottom,
window.scrollY
);
```

Here’s an example that tracks the percentage scroll position of a target slab against the viewport.

## Range

This final method is ace. It’s a one-liner that converts a value from one data range to another. That might sound a bit arbitrary, but it’s surprisingly useful. We pass in two data ranges and a value that sits within data range one (it will still be clamped).

```
// Range 1 Range 2 Value
range(10, 100, 2000, 20000, 50) // 10000
```

Taking the previous example up a notch, let’s say that as the user scrolls through a section, we want to subtly move an element down the page by `150px`

. The section is in the middle of the document, starting at `3214px`

and ending at `3892px`

, and we want to convert `window.scrollY`

from the big range down to a value between `0px`

and `150px`

. That’s a pretty nasty calculation to make, but `range()`

makes it nice and clean.

```
const position = el.getBoundingClientRect();
const transformY = range(
position.top,
position.bottom,
0,
150,
window.scrollY
);
```

If the user is above the section, it’ll be clamped to `0px`

. If they’re below, it’ll be clamped to `150px`

. And in all positions in between, it’ll evenly interpolate between the values.

The final example takes the previous Codepen and maps the result against a `transform: translateY`

range of `-20%`

to `20%`

. Parallax, eat your heart out.

## Typescript version

```
const lerp = (x: number, y: number, a: number) => x * (1 - a) + y * a;
const invlerp = (x: number, y: number, a: number) => clamp((a - x) / (y - x));
const clamp = (a: number, min = 0, max = 1) => Math.min(max, Math.max(min, a));
const range = (
x1: number,
y1: number,
x2: number,
y2: number,
a: number
) => lerp(x2, y2, invlerp(x1, y1, a));
```

Posted on in Web