remotion-video

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Remotion (Programmatic Video)

Remotion(程序化视频)

Build real MP4/GIF/WebM videos in React. Every frame is a pure function of
useCurrentFrame()
, so output is deterministic, scrubbable, diffable, and renderable in CI. The code-first alternative to After Effects for templated and data-driven motion graphics.
在React中构建真实的MP4/GIF/WebM视频。每一帧都是
useCurrentFrame()
的纯函数,因此输出具有确定性,可擦洗、可对比,并且能在CI环境中渲染。它是After Effects的代码优先替代方案,适用于模板化和数据驱动的动态图形。

When to use

适用场景

  • Render MP4/GIF/WebM from code (social clips, title cards, explainers).
  • Templated/data-driven videos: one composition, many outputs from props (per-user, per-record, per-row of a CSV/DB).
  • Motion graphics that must be versioned, code-reviewed, and rendered in CI without a GUI.
  • Programmatic audio sync, charts that animate from data, or embedding shaders/Three.js into video.
  • 通过代码渲染MP4/GIF/WebM(社交片段、标题卡片、解说视频)。
  • 模板化/数据驱动视频:一个合成内容,通过props生成多种输出(面向用户、单条记录、CSV/数据库的每一行)。
  • 需要进行版本控制、代码评审,且无需GUI即可在CI环境中渲染的动态图形。
  • 程序化音频同步、基于数据动效的图表,或在视频中嵌入着色器/Three.js。

Core techniques

核心技术

Frame-driven animation

帧驱动动画

Animation is derived from the current frame, never from
setState
or
requestAnimationFrame
.
interpolate
maps an input range to an output range;
spring
produces physically natural motion.
jsx
import {useCurrentFrame, useVideoConfig, interpolate, spring} from 'remotion';

export const Title = () => {
  const frame = useCurrentFrame();
  const {fps} = useVideoConfig();

  // Fade in over frames 0-30, then stay (clamp prevents over/undershoot).
  const opacity = interpolate(frame, [0, 30], [0, 1], {
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp',
  });

  // Spring entrance; divide by spring duration is unnecessary — drive transforms directly.
  const scale = spring({frame, fps, config: {damping: 200, mass: 1, stiffness: 100}});

  return <h1 style={{opacity, transform: `scale(${scale})`}}>Hello</h1>;
};
Always clamp
interpolate
unless an intentional overshoot is desired — by default it extrapolates linearly past the range, which produces opacity > 1 or negative values.
动画由当前帧生成,绝不依赖
setState
requestAnimationFrame
interpolate
将输入范围映射到输出范围;
spring
生成符合物理规律的自然动效。
jsx
import {useCurrentFrame, useVideoConfig, interpolate, spring} from 'remotion';

export const Title = () => {
  const frame = useCurrentFrame();
  const {fps} = useVideoConfig();

  // 在0-30帧淡入,之后保持(clamp防止过度/不足)。
  const opacity = interpolate(frame, [0, 30], [0, 1], {
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp',
  });

  // 弹簧入场效果;无需除以弹簧时长——直接驱动变换。
  const scale = spring({frame, fps, config: {damping: 200, mass: 1, stiffness: 100}});

  return <h1 style={{opacity, transform: `scale(${scale})`}}>Hello</h1>;
};
除非需要故意的过度效果,否则请始终对
interpolate
进行clamp处理——默认情况下它会线性外推超出范围的值,这会导致不透明度大于1或出现负值。

Scheduling: Sequence and Series

调度:Sequence与Series

<Sequence>
shifts time: children see
frame
reset to 0 at the sequence's
from
. Use it to place clips on a timeline.
<Series>
lays out back-to-back segments without manual offset math.
jsx
import {Sequence, Series, AbsoluteFill} from 'remotion';

export const Timeline = () => (
  <AbsoluteFill style={{backgroundColor: 'black'}}>
    <Sequence from={0} durationInFrames={60}><Intro /></Sequence>
    <Sequence from={60} durationInFrames={90}><Body /></Sequence>

    {/* Series auto-sequences; offset overlaps the previous segment for crossfades */}
    <Series>
      <Series.Sequence durationInFrames={60}><ShotA /></Series.Sequence>
      <Series.Sequence durationInFrames={60} offset={-15}><ShotB /></Series.Sequence>
    </Series>
  </AbsoluteFill>
);
<Sequence>
用于偏移时间:子组件会看到
frame
在序列的
from
位置重置为0。使用它在时间轴上放置片段。
<Series>
无需手动计算偏移量,即可将片段按顺序排列。
jsx
import {Sequence, Series, AbsoluteFill} from 'remotion';

