Product film 是一个用于在代码中制作产品影片的技能(skill),基于 Remotion 实现,适用于落地页循环动画、发布视频、宣传片、演示 reel、社交媒体剪辑等。它的目标是做出「看起来就像产品自己做出来的」影片:使用产品自身的颜色、字体、组件、logo 与语声,并剪辑到音乐上。影片在产品的代码库内部(或旁边)用 Remotion 构建,因此可以复用真实组件并保持可编辑。
【核心原则】
- 产品的设计优先:每条规则都来自产品自身,包括其设计 token、组件、规则文件、落地页与文案。绝不把其他产品的品味带进来,包括本技能中任何与其设计相矛盾的内容。
- 询问而非假设:影片展示什么以及如何制作由用户决定。在写故事前先访谈用户(见 reference/interview.md)。所提供的选项应来自在用户代码中发现的内容,而非通用选项。
- 每一帧都是时间的纯函数:不使用 CSS transition 或 keyframes,不使用计时器,不使用 Date.now(),帧与帧之间不携带状态。自带时钟的组件需要有一个由帧驱动的孪生版本。
- 测量而非猜测:节拍来自音频,位置来自 DOM(调试叠加层),颜色来自最终文件解码后的像素。
- 诚实的宣称:只展示产品真正能做的事。在写下任何文字前,先找到其宣称规则与已批准的文案。
- 提交、推送或发布前先征求同意。保留每一个渲染版本(out/<film>/v1、v2……)。
【工作流】
- 快速发现:只收集足够提出好问题的信息——规则文件、token、组件、logo(以及吉祥物或动画标识)、落地页、主要功能。见 reference/discovery.md。
- 访谈:进行两轮 AskUserQuestion——先是 brief,再是 ingredients。必要时进行第三轮。见 reference/interview.md。
- 品牌套件 → videos/BRAND.md:根据用户的选择完成发现,并填充 templates/BRAND.md。
- 故事 → videos/<film>-prompt.md:阅读 reference/story.md 与 reference/ingredients.md,然后填充 templates/film-prompt.md。与用户确认检查点:beat sheet 与 3 张风格帧。
- 音乐:若影片无声则跳过。见 reference/music.md:scripts/beats.py、per-bar stem map、scripts/audio-edit.py、在测量出的峰值上放置 SFX。
- 引擎与场景:阅读 reference/engine.md。将 templates/kit/ 复制到 videos/src/kit/。每部影片一个文件夹:cues.ts(作为数据的 beat sheet)、layout.ts、acts/、composition。
- 评审循环:见 reference/review.md。每次交接都产出静帧(scripts/stills.ts,--debug 用于测量)。产出 contact 与 handoff sheet,然后是一个半分辨率草稿。修复、重复,并随时向用户展示帧。
- 最终渲染、验证、交付:见 reference/render.md:scripts/render.ts,然后 scripts/verify.py,然后发送文件。
【质量下限(无论素材如何,始终适用)】
- 只使用产品自身的表面、颜色、边框和明暗。绝不发明其未使用的卡片背景、轮廓或色调。
- 在交付尺寸下可读。更少的字胜过更小的字。删掉只是重复画面的标签。
- 文字绝不被光标、chip 或纹理遮挡。移动时绝不穿过其他文字。一行在构建过程中绝不重新居中:保持每个词的位置。
- 加载状态保持其宽度。使用产品自身的加载模式。
- 每个节拍都要有事情发生。没有任何移动的 bar 会读起来很慢。
- 场景边界落在 bar 上。循环的最后一帧等于第一帧。落地页循环必须在静音下也读得通。
- 不使用产品语言中没有的效果:glow、粒子、点击涟漪、有弹性的缓动、shader。
【会耗费大量时间的陷阱】
- Remotion 静帧不会转发 console 日志。把测量值打印到帧里(templates/kit/debug.tsx)。
- npx remotion still 每次调用都会重新打包。使用 scripts/stills.ts:打包一次,渲染多帧。
- Remotion 自带的 ffmpeg 没有 tmix、select 或 tile。使用完整的 ffmpeg(uv run --with imageio-ffmpeg)。
- 色彩范围:Remotion master 是 limited range、BT.601、未打标签。把它当作 full range 混合会把 #0a0a0a 提亮到 #171717,在深色页面上变成一个灰盒子。解码每个交付物的第 0 帧并检查数值。
- interpolateColors 无法解析 color-mix()。任何做动画的颜色都必须是 hex token。
- 异步图片组件(Radix 或 base-ui 的 avatar)可能在某一帧渲染为空。用 Remotion <Img> 做孪生版本。
- 会 retarget 的 spring:为每个 key 求和一个闭式步骤,key 按时间排序。
- CSS 虚线边框在盒子缩放时会爬动。用固定间距的 SVG stroke 来画虚线。
- WebGL 或 shader 效果自带时钟,每次运行会画出不同的画面。优先使用逐帧绘制的纹理。
【在 monorepo 中】
- 把每个 remotion 与 @remotion/* 固定到同一个精确版本
- 绝不让视频 workspace 重新解析应用的 Tailwind
- 在 webpack override 中为应用的路径导入设置 alias
只停止你启动的进程;其他会话可能正在等待这台机器。