使用 Claude Code 和 Remotion 制作视频
Claude Code 配合 Remotion 技能是一套通过自然语言提示生成视频的工作流:安装技能包、搭建 Remotion 项目、用普通英文描述视频内容,然后让 Claude 编写能精确渲染到 MP4 的 React 代码。整个流程就是如此简洁。真正的阻力从来不在于如何提示——而在于能否充分理解 Claude 生成的代码,以便在渲染结果偏离预期时进行修正。本文将通过一个面向开发者的完整示例,带你了解整个流程,并教你如何掌握 Remotion 的核心思维模型,从而能够通过 Studio 预览来诊断渲染问题,而不是盲目地反复重新提示。
核心要点
- Remotion 是一个 React 框架,它将视频渲染为每一帧都是帧编号纯函数的形式,通过
useCurrentFrame()访问,并对输出进行截图以生成 MP4。 - 在 30fps 下,一段 15 秒的视频共有 450 帧;每个动画都是从帧编号到 CSS 值的映射,无需任何时间轴编辑器。
- 在各类教程中,安装命令出现过多种形式——在执行之前,请务必对照
remotion.dev/docs/ai/claude-code核实当前的正确命令,因为技能工具链一直处于变动之中。 - 当预览中所有动画同时触发时,原因几乎总是
<Sequence>组件缺少from偏移值——该属性默认从第 0 帧开始。 - Remotion 通过启动无头 Chromium 并对每一帧进行截图来完成渲染,这正是为什么一段 30fps 的 30 秒合成视频需要 900 张截图,也是 Remotion Lambda 存在于生产流水线中的原因。
这套技术栈是什么
Remotion 是一个用于以编程方式创建视频的开源 React 框架:你将场景定义为 React 组件,用帧编号控制时序,并将输出渲染为 MP4、WebM 或 GIF——无需时间轴编辑器,无需拖拽操作。Remotion 的 Claude Code 技能包是一套规则集,用于向 AI 代理传授 Remotion 的 API 接口——包括合成(compositions)、序列(sequences)、interpolate()、spring()、渲染配置等——使其能够生成正确的帧数学计算,而非凭空猜测。它不是一个在渲染时运行的插件,而是注入到 Claude 上下文中的知识,使其编写的代码能够按照你的描述正确编译和执行动画。
Remotion 的核心思维模型是什么?
在 Remotion 中,你的视频是帧编号的纯函数。一个合成(composition)是一个具有固定帧数时长的 React 组件。在 30fps 下,15 秒的视频共有 450 帧,每个动画都表示为从当前帧到某个 CSS 值(透明度、变换、颜色)的映射。没有时间轴,只有数学计算。
整个模型由四个基本原语构成,均记录在 Remotion 基础文档中:
useCurrentFrame()返回当前正在渲染的帧编号。你的组件会在每一帧重新执行。- fps 和时长 定义在
<Composition>上。根据合成文档,你需要在此处设置durationInFrames、fps、width和height。 <Sequence>用于时间偏移。包裹在<Sequence from={90}>中的子组件,在全局帧为 90 时会看到帧 0,这正是实现场景错开播放的方式。interpolate()将帧范围映射到值范围。
你会频繁用到的帧数换算表:
| 时长 | 30fps 对应帧数 |
|---|---|
| 5秒 | 150 |
| 10秒 | 300 |
| 15秒 | 450 |
| 30秒 | 900 |
| 60秒 | 1800 |
一旦你内化了”输入帧编号,输出 CSS 值”这一概念,生成的代码就不再神秘难懂。
前置条件与安装
Discover how at OpenReplay.com.
你需要安装 Node.js(在安装前请查阅 Remotion 入门页面上的版本要求)以及 Anthropic 的终端编程代理 Claude Code。Claude Code 需要 Claude 订阅或 API 计费——由于套餐可用性会有变化,请在 Anthropic 的 Claude Code 文档中确认当前的访问要求。
现在来说说每个教程都一笔带过的部分。Remotion 技能的安装命令在近期的各类文章中出现过至少三种不同形式(npx @anthropic-ai/skills add remotion、npx skills add remotion、npx skills add remotion-dev/skills)。由于技能包的打包方式一直在调整,请不要直接复用他人的命令。请打开官方技能页面——remotion.dev/docs/ai/skills——并运行页面上当前列出的命令。在生态系统稳定之前,请将该页面视为唯一可信来源。
首先确认 Claude Code 已添加到你的 PATH:
claude --version
然后使用官方文档页面上的命令安装技能,并在项目中向 Claude 提问以确认安装成功:“你是否已加载 Remotion 技能?“
完整示例:制作一段 15 秒的功能展示短片
这套技术栈最适合的开发者使用场景,是那些需要根据数据或按计划定期重新生成的资产:基于 CHANGELOG.md 构建的更新日志短片、反映当前系统状态的动态架构图,或每次发布时重新渲染的产品功能展示视频。我们将以功能展示视频为例进行演示。
第一步——搭建项目脚手架
脚手架命令记录在 Remotion 入门页面:
npx create-video@latest feature-reveal
cd feature-reveal
npm install
使用 TypeScript 模板或配置创建项目。脚手架会生成 src/Root.tsx(用于注册合成)以及一个初始合成示例。
第二步——向 Claude Code 提示
在项目中打开代理,并明确指定帧数计算——这是大多数错误输出的根源:
Create a Remotion composition called FeatureReveal.
- 15 seconds at 30fps (450 frames), 1920x1080.
- Dark background (#0d1117).
- A headline "Ship changelogs as video" fades in over frames 0-30,
holds, then fades out over frames 420-450.
- Three feature rows below the headline, each sliding up from 40px
with a spring, staggered: row 1 enters at frame 60, row 2 at 90,
row 3 at 120.
- Register FeatureReveal in Root.tsx with the correct
durationInFrames and fps.
第三步——阅读生成的代码
正确生成的代码大致如下所示。标题使用 interpolate();各行使用带 <Sequence from> 偏移的 spring():
import {
AbsoluteFill,
interpolate,
spring,
Sequence,
useCurrentFrame,
useVideoConfig,
} from "remotion";
const features = ["One prompt", "Re-render per release", "Version-controlled"];
const FeatureRow: React.FC<{ label: string }> = ({ label }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
// spring() 返回 0→1 的值;我们将其映射到 translateY 和 opacity。
const enter = spring({ frame, fps, config: { damping: 14 } });
const translateY = interpolate(enter, [0, 1], [40, 0]);
return (
<div style={{ opacity: enter, transform: `translateY(${translateY}px)` }}>
{label}
</div>
);
};
export const FeatureReveal: React.FC = () => {
const frame = useCurrentFrame();
// 标题透明度:在第 30 帧前淡入,保持,从第 420 帧到第 450 帧淡出。
const headlineOpacity = interpolate(
frame,
[0, 30, 420, 450],
[0, 1, 1, 0],
{ extrapolateRight: "clamp" }
);
return (
<AbsoluteFill style={{ backgroundColor: "#0d1117", color: "white" }}>
<h1 style={{ opacity: headlineOpacity, fontSize: 72 }}>
Ship changelogs as video
</h1>
{features.map((label, i) => (
// 每行通过 `from` 属性比上一行延迟 30 帧入场。
<Sequence key={label} from={60 + i * 30}>
<FeatureRow label={label} />
</Sequence>
))}
</AbsoluteFill>
);
};
最值得牢记的是标题的 interpolate() 调用。它将帧编号映射到透明度值:第 0 帧时透明度为 0,到第 30 帧时变为 1,保持到第 420 帧,然后在第 450 帧时淡出至 0。帧数组和值数组的长度必须相同,而 { extrapolateRight: "clamp" } 用于防止值在最后一个节点之后继续延伸——该选项的名称和行为在 interpolate() 参考文档中有详细说明。
错开效果来自每个 <Sequence> 上的 from={60 + i * 30}。由于每一行都有自己独立的序列,其 useCurrentFrame() 会在各自的起始帧重置为 0,因此 spring() 从每行的入场时间点开始触发,而非从视频开头开始。
Claude 还必须在 Root.tsx 中注册该合成:
import { Composition } from "remotion";
import { FeatureReveal } from "./FeatureReveal";
export const RemotionRoot: React.FC = () => (
<Composition
id="FeatureReveal"
component={FeatureReveal}
durationInFrames={450}
fps={30}
width={1920}
height={1080}
/>
);
如果这里的 durationInFrames 和 fps 与组件中的帧编号不一致,时序就会出错。这是导致”视频时长不对”的最常见原因。
第四步——在 Studio 中预览
启动 Remotion Studio:
npm run dev
Studio 会在浏览器中打开(注意它打印的端口号——并不总是固定的)。选择 FeatureReveal,按下播放键,并拖动播放头。将播放头拖到特定帧是核心调试操作:预览会精确显示第 N 帧的渲染结果,让你能够将其与代码中的帧数计算进行对比。
如何诊断 Remotion 渲染结果异常?
应从 Studio 预览入手进行诊断,而不是依赖一份通用错误清单。将播放头拖到问题出现的帧,然后阅读控制该帧范围的代码。以下三种失败模式涵盖了大多数情况。
所有动画同时触发。 当预览中所有动画同时播放时,原因几乎总是 <Sequence> 组件缺少 from 偏移值:每个场景默认从第 0 帧开始,除非你设置了 from={startFrame}。将播放头拖到第 0 帧——如果三行内容都已开始运动,说明序列没有错开。修复方法是这样提示:“将每个功能行分别包裹在独立的 <Sequence> 中,并将 from 分别设置为 60、90 和 120。”
视频时长不正确。 Claude 根据你指定的 fps 来计算帧编号。如果你没有指定 fps,它会假设一个默认值,可能与合成配置中的设置不一致,从而导致计算错误。将播放头拖到末尾:如果你的 450 帧动画在第 300 帧就结束了,说明合成的 durationInFrames 被设置成了 300。重新提示时请给出精确数字——“15 秒在 30fps 下是 450 帧;请将 durationInFrames 设置为 {450}”——而不是模糊地说”让它更长一点”。
动画感觉机械生硬。 没有缓动的线性 interpolate() 看起来很死板。拖动播放头观察入场动画:如果它以匀速运动,说明没有缓动效果。可以换用 spring() 来实现有机自然的运动(它返回一个 0→1 的值,你可以将其映射到变换属性上),或者按照 interpolate 文档向 interpolate() 传入 easing 选项。提示方式:“将行入场动画从线性插值改为使用 spring()。”
落地页上嵌入的演示视频的会话回放有时会暴露另一种独立的问题——自动播放大体积 MP4 阻塞页面渲染——但这属于交付层面的问题,与渲染本身无关。
渲染为 MP4
使用 npx remotion render 从命令行进行渲染,传入合成 ID 和输出路径:
npx remotion render FeatureReveal out/feature-reveal.mp4
这里有一个现实情况,是那些简短的提示词所忽略的。Remotion 通过启动无头 Chromium、对每一帧进行截图,并将帧通过 ffmpeg 合成来完成渲染——其架构在渲染文档中有详细说明。一段 30fps 的 30 秒合成视频意味着需要 900 张浏览器截图。这正是渲染时间随帧数和每帧复杂度线性增长的原因,也是为什么一段较长或视觉复杂的合成视频在笔记本电脑上可能需要相当长的实际时间,而不是”一分钟内完成”。
对于生产流水线——长视频、多变体、不能占用构建代理的 CI 环境——Remotion 官方推荐的云端方案是 Remotion Lambda,它将帧分散到多个并行 Lambda 函数中处理。一旦本地渲染时间成为瓶颈,或者你需要在没有开发者机器参与的情况下按计划渲染,Lambda 的配置成本就物有所值了。对于一次性的 15 秒短片,在本地渲染即可,无需引入 Lambda。
Remotion 对个人和小型公司免费;较大规模的公司需要购买许可证——在商业发布前,请在 Remotion 许可证页面上确认当前条款。
什么时候不应该使用 Remotion?
当视频内容是数据驱动的、可重复生成的,或者必须精确匹配设计系统时,选择 Remotion——比如更新日志短片、动态图表、每次发布时生成的功能展示视频。当内容是真实拍摄的、追求照片级真实感,或者是一次性创意作品(重新渲染的迭代成本超过代码化控制的收益)时,选择传统编辑器或 AI 视频生成工具。剪辑真人出镜录像中的 NG 片段、对素材进行调色,或制作有机自然的照片级动态效果,这些都是 Remotion 力不从心的场景。判断标准在于:输出内容是否能从”成为一个程序”中受益——如果你只渲染一次、此后再也不会修改,代码化只会增加负担,而非带来优势。
这套技术栈真正的回报在于第二次渲染,而非第一次。一旦合成存在,用新数据重新生成它——新的更新日志、新的指标、新的版本发布——只需一条命令。从官方文档页面安装技能,按照上面的示例构建功能展示视频,然后将同一个合成指向你实际需要发布的数据。
常见问题
interpolate() 默认以线性方式将帧范围映射到值范围,因此你可以精确控制起始和结束帧,例如在第 0 帧到第 30 帧之间将透明度从 0 淡入到 1。spring() 返回一个基于物理的 0 到 1 的值,它会自然地缓入并稳定下来,你可以将其映射到变换属性或透明度上。需要精确时序和保持效果时使用 interpolate();需要感觉不那么机械的有机运动时使用 spring()。
视频时长由 Root.tsx 中 Composition 的 durationInFrames 控制,而非由组件内部的动画值决定。如果 durationInFrames 为 300,但你的动画运行到第 450 帧,渲染会在第 300 帧停止并截断其余部分。fps 值也必须与你使用的计算相匹配:在 30fps 下,15 秒等于 450 帧。请明确设置这两个值,确保组件中的帧数计算与合成配置保持一致。
你可以用普通英文描述视频内容,让 Claude Code 生成 React 代码,但当渲染结果偏离预期时,你仍然需要阅读输出代码来进行修正。该技能让 Claude 掌握 Remotion 的 API,使其能够生成正确的帧数计算,但在调试时,它并不能消除理解合成、序列和帧编号的必要性。掌握“输入帧编号,输出 CSS 值”的思维模型,才能让你从 Studio 预览中诊断问题。
一旦本地渲染时间成为瓶颈,或者你需要在没有开发者机器参与的情况下按计划渲染,Remotion Lambda 的配置成本就物有所值了。由于 Remotion 通过无头 Chromium 对每一帧进行截图,渲染时间随帧数和每帧复杂度线性增长,因此长视频或大量变体会占用笔记本电脑或构建代理的大量资源。Lambda 将帧分散到多个并行函数中处理。对于一次性的短片,在本地渲染即可,无需引入 Lambda。
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.