Implementing Custom Interpolation in Remotion: A Complete Guide to Animation Primitives
Remotion provides a composable interpolation system through pure TypeScript functions like interpolate, interpolateColors, and interpolateStyles, allowing you to animate numbers, colors, CSS properties, and SVG paths by mapping input ranges to output ranges with optional easing.
Remotion’s animation engine is built around a small set of low-level interpolation utilities that let you map any numeric input to any output type. These utilities are pure TypeScript functions located in the remotion-dev/remotion repository, so they can be composed, customized, and reused inside your own components to create smooth video animations.
Understanding the Core interpolate Function
The foundation of Remotion’s animation system is the interpolate function defined in packages/core/src/interpolate.ts. This primitive accepts a numeric input, maps it from an input range to an output range, and returns the interpolated value.
export function interpolate(
input: number,
inputRange: readonly number[],
outputRange: readonly number[],
options?: InterpolateOptions,
): number
Range Validation and Extrapolation
According to the source code in packages/core/src/interpolate.ts, the function first validates that inputRange and outputRange have identical lengths and that all elements are finite numbers in monotonic order. For values falling outside the defined range, Remotion supports four extrapolation types via the ExtrapolateType union: extend, identity, clamp, and wrap. These determine whether the animation continues linearly, freezes at the boundary, or wraps around.
Easing and Linear Interpolation
The options parameter accepts an optional easing function that transforms the linear progress into curved motion. If no easing is provided, the function performs standard linear interpolation by finding the containing segment via findRange and calculating the proportional distance between the two surrounding points.
Built-in Interpolation Helpers
While the core interpolate function handles numbers, Remotion provides higher-level helpers that delegate to this primitive while managing complex data types like colors, CSS styles, and SVG paths.
Color Interpolation with interpolateColors
The interpolateColors function in packages/core/src/interpolate-colors.ts enables smooth transitions between CSS color strings.
export const interpolateColors = (
input: number,
inputRange: readonly number[],
outputRange: readonly string[],
): string
Internally, the function parses CSS color strings into 32-bit ARGB numbers using processColor, splits the result into individual RGBA channels, interpolates each channel using the core interpolate function, and reassembles the output into a standard rgba(r,g,b,a) string.
Style Object Interpolation with interpolateStyles
For animating complex CSS properties, interpolateStyles in packages/animation-utils/src/transformation-helpers/interpolate-styles/index.tsx decomposes style objects into interpolatable parts.
export const interpolateStyles = (
input: number,
inputRange: number[],
outputStylesRange: Style[],
options?: InterpolateOptions,
) => Style
The helper uses breakDownValueIntoUnitNumberAndFunctions to recursively decompose each CSS value into numbers, units, functions, or colors. The interpolatedPropertyPart function then handles four specific scenarios: forwarding colors to interpolateColors, recursively interpolating function arguments (like translate(10px, 5px)), blending unit-numbers while preserving units, and handling pure units when the numeric value is zero. The function validates structural compatibility between start and end values, throwing descriptive errors for mismatched shapes or non-animatable values before assembling the final style object.
SVG Path Morphing with interpolatePath
For vector animations, interpolatePath in packages/paths/src/interpolate-path/interpolate-path.ts enables frame-by-frame morphing between SVG path strings.
export const interpolatePath = (
value: number,
firstPath: string,
secondPath: string,
) => string
The implementation parses both SVG strings into command lists using parsePath, normalizes the lists via reduceInstructions to ensure identical lengths and compatible segment types, interpolates each command’s numeric parameters using the core interpolate function, and serializes the result back to a path string with serializeInstructions.
Creating Custom Interpolation Functions
To extend Remotion’s animation system for custom data types or specialized CSS properties, you can compose the existing primitives. The general pattern involves parsing inputs into deterministic numeric structures, applying the core interpolate function with optional custom easing, and reassembling the result into the target format.
For example, to interpolate between CSS lengths that mix percentages and pixels, you can normalize both values to a common unit before interpolation:
import {interpolate} from '@remotion/remotion';
/**
* Interpolates a CSS length expressed either as a number (px) or a string ending with '%'.
* The function treats percentages as fractions of a given base width.
*/
function interpolateLength(
input: number,
inputRange: [number, number],
outputRange: [string | number, string | number],
baseWidth: number,
options?: InterpolateOptions,
) {
// Convert both end values to pixels
const toPx = (v: string | number) =>
typeof v === 'number'
? v
: parseFloat(v) / 100 * baseWidth; // % → px
const pixelRange: [number, number] = [
toPx(outputRange[0]),
toPx(outputRange[1]),
];
const px = interpolate(input, inputRange, pixelRange, options);
return `${px}px`;
}
// Example – animate from 10% to 200px over a 400‑pixel container:
const width = interpolateLength(0.5, [0, 1], ['10%', 200], 400); // '210px'
This approach leverages the validation, easing, and extrapolation logic built into packages/core/src/interpolate.ts while handling the domain-specific conversion between percentage strings and pixel values.
Summary
- Core primitive: The
interpolatefunction inpackages/core/src/interpolate.tshandles numeric mapping with support for easing, extrapolation, and range validation. - Color support:
interpolateColorsinpackages/core/src/interpolate-colors.tsparses CSS colors into RGBA channels and delegates to the core interpolator. - Style objects:
interpolateStylesinpackages/animation-utils/src/transformation-helpers/interpolate-styles/index.tsxrecursively decomposes CSS values to animate complex properties liketransformandborder. - SVG paths:
interpolatePathinpackages/paths/src/interpolate-path/interpolate-path.tsnormalizes and morphs between vector path strings. - Extensibility: You can build custom interpolators by normalizing inputs to numbers, applying the core
interpolatefunction, and reconstructing the output format.
Frequently Asked Questions
How do I implement easing in custom interpolation functions?
When building a custom interpolator, pass your easing function directly to the options parameter of the core interpolate function. The easing curve is applied to the normalized progress value (0 to 1) before the final numeric calculation, allowing you to reuse Remotion’s built-in easing curves like Easing.elasticOut or provide your own cubic-bezier function.
Can I interpolate between custom data types not supported by Remotion?
Yes. To interpolate between custom data types—such as complex coordinate objects or domain-specific units—decompose the values into numeric components that the core interpolate function can process. After interpolation, reconstruct the data type from the resulting numbers. This pattern mirrors how interpolateStyles handles CSS functions and how interpolatePath handles SVG commands.
What is the performance impact of using interpolateStyles versus manual interpolation?
interpolateStyles incurs a small overhead due to its recursive value decomposition and structural validation, which ensures type safety and prevents animation errors. For high-frequency animations (60 FPS video frames), this cost is negligible in most cases, but if you are animating thousands of elements per frame, you may see better performance by using the raw interpolate function on pre-extracted numeric values rather than full style objects.
How does Remotion handle extrapolation when values fall outside the input range?
The core interpolate function accepts an extrapolate option in its configuration object that supports four behaviors: extend continues the interpolation linearly beyond the range, clamp freezes the value at the nearest boundary, identity returns the input value unchanged, and wrap cycles the value through the range. These options are defined in packages/core/src/interpolate.ts and apply to all higher-level helpers that delegate to the core function.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →