📝 The interpolate() signature
O interpolate() takes four arguments: the input value (almost always frame), o inputRange, o outputRange and optional options. In return, it gives you a number calculated by linear interpolation between the points.
import { interpolate, useCurrentFrame } from "remotion"; const frame = useCurrentFrame(); // interpolate(valor, inputRange, outputRange, opcoes?) const opacity = interpolate( frame, // entrada: número atual [0, 30], // inputRange: do frame 0 ao 30 [0, 1], // outputRange: de 0 a 1 (opacidade) { extrapolateLeft: "clamp", extrapolateRight: "clamp" } );
🎯 What each argument does
- •frame — the number that changes each frame; it can be any numeric variable.
- •inputRange — the frame window where the animation happens (at least 2 points).
- •outputRange — the corresponding output values; they can be pixels, degrees, or decimals.
- •options — controls what happens outside the inputRange (clamp, extend, wrap).
🗺️ inputRange and outputRange — domain and codomain
Think in inputRange like the domain (which frames are part of the animation) and outputRange like the codomain (which values come out). The interpolate() finds the proportionally equivalent point in the outputRange.
📊 Mapping examples
[0, 30] → [0, 1]— fade-in: opacity increases from 0 to 1 in 1 second (30fps).[0, 30] → [0, 300]— slide: translateX goes from 0 to 300px in 1 second.[0, 30] → [0.5, 1]— scale: object starts at half size and grows to 100%.[0, 30] → [360, 0]— reverse rotation: starts rotated 360° and moves to 0.
const opacity = interpolate(frame, [0, 20], [0, 1], { extrapolateRight: "clamp" }); const translateY = interpolate(frame, [0, 20], [40, 0], { extrapolateRight: "clamp" }); return ( <div style={{ opacity, transform: `translateY(${translateY}px)` }}> Conteúdo que entra de baixo </div> );
💡 Tip: ranges don't need to start at 0
You can use [45, 60] as inputRange so the animation starts 1.5s after the video begins. Values outside the range are controlled by the extrapolate.
🔒 extrapolate: clamp vs extend
What happens when frame exits the inputRange? By default, the interpolate() extends the line beyond the defined points — which can make the opacity exceed 1 or drop below 0. The clamp prevents this.
✓ What to DO
- ✓Use
extrapolateRight: "clamp"in opacity so it never exceeds 1. - ✓Use
extrapolateLeft: "clamp"to hold the value before the animation starts. - ✓Make
extend(default) only when extrapolation makes sense (e.g., a position that continues).
✗ What NOT to do
- ✗Leave opacity without
clamp— at frame 60,[0,30]→[0,1]returns 2 (invisible in CSS). - ✗Forgetting the
extrapolateLeftwhen the animation starts late (frame 45+). - ✗Use different options for
clamp/extend/wrap/identity— they don't exist.
// SEM clamp: frame=60 → opacity=2 (bug visual) const opacityBug = interpolate(frame, [0, 30], [0, 1]); // COM clamp: frame=60 → opacity=1 (correto) const opacity = interpolate(frame, [0, 30], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp", });
🔑 Multiple keyframes — fade-in / hold / fade-out
O inputRange e o outputRange accept more than two points. This creates keyframes: the animation passes through each pair [frame → valor] in sequence. It’s like a video editor’s timeline, but in code.
frame 0 → opacity 0
Element is invisible at the start.
frame 15 → opacity 1
Complete fade-in in 0.5s.
frame 75 → opacity 1
Stays fully visible until 2.5s — the "hold".
frame 90 → opacity 0
0.5s fade-out before the cut.
const opacity = interpolate( frame, [0, 15, 75, 90], // 4 keyframes [0, 1, 1, 0], // entra, segura, sai { extrapolateLeft: "clamp", extrapolateRight: "clamp" } );
🎨 Easing — natural movement
By default, the interpolate() performs an interpolation linear — constant speed from start to finish. For natural movement, use the option easing with module functions Easing from Remotion (compatible with React Native).
🎯 Most-used easings
- •
Easing.out(Easing.ease)— starts fast, then slows down at the end. Classic for entrances. - •
Easing.in(Easing.ease)— starts slowly, then accelerates. Good for exits. - •
Easing.inOut(Easing.ease)— smooth at both ends. Solid for transitions. - •
Easing.bezier(0.25, 0.1, 0.25, 1)— full control with a cubic curve. - •
Easing.cubic— a quick alternative to bezier with a standard cubic curve.
import { interpolate, Easing, useCurrentFrame } from "remotion"; const frame = useCurrentFrame(); const translateY = interpolate(frame, [0, 25], [60, 0], { extrapolateRight: "clamp", easing: Easing.out(Easing.cubic), });
💡 Linear vs. easing in practice
A linear animation feels "mechanical" — an object moves at a robotic speed. With Easing.out, the element enters with energy and naturally slows down, as if obeying the laws of physics. That difference is what separates prototypes from professional videos.
🧩 Patterns: fade, slide, and scale
With interpolate() on hand, three patterns cover 90% of video UI animations: fade (opacity), slide (translateX/Y) and scale. By combining the three, you can build any element entrance or exit.
✓ The three essential patterns
- ✓Fade:
opacity = interpolate(frame, [0,20], [0,1], {clamp}) - ✓Slide X:
transform: translateX(${x}px)with x from −200 to 0. - ✓Scale:
transform: scale(${s})with s from 0.8 to 1.
✗ Common mistakes
- ✗Use CSS transitions or keyframes—they conflict with Remotion's render model.
- ✗Combine
scaleetranslateXwithouttransformin a single string (they disappear). - ✗Use
width/heightanimated instead oftransform: scale(causes reflow).
const opts = { extrapolateRight: "clamp", easing: Easing.out(Easing.cubic) } as const; const opacity = interpolate(frame, [0, 20], [0, 1], opts); const x = interpolate(frame, [0, 20], [-60, 0], opts); const scale = interpolate(frame, [0, 20], [0.9, 1], opts); return ( <div style={{ opacity, transform: `translateX(${x}px) scale(${scale})`, }}> Conteúdo com entrada suave </div> );
💡 Always combine in the same transform
When you use transform twice in the same style object, the second overwrites the first one. Always concatenate everything into a single string: `translateX(${x}px) scale(${s})`.
📌 Module summary
Easing.out(Easing.cubic) makes the motion feel natural; combine it with the fade/slide/scale patterns.Next module:
1.3 — spring() & composition: spring physics, natural overshoot, and how to stack scenes with Sequence and AbsoluteFill.