remotion-video
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRemotion (Programmatic Video)
Remotion(程序化视频)
Build real MP4/GIF/WebM videos in React. Every frame is a pure function of , so output is deterministic, scrubbable, diffable, and renderable in CI. The code-first alternative to After Effects for templated and data-driven motion graphics.
useCurrentFrame()在React中构建真实的MP4/GIF/WebM视频。每一帧都是的纯函数,因此输出具有确定性,可擦洗、可对比,并且能在CI环境中渲染。它是After Effects的代码优先替代方案,适用于模板化和数据驱动的动态图形。
useCurrentFrame()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 or . maps an input range to an output range; produces physically natural motion.
setStaterequestAnimationFrameinterpolatespringjsx
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 unless an intentional overshoot is desired — by default it extrapolates linearly past the range, which produces opacity > 1 or negative values.
interpolate动画由当前帧生成,绝不依赖或。将输入范围映射到输出范围;生成符合物理规律的自然动效。
setStaterequestAnimationFrameinterpolatespringjsx
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>;
};除非需要故意的过度效果,否则请始终对进行clamp处理——默认情况下它会线性外推超出范围的值,这会导致不透明度大于1或出现负值。
interpolateScheduling: Sequence and Series
调度:Sequence与Series
<Sequence>framefrom<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 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>framefrom<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 declares id, size, fps, duration, and . A zod schema makes props type-safe and editable in the Studio sidebar.
<Composition>defaultPropsjsx
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 on the Composition to compute from props (e.g. number of rows × frames per row) before render.
calculateMetadatadurationInFrames<Composition>defaultPropsjsx
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上使用,在渲染前根据props计算(例如:行数 × 每行帧数)。
calculateMetadatadurationInFramesAudio 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 or aubio) and bake the timestamps into props — never analyze audio at render time, since headless rendering has no realtime audio clock.
web-audio-beat-detectorjsx
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>
</>
);
};离线检测节拍(例如使用或aubio)并将时间戳写入props——绝不要在渲染时分析音频,因为无头渲染没有实时音频时钟。
web-audio-beat-detectorRendering
渲染
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 outputFor batch/data-driven pipelines, render in Node with (bundle once, render many) — see the reference file.
@remotion/renderer在基于浏览器的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输出对于批量/数据驱动的流水线,使用在Node中渲染(打包一次,多次渲染)——请参考参考文件。
@remotion/rendererDeliver & verify (rendered stills → MP4)
交付与验证(渲染静帧 → MP4)
Packaged helper (): tile your stills withscripts/, then assert the encode withscripts/contact-sheet.sh sheet.png f-hook.png f-mid.png f-end.png. Seescripts/probe-mp4.sh out.mp4 [WxH] [fps].scripts/README.md
Remotion is frame-deterministic by construction — every frame is a pure function of , so you can render any exact frame headlessly with no seek harness (this is the heavy-tier counterpart to a web scene's ). 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.
useCurrentFrame()?t=NOutput contract:
- A Remotion project with the composition registered (+ zod
<Composition>+schema), all motion frame-driven (no timers /defaultProps/Date.now()).Math.random() - Deliverable = the rendered (plus the project, so the user can re-render with new props/data).
out/*.mp4 - Duration data-dependent? compute it in , not by hand.
calculateMetadata
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天生具有帧确定性——每一帧都是的纯函数,因此你可以无头渲染任意精确帧,无需查找工具(这是Web场景的重量级替代方案)。当输出必须为MP4/GIF、必须包含精确数字/文本,或需要从数据批量生成时使用该方式;若为轻量级Web动画,直接交付独立HTML即可。
useCurrentFrame()?t=N输出约定:
- 一个已注册合成内容的Remotion项目(包含+ zod
<Composition>+schema),所有动效均由帧驱动(无计时器 /defaultProps/Date.now())。Math.random() - 交付物 = 渲染后的(附带项目文件,以便用户使用新的props/数据重新渲染)。
out/*.mp4 - 时长依赖数据?在中计算,而非手动设置。
calculateMetadata
验证循环——渲染静帧 → 检查 → 编码。 先渲染单帧(成本低,无需视频编码),检查无误后再编码完整视频。
bash
undefined1. 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
快速参考
| Need | Use |
|---|---|
| Map time → value | |
| Natural easing | |
| Place a clip at t | |
| Back-to-back shots | |
| Audio | |
| Frame ↔ seconds | |
| Loop a value | |
| Editable props | zod |
| CI render | |
| 需求 | 解决方案 |
|---|---|
| 将时间映射为值 | |
| 自然缓动 | |
| 在时间点t放置片段 | |
| 连续镜头 | |
| 音频 | |
| 帧 ↔ 秒 | |
| 循环值 | |
| 可编辑props | 在 |
| CI渲染 | |
Gotchas
注意事项
- Never use ,
Math.random(), or animation timers — they break determinism. UseDate.now()from Remotion for stable per-frame randomness.random(seed) - Load fonts and assets via and wait with
staticFile()/delayRender, or fonts pop in mid-render.continueRender - Default extrapolates — clamp it.
interpolate - inside a
useCurrentFrameis local (starts at 0); use<Sequence>for absolute timing.useVideoConfig().durationInFrames
- 绝不要使用、
Math.random()或动画计时器——它们会破坏确定性。如需稳定的每帧随机性,请使用Remotion提供的Date.now()。random(seed) - 通过加载字体和资源,并使用
staticFile()/delayRender等待加载完成,否则字体可能在渲染中途突然出现。continueRender - 默认的会外推——请进行clamp处理。
interpolate - 内部的
<Sequence>是局部的(从0开始);如需绝对时间,请使用useCurrentFrame。useVideoConfig().durationInFrames
Reference files
参考文件
- —
references/api-and-patterns.mdoptions andinterpolate, spring config recipes, Sequence/Series scheduling,Easing+ beat-sync, parametric<Audio>with zod +defaultProps, CLI flags, programmaticcalculateMetadatabatch rendering, and embedding GLSL shaders / Three.js (@remotion/renderer).@remotion/three
- ——
references/api-and-patterns.md选项与interpolate、弹簧配置方案、Sequence/Series调度、Easing+ 节拍同步、带zod +<Audio>的参数化calculateMetadata、CLI标志、程序化defaultProps批量渲染,以及嵌入GLSL着色器/Three.js(@remotion/renderer)。@remotion/three