---
title: "Animation: Frames, Interpolation & Springs"
type: concept
tags: [animation, interpolate, spring, easing, noise, randomness]
created: 2026-06-11
updated: 2026-06-11
confidence: high
sources: ["raw/github_doc-packages-docs-docs-animating-properties-mdx.md", "raw/github_doc-packages-docs-docs-interpolate-mdx.md", "raw/github_doc-packages-docs-docs-spring-mdx.md", "raw/github_doc-packages-docs-docs-measure-spring-mdx.md", "raw/github_doc-packages-docs-docs-animation-math-mdx.md", "raw/github_doc-packages-docs-docs-noise-visualization-mdx.md", "raw/github_doc-packages-docs-docs-interpolate-colors-mdx.md", "raw/github_doc-packages-docs-docs-random-mdx.md"]
---

# Animation: Frames, Interpolation & Springs

## Definition

Animation in Remotion is a pure function of the frame number: `useCurrentFrame()` gives the current frame, and you derive every animated property from it. Two helpers do most of the work — `interpolate()` maps a value range to an output range (with easing and clamping), and `spring()` runs a physics simulation from 0 to 1. Because frames render concurrently across multiple headless browser tabs, anything time- or randomness-dependent must be deterministic per frame.

## How It Works

### The frame-driven model

A fade-in is just math on the frame: `const opacity = Math.min(1, frame / 60);`. The cardinal rule: **always animate using `useCurrentFrame()`** — CSS transitions and other wall-clock animations cause flickering during rendering because frames are captured out of order.

### `interpolate(input, inputRange, outputRange, options?)`

```ts
const opacity = interpolate(frame, [0, 60], [0, 1], {
  extrapolateRight: "clamp",
});
```

- **Multiple keyframes**: `interpolate(frame, [0, 20, durationInFrames - 20, durationInFrames], [0, 1, 1, 0])` fades in and out (`durationInFrames` from `useVideoConfig()`).
- **Any driver, not just time**: feed a spring into it — `interpolate(driver, [0, 1], [0, 200])` maps a 0→1 spring to 0→200px.
- **Extrapolation**: `extrapolateLeft` / `extrapolateRight` accept `extend` (default), `clamp`, `wrap`, `identity`. E.g. `interpolate(1.5, [0, 1], [0, 2], {extrapolateRight: 'clamp'}) // 2`.
- **Easing**: `easing: Easing.bezier(0.8, 0.22, 0.96, 0.65)` curves the progress within the active segment; from v4.0.462 pass an array with one easing per segment, e.g. `[Easing.out(Easing.cubic), Easing.in(Easing.cubic)]` for a 3-keyframe range (length must be `inputRange.length - 1`).
- **Posterize** (v4.0.470+): `posterize: 3` quantizes the input so frames 0–2 share frame 0's value — a stop-motion/sampled look.
- **CSS transform strings** (v4.0.472+): output ranges may be `scale`, `translate`, `rotate`, or `transformOrigin` strings, e.g. `translate: interpolate(frame, [0, 30], ['0px 0px', '100px 50px'])`. Units must match per component.
- **Numeric tuples** (v4.0.473+): output range entries can be same-length number tuples.
- Input and output ranges must have equal length; single-value ranges work from v4.0.469.

### `spring()` and `measureSpring()`

```ts
const value = spring({
  frame,
  fps,
  config: {
    stiffness: 100,
  },
});
```

Animates `from: 0` to `to: 1` by default; pass `fps` from `useVideoConfig()`. Config knobs: `mass` (default `1`, lower = faster), `damping` (default `10`, higher = less bounce), `stiffness` (default `100`, bounciness), `overshootClamping` (default `false`, caps at `to`). Timing knobs: `durationInFrames` stretches the curve to an exact length, `delay` holds the initial value for n frames, `reverse` plays backwards. Order of operations: stretch → reverse → delay. Delay can also be done manually via `frame - 20` as the `frame` argument.

`measureSpring({fps, config})` returns how many frames a spring needs to settle (e.g. `measureSpring({fps: 30, config: {damping: 200}}) // => 23`); `threshold` (default `0.005`) defines "settled" — lower threshold, longer measured duration. Use it to size sequences around a spring.

### Animation math

Spring values are plain numbers, so combine them arithmetically. The enter/exit idiom:

```ts
const enter = spring({fps, frame, config: {damping: 200}});
const exit = spring({fps, frame, config: {damping: 200}, durationInFrames: 20, delay: durationInFrames - 20});
const scale = enter - exit;
```

`enter` rises 0→1 at the start; `exit` rises 0→1 in the last 20 frames; their difference scales the element in and back out with one expression.

### Colors: `interpolateColors()`

`interpolateColors(frame, [0, 20], ['red', 'yellow'])` returns an `rgba()` string (e.g. `rgba(255, 128, 0, 1)`). Accepts hex, `rgb`/`rgba`, `hsl`/`hsla`, CSS color names, and (v4.0.439+) `oklch`, `oklab`, `lab`, `lch`, `hwb` with optional `/ alpha`. Options: `easing` (v4.0.475+, single function or per-segment array) and `posterize` (v4.0.470+).

### Noise and deterministic randomness

- `@remotion/noise` provides `noise3D(seed, x, y, z)` — use two dimensions for space and the third for time (`frame * speed`) to get organic motion like a floating dot grid; map its `[-1, 1]` output with `interpolate()`.
- `random(seed)` from `remotion` returns a deterministic pseudorandom number in `[0, 1)` for a `number` or `string` seed: `random(1) // 0.07301638228818774`, always. Seed per element (`random(`random-x-${i}`)`) for stable particle layouts. `Math.random()` triggers an ESLint warning because each render thread would see different values; if you truly want non-determinism, `random(null)` bypasses the warning.

## Key Parameters

- `interpolate()` options: `extrapolateLeft`, `extrapolateRight`, `easing`, `posterize`. Types `ExtrapolateType` and `InterpolateOptions` exported since v3.3.77.
- `spring()` args: `frame`, `fps`, `from`, `to`, `reverse`, `delay`, `durationInFrames`, `durationRestThreshold`, `config: {mass, damping, stiffness, overshootClamping}`.
- `measureSpring()` args: `fps`, `threshold`, `config`, `from`, `to`.
- `random()` arg: `number | string | null`.

## When To Use

- Linear/keyframed property changes → `interpolate()` with `clamp`.
- Natural, bouncy entrances and scale pops → `spring()`; flatten the bounce with `damping: 200`.
- Combined enter/exit on one element → animation math (`enter - exit`).
- Animated color shifts → `interpolateColors()`.
- Organic wobble, particles, generative art → `@remotion/noise` + `random(seed)`.

## Risks & Pitfalls

- **CSS transitions/animations flicker in renders** — never animate outside `useCurrentFrame()`.
- **Forgetting `extrapolateRight: 'clamp'`** lets values run past the output range (scale 2 at frame 40 for a `[0, 20] → [0, 1]` mapping).
- **`Math.random()` differs per render thread** — use `random(seed)`.
- **Springs overshoot by design** — values can exceed `to` briefly; use `overshootClamping: true` if that breaks layout.
- **Hardcoding spring length** — a spring's natural settle time depends on config and fps; measure with `measureSpring()` instead of guessing.
- **Per-segment easing array of wrong length** errors — must be `inputRange.length - 1`.

## Related Concepts

- [[concepts/compositions-fundamentals]] — `<Sequence>` shifts the frame that drives these helpers
- [[concepts/transitions]] — packaged scene-to-scene animations
- [[concepts/audio]] — driving animation from audio data
- [[syntheses/performance-recipes]] — keeping per-frame work cheap

## Sources

- raw/github_doc-packages-docs-docs-animating-properties-mdx.md — frame-driven model, flickering warning
- raw/github_doc-packages-docs-docs-interpolate-mdx.md — `interpolate()` full API incl. easing, posterize, CSS transforms
- raw/github_doc-packages-docs-docs-spring-mdx.md / -measure-spring-mdx.md — spring API and measurement
- raw/github_doc-packages-docs-docs-animation-math-mdx.md — enter/exit composition
- raw/github_doc-packages-docs-docs-interpolate-colors-mdx.md — color interpolation
- raw/github_doc-packages-docs-docs-noise-visualization-mdx.md — `noise3D()` technique
- raw/github_doc-packages-docs-docs-random-mdx.md — deterministic randomness
