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 }); ```