In the previous chapter, Core React Primitives, we learned how to set up a video and position elements on a timeline. We briefly touched on animation using raw math (frame / 20), but as you might guess, calculating complex movements manually is difficult and error-prone.
In this chapter, we will explore Animation Utilities. These are helper functions designed to make movement smooth, natural, and easy to read.
Imagine you want to move a red box from the left side of the screen (0px) to the right side (500px). You want this movement to happen specifically between frame 0 and frame 30.
If you use raw math:
const x = (frame / 30) * 500;
The Problem:
We need a way to say: "Map frame 0-30 to pixel 0-500, and don't go beyond that."
interpolate
The interpolate function is the bread and butter of Remotion. It maps an Input Range (usually time) to an Output Range (values like opacity, pixels, rotation).
Let's solve the "moving box" problem.
import { interpolate, useCurrentFrame } from 'remotion';
export const MovingBox = () => {
const frame = useCurrentFrame();
// Map time (0 to 30) to position (0 to 500)
const x = interpolate(frame, [0, 30], [0, 500]);
return <div style={{ transform: `translateX(${x}px)` }} />;
};
What happens here?
x is 0.x is 250 (halfway).x is 500.
By default, interpolate extends values endlessly. If we are at frame 100, x will be huge. To stop the animation when it reaches the target, we use Extrapolation.
const x = interpolate(
frame,
[0, 30],
[0, 500],
{
extrapolateRight: 'clamp', // Stop increasing after frame 30
extrapolateLeft: 'clamp', // Stop decreasing before frame 0
}
);
Now, at frame 100, x stays at 500. The box stops moving.
spring
While interpolate creates linear, robotic movement, spring creates natural, physics-based motion. It simulates pulling a heavy object with a rubber band.
It doesn't just stop abruptly; it might overshoot slightly and settle, just like real objects.
The spring function takes the current time, the video FPS, and a configuration object.
import { spring, useCurrentFrame, useVideoConfig } from 'remotion';
export const BouncyBox = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
// Calculates a value from 0 to 1 with physics
const scale = spring({
frame,
fps,
config: { stiffness: 100 }
});
return <div style={{ transform: `scale(${scale})` }} />;
};
Key Differences from Interpolate:
scale or opacity).
You can tune the "feel" of the animation using the config prop:
const move = spring({
frame,
fps,
from: 0,
to: 100, // Move 100 pixels
config: {
mass: 1,
damping: 10, // Try changing this to 200 for no bounce
},
});
How does Remotion calculate these values so fast without storing state?
interpolate
interpolate is a stateless mathematical function. It doesn't know what happened in the previous frame. It simply calculates a ratio.
Let's look at the simplified implementation from packages/core/src/interpolate.ts.
Step 1: Normalization First, we figure out how far along the input we are (0.0 to 1.0).
// Simplified concept
const inputMin = 0;
const inputMax = 30;
const input = 15;
// Result is 0.5 (50%)
let result = (input - inputMin) / (inputMax - inputMin);
Step 2: Linear Interpolation (Lerp) Then, we map that percentage to the output range.
const outputMin = 0;
const outputMax = 500;
// 0.5 * (500 - 0) + 0 = 250
const finalValue = result * (outputMax - outputMin) + outputMin;
This math is incredibly efficient, allowing Remotion to render thousands of frames quickly.
springYou might think physics requires simulating every previous frame to know the current velocity. However, Remotion uses a closed-form solution (analytical math) to calculate the position of a spring at any specific time instantly.
This means you can jump to frame 100, and spring calculates the exact position without needing to calculate frames 0-99.
Simplified flow in packages/core/src/spring/index.ts:
durationInFrames is passed, it stretches the physics to fit that time.frame requested.from and to are provided, it maps the spring's natural 0-1 movement to your desired values.// Inside spring() function
const spr = springCalculation({
fps,
frame: durationProcessed, // The current time
config,
});
// If you asked for 0 to 100px, we map the spring result (0-1) to 0-100
return interpolate(spr.current, [0, 1], [from, to]);
Sometimes you want to animate a CSS string like 10px to 50% or colors like red to blue. Remotion provides interpolateStyles for this.
It automatically detects units and numbers inside strings.
import { interpolateStyles } from 'remotion';
const style = interpolateStyles(
frame,
[0, 30],
[{ color: 'red', width: '10px' }, { color: 'blue', width: '100px' }]
);
// Result at frame 15: { color: 'purple', width: '55px' }
See packages/animation-utils/src/transformation-helpers/interpolate-styles/index.tsx for details on how it parses and splits strings to animate numbers individually.
In this chapter, we unlocked the power of motion:
interpolate: Maps time to values linearly. Use clamp to set limits.spring: Creates fluid, organic motion based on physics.Now that we know how to create components and animate them, we need a way to view and debug our video. In the next chapter, we will explore the tool you see in the browser: The Player.
Generated by Code IQ