Linear Interpolation isn’t just for animation, it’s amazing for data manipulation and a worthwhile tool to have in your coding arsenal.

I 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) // 40``````

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.

See the Pen Lerp by Trys Mudford (@trys) on CodePen.

## 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.

See the Pen Clamp by Trys Mudford (@trys) on CodePen.

## 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.

See the Pen Inverse Lerp by Trys Mudford (@trys) on CodePen.

## 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.

See the Pen Range by Trys Mudford (@trys) on CodePen.

## 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));``````

This was originally posted on my website.