← Files RemotionARCHIVED FILE

skills/remotion-best-practices/remotion-markup/timing.md

3.24 KB · Oct 5, 2026 · 18:37 UTC

↓ Download file

Drive motion with `interpolate()` over an explicit frame range. 
To customize timing, use **`Easing.bezier`** or `Easing.spring`.

A simple linear interpolation is done using the `interpolate` function.

```ts title="Going from 0 to 1 over 0.3 seconds"
import { interpolate } from "remotion";

const opacity = interpolate(frame, [0, 0.3 * fps], [0, 1]);
```

By default, the values are not clamped, so the value can go outside the range [0, 1].  
Here is how they can be clamped:

```ts title="Going from 0 to 1 over 0.3 seconds with extrapolation"
const opacity = interpolate(frame, [0, 0.3 * fps], [0, 1], {
  extrapolateRight: "clamp",
  extrapolateLeft: "clamp",
});
```

## Studio-editable animation patterns

When an animation should be editable in Remotion Studio, keep the `interpolate()` call directly in the `style` prop and prefer individual CSS transform properties:

```tsx
// 👍 Inline editable keyframes and transform shorthands
style={{
  scale: interpolate(frame, [0, 100], [0, 1]),
  translate: interpolate(frame, [0, 100], ["0px 0px", "100px 100px"]),
  rotate: interpolate(frame, [0, 100], ["20deg", "90deg"]),
}}

// 👎 Hidden values and transform strings become computed in Studio
const translateY = interpolate(frame, [0, 100], [0, 120]);
const rotation = interpolate(frame, [0, 100], [0, 20]);

style={{
  transform: `translateY(${translateY}px) rotate(${rotation}deg)`,
}}
```

Use `transform` strings only when individual CSS transform properties do not cover the effect, such as `skew()`, `perspective()`, or order-sensitive multi-transform chains.

## Spring easing

A nice push movement with no bounce:

```ts
import { interpolate, Easing } from "remotion";

const opacity = interpolate(frame, [0, 0.3 * fps], [0, 1], {
  easing: Easing.spring({damping: 200}),
  extrapolateLeft: "clamp",
  extrapolateRight: "clamp",
});
```

## Bézier easing

Pass values like you would to a CSS cubic-bezier function.

```ts
import { interpolate, Easing } from "remotion";

const opacity = interpolate(frame, [0, 0.3 * fps], [0, 1], {
  easing: Easing.bezier(0.16, 1, 0.3, 1),
  extrapolateLeft: "clamp",
  extrapolateRight: "clamp",
});
```

## Animating scale

When animating scale, if the output is linear, the perceived scale would be smaller the larger the scale gets.  
Use this option to compensate:

```ts
const scale = interpolate(frame, [0, 0.3 * fps], [0, 1], {
  easing: Easing.bezier(0.16, 1, 0.3, 1),
  extrapolateLeft: "clamp",
  extrapolateRight: "clamp",
  output: 'perceptual-scale' // <- Add this to scale animations
});
```

## Multiple keyframes

Add as many keyframes as you want. For multiple easings, pass an array with `n - 1` items:

```ts
const scale = interpolate(frame, [0, 1 * fps, 9 * fps, 10 * fps], [0, 1, 1, 0], {
  easing: [Easing.bezier(0.16, 1, 0.3, 1), Easing.linear, Easing.spring({damping: 200})],
  extrapolateLeft: "clamp",
  extrapolateRight: "clamp",
  output: 'perceptual-scale' // <- Add this to scale animations
});
```

## Posterization

You can intentionally reduce the frame rate for artistic reason. Use if it makes sense.

```ts
const scale = interpolate(frame, [0, 1 * fps], [0, 1], {
  easing: Easing.bezier(0.16, 1, 0.3, 1),
  extrapolateLeft: "clamp",
  extrapolateRight: "clamp",
  posterize: 3 // Only every 3rd frame is sampled
});
```

SHA-256: 48047e758b96aea519916479ede62b832bf51f982924978e091c37f569ab4bac