export const Timeline = () => (
  <AbsoluteFill style={{backgroundColor: 'black'}}>
    <Sequence from={0} durationInFrames={60}><Intro /></Sequence>
    <Sequence from={60} durationInFrames={90}><Body /></Sequence>

    {/* Series自动排序;offset用于与前一个片段重叠以实现交叉淡入 */}
    <Series>
      <Series.Sequence durationInFrames={60}><ShotA /></Series.Sequence>
      <Series.Sequence durationInFrames={60} offset={-15}><ShotB /></Series.Sequence>
    </Series>
  </AbsoluteFill>
);

Composition registration + parametric props

合成内容注册 + 参数化props

The
<Composition>
declares id, size, fps, duration, and
defaultProps
. A zod schema makes props type-safe and editable in the Studio sidebar.
jsx
import {Composition} from 'remotion';
import {z} from 'zod';

export const schema = z.object({
  title: z.string(),
  accent: z.string(),
  fps: z.number().default(30),
});

export const Root = () => (
  <Composition
    id="Promo"
    component={Promo}
    durationInFrames={150}
    fps={30}
    width={1080}
    height={1920}
    schema={schema}
    defaultProps={{title: 'Launch', accent: '#5b8cff', fps: 30}}
  />
);
To make duration data-dependent, use
calculateMetadata
on the Composition to compute
durationInFrames
from props (e.g. number of rows × frames per row) before render.
<Composition>
声明id、尺寸、fps、时长和
defaultProps
。zod模式可确保props类型安全,且能在Studio侧边栏中编辑。
jsx
import {Composition} from 'remotion';
import {z} from 'zod';

export const schema = z.object({
  title: z.string(),
  accent: z.string(),
  fps: z.number().default(30),
});

export const Root = () => (
  <Composition
    id="Promo"
    component={Promo}
    durationInFrames={150}
    fps={30}
    width={1080}
    height={1920}
    schema={schema}
    defaultProps={{title: 'Launch', accent: '#5b8cff', fps: 30}}
  />
);
若要让时长依赖数据,请在Composition上使用
calculateMetadata
,在渲染前根据props计算
durationInFrames
(例如:行数 × 每行帧数)。

Audio and beat sync

音频与节拍同步

jsx
import {Audio, staticFile, useCurrentFrame, useVideoConfig} from 'remotion';

const BEATS_SEC = [0.5, 1.0, 1.5, 2.0]; // detected offline
export const Music = ({children}) => {
  const frame = useCurrentFrame();
  const {fps} = useVideoConfig();
  const t = frame / fps;
  const onBeat = BEATS_SEC.some((b) => Math.abs(t - b) < 1 / fps);
  return (
    <>
      <Audio src={staticFile('track.mp3')} />
      <div style={{transform: `scale(${onBeat ? 1.06 : 1})`}}>{children}</div>
    </>
  );
};
Detect beats offline (e.g. with
web-audio-beat-detector
or aubio) and bake the timestamps into props — never analyze audio at render time, since headless rendering has no realtime audio clock.
jsx
import {Audio, staticFile, useCurrentFrame, useVideoConfig} from 'remotion';

const BEATS_SEC = [0.5, 1.0, 1.5, 2.0]; // 离线检测得到
export const Music = ({children}) => {
  const frame = useCurrentFrame();
  const {fps} = useVideoConfig();
  const t = frame / fps;
  const onBeat = BEATS_SEC.some((b) => Math.abs(t - b) < 1 / fps);
  return (
    <>
      <Audio src={staticFile('track.mp3')} />
      <div style={{transform: `scale(${onBeat ? 1.06 : 1})`}}>{children}</div>
    </>
  );
};
离线检测节拍(例如使用
web-audio-beat-detector
或aubio)并将时间戳写入props——绝不要在渲染时分析音频,因为无头渲染没有实时音频时钟。

Rendering

渲染

Preview in the browser-based Studio; render headlessly via CLI or the programmatic API.
bash
npx remotion studio                                  # interactive preview
npx remotion render Promo out/promo.mp4 \
  --props='{"title":"Launch","accent":"#f43"}'        # pass parametric props
npx remotion render Promo out.gif --codec=gif         # GIF output
For batch/data-driven pipelines, render in Node with
@remotion/renderer
(bundle once, render many) — see the reference file.
在基于浏览器的Studio中预览;通过CLI或程序化API进行无头渲染。
bash
npx remotion studio                                  # 交互式预览
npx remotion render Promo out/promo.mp4 \
  --props='{"title":"Launch","accent":"#f43"}'        # 传递参数化props
npx remotion render Promo out.gif --codec=gif         # GIF输出
对于批量/数据驱动的流水线,使用
@remotion/renderer
在Node中渲染(打包一次,多次渲染)——请参考参考文件。

Deliver & verify (rendered stills → MP4)

交付与验证(渲染静帧 → MP4)

Packaged helper (
scripts/
): tile your stills with
scripts/contact-sheet.sh sheet.png f-hook.png f-mid.png f-end.png
, then assert the encode with
scripts/probe-mp4.sh out.mp4 [WxH] [fps]
. See
scripts/README.md
.
Remotion is frame-deterministic by construction — every frame is a pure function of
useCurrentFrame()
, so you can render any exact frame headlessly with no seek harness (this is the heavy-tier counterpart to a web scene's
?t=N
). Use this tier when the output must be an MP4/GIF, must carry exact numbers/text, or batches from data; for a lightweight web animation, deliver standalone HTML instead.
Output contract:
  • A Remotion project with the composition registered (
    <Composition>
    + zod
    schema
    +
    defaultProps
    ), all motion frame-driven (no timers /
    Date.now()
    /
    Math.random()
    ).
  • Deliverable = the rendered
    out/*.mp4
    (plus the project, so the user can re-render with new props/data).
  • Duration data-dependent? compute it in
    calculateMetadata
    , not by hand.
Verify loop — render stills → inspect → encode. Render single frames first (cheap, no video encode), inspect them, and encode the full video only once the frames are right.
bash
undefined
打包助手
scripts/
目录):使用
scripts/contact-sheet.sh sheet.png f-hook.png f-mid.png f-end.png
拼接静帧,然后使用
scripts/probe-mp4.sh out.mp4 [WxH] [fps]
验证编码。详见
scripts/README.md
Remotion天生具有帧确定性——每一帧都是
useCurrentFrame()
的纯函数,因此你可以无头渲染任意精确帧,无需查找工具(这是Web场景
?t=N
的重量级替代方案)。当输出必须为MP4/GIF、必须包含精确数字/文本,或需要从数据批量生成时使用该方式;若为轻量级Web动画,直接交付独立HTML即可。
输出约定:
  • 一个已注册合成内容的Remotion项目(包含
    <Composition>
    + zod
    schema
    +
    defaultProps
    ),所有动效均由帧驱动(无计时器 /
    Date.now()
    /
    Math.random()
    )。
  • 交付物 = 渲染后的
    out/*.mp4
    (附带项目文件,以便用户使用新的props/数据重新渲染)。
  • 时长依赖数据?在
    calculateMetadata
    中计算,而非手动设置。
验证循环——渲染静帧 → 检查 → 编码。 先渲染单帧(成本低,无需视频编码),检查无误后再编码完整视频。
bash
undefined

1. Frame-exact stills at start / mid / end (PNG, headless, fast)

1. 精确渲染开始/中间/结束帧的静帧(PNG格式,无头渲染,速度快)

npx remotion still Promo out/f-start.png --frame=0 npx remotion still Promo out/f-mid.png --frame=75 npx remotion still Promo out/f-end.png --frame=149 # last frame = durationInFrames - 1
npx remotion still Promo out/f-start.png --frame=0 npx remotion still Promo out/f-mid.png --frame=75 npx remotion still Promo out/f-end.png --frame=149 # 最后一帧 = durationInFrames - 1

render with the SAME props you'll ship, not just defaultProps:

使用你要交付的相同props渲染,而非仅使用defaultProps:

npx remotion still Promo out/f-mid.png --frame=75 --props='{"title":"Launch"}'

npx remotion still Promo out/f-mid.png --frame=75 --props='{"title":"Launch"}'

2. Inspect each PNG — fidelity (matches brief; numbers/text correct) AND

2. 检查每张PNG——保真度(符合需求;数字/文本正确)以及

artifacts (text overflow, off-canvas, clipped safe-area, missing font, wrong data binding).

人工问题(文本溢出、超出画布、裁剪安全区域、字体缺失、数据绑定错误)。

3. Only after the stills check out, encode the video:

3. 仅在静帧检查通过后,编码视频:

npx remotion render Promo out/promo.mp4 --props='{"title":"Launch"}'

- Use `npx remotion compositions` to read each composition's `durationInFrames`/`fps` and pick the end frame.
- **Data-driven / batch**: verify ONE representative props set via stills *before* batch-rendering all rows — catch a layout bug once instead of N times.
- **README demo GIF for free**: `npx remotion render Promo out/demo.gif --codec=gif` produces the first-screen proof clip (Direction D).

**Before you finish:**
1. `npx remotion still` renders cleanly at frame 0, mid, and last — no errors, no missing assets/fonts.
2. Numbers/text are exact and inside safe areas at every checked frame.
3. Frame-driven only — no `Date.now()` / `Math.random()` / timers (determinism holds in CI).
4. Props are zod-typed; the **shipped** props render correctly (not just `defaultProps`).
5. Full MP4 encoded and plays; (optional) GIF rendered for the README.
npx remotion render Promo out/promo.mp4 --props='{"title":"Launch"}'

- 使用`npx remotion compositions`查看每个合成内容的`durationInFrames`/`fps`,并选择结束帧。
- **数据驱动/批量渲染**:在批量渲染所有行之前,先通过静帧验证一组有代表性的props——一次性发现布局错误,而非重复N次。
- **免费生成README演示GIF**:`npx remotion render Promo out/demo.gif --codec=gif`可生成首屏演示片段(Direction D)。

**完成前检查:**
1. `npx remotion still`能干净地渲染第0帧、中间帧和最后一帧——无错误,无缺失资源/字体。
2. 数字/文本精确且位于安全区域内(在所有检查过的帧中)。
3. 仅由帧驱动——无`Date.now()` / `Math.random()` / 计时器(在CI环境中保持确定性)。
4. Props由zod类型化;**交付的**props能正确渲染(而非仅`defaultProps`)。
5. 完整MP4已编码且可播放;(可选)已为README渲染GIF。

Quick reference

快速参考

NeedUse
Map time → value
interpolate(frame, [a,b], [x,y], {extrapolateRight:'clamp'})
Natural easing
spring({frame, fps, config})
Place a clip at t
<Sequence from durationInFrames>
Back-to-back shots
<Series>
Audio
<Audio src={staticFile(...)} />
Frame ↔ seconds
frame / fps
Loop a value
frame % period
then interpolate
Editable propszod
schema
on
<Composition>
CI render
npx remotion render
or
@remotion/renderer
需求解决方案
将时间映射为值
interpolate(frame, [a,b], [x,y], {extrapolateRight:'clamp'})
自然缓动
spring({frame, fps, config})
在时间点t放置片段
<Sequence from durationInFrames>
连续镜头
<Series>
音频
<Audio src={staticFile(...)} />
帧 ↔ 秒
frame / fps
循环值
frame % period
后进行插值
可编辑props
<Composition>
上使用zod
schema
CI渲染
npx remotion render
@remotion/renderer

Gotchas

注意事项

  • Never use
    Math.random()
    ,
    Date.now()
    , or animation timers — they break determinism. Use
    random(seed)
    from Remotion for stable per-frame randomness.
  • Load fonts and assets via
    staticFile()
    and wait with
    delayRender
    /
    continueRender
    , or fonts pop in mid-render.
  • Default
    interpolate
    extrapolates — clamp it.
  • useCurrentFrame
    inside a
    <Sequence>
    is local (starts at 0); use
    useVideoConfig().durationInFrames
    for absolute timing.
  • 绝不要使用
    Math.random()
    Date.now()
    或动画计时器——它们会破坏确定性。如需稳定的每帧随机性,请使用Remotion提供的
    random(seed)
  • 通过
    staticFile()
    加载字体和资源,并使用
    delayRender
    /
    continueRender
    等待加载完成,否则字体可能在渲染中途突然出现。
  • 默认的
    interpolate
    会外推——请进行clamp处理。
  • <Sequence>
    内部的
    useCurrentFrame
    是局部的(从0开始);如需绝对时间,请使用
    useVideoConfig().durationInFrames

Reference files

参考文件

  • references/api-and-patterns.md
    interpolate
    options and
    Easing
    , spring config recipes, Sequence/Series scheduling,
    <Audio>
    + beat-sync, parametric
    defaultProps
    with zod +
    calculateMetadata
    , CLI flags, programmatic
    @remotion/renderer
    batch rendering, and embedding GLSL shaders / Three.js (
    @remotion/three
    ).
  • references/api-and-patterns.md
    ——
    interpolate
    选项与
    Easing
    、弹簧配置方案、Sequence/Series调度、
    <Audio>
    + 节拍同步、带zod +
    calculateMetadata
    的参数化
    defaultProps
    、CLI标志、程序化
    @remotion/renderer
    批量渲染,以及嵌入GLSL着色器/Three.js(
    @remotion/three
    )。