PTENES
MODULE 1.2

📈 interpolate() — frame becomes a value

O interpolate() is Remotion’s central tool: you provide a frame, and it returns a number—opacity, position, scale, color. One call, endless animations.

6
Topics
40
Minutes
Basic
Level
Practical
Type
frame 42 interpolate(frame, [0, 30], [0, 1]) inputRange → outputRange opacity 1.4 entrance: current frame clamp: value stays between 0 and 1 output: any number
1

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

Full signature interpolate.ts
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).
arg 1
Input value
arg 2
inputRange []
arg 3
outputRange []
arg 4
options (optional)
2

🗺️ 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.
Slide + opacity together SlideIn.tsx
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.

Domain
frame inputRange
Codomain
outputRange of values
Linear
Proportional by default
Any value
px, degrees, 0–1
3

🔒 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 extrapolateLeft when the animation starts late (frame 45+).
  • ✗Use different options for clamp/extend/wrap/identity — they don't exist.
clamp on both sides Fade.tsx
// 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",
});
clamp
Holds at the extremes
extend
Continues the straight line
wrap
Resets in a loop
Pattern
extend (use with caution!)
4

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

0

frame 0 → opacity 0

Element is invisible at the start.

15

frame 15 → opacity 1

Complete fade-in in 0.5s.

75

frame 75 → opacity 1

Stays fully visible until 2.5s — the "hold".

90

frame 90 → opacity 0

0.5s fade-out before the cut.

fade-in / hold / fade-out FadeInOut.tsx
const opacity = interpolate(
  frame,
  [0, 15, 75, 90],   // 4 keyframes
  [0,  1,  1,  0],   // entra, segura, sai
  { extrapolateLeft: "clamp", extrapolateRight: "clamp" }
);
Keyframes
N points in the range
Hold
Same value repeated
Text pattern
0→1→1→0
Flexible
Any sequence
5

🎨 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.
Entrance with natural easing EasedSlide.tsx
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.

linear
Constant speed
Easing.out
Slows down at the end
Easing.in
Accelerates to the end
bezier
Full control
6

🧩 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 scale e translateX without transform in a single string (they disappear).
  • ✗Use width/height animated instead of transform: scale (causes reflow).
Fade + slide + scale combined Entrance.tsx
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})`.

opacity
0→1 fade
translateX/Y
slide
scale
grows/shrinks
Combine
1 string transform

📌 Module summary

✓
interpolate(frame, inputRange, outputRange, opts) — maps any frame to any value.
✓
inputRange → outputRange — frame domain mapped linearly to any range of values.
✓
extrapolate: "clamp" — prevents opacity from going above 1 or below 0.
✓
Multiple keyframes — [0,15,75,90]→[0,1,1,0] creates a fade-in, hold, and fade-out in one line.
✓
Easing — 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.