Communitygithub.com

kosta4a/stopmotion-video

用于制作逐帧定格动画风格视频的规则、Remotion 辅助工具和音频脚本,默认使用 Remotion 实现。

stopmotion-video 是什么?

该技能旨在帮助开发者和 AI 代理创建类似定格动画风格、并与音乐同步的低帧率视频。它默认推荐使用 Remotion 框架,并提供了从测量歌曲节奏、设置时钟、编排页面到放置点击音效和间距控制的一整套规则和辅助脚本。视频的每个姿势(pose)对应一个八分音符,页面切换发生在小节线处,点击音效则精准落在鼓点上。技能文档详细描述了如何通过保持低帧率来保留音乐的节拍感。它支持 Remotion(用于渲染视频)和浏览器实时播放两种模式,并包含了用于测量同步、处理音频和跳过节拍的策略。该技能源于为169.825 BPM的鼓打贝斯音乐制作47秒启动影片的实战经验,其规则经过数百轮迭代打磨。

兼容平台~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/kosta4a/stopmotion-video/tree/main/skills/stopmotion-video

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

定格动画视频

使用本技能制作的视频运动方式如同随歌曲逐帧拍摄的定格动画。每个姿势保持一个八分音符的长度,然后所有元素一起变化。页面在小节线处进入。每次点击音效都与鼓点同步,而重大变化则发生在重击鼓声上。帧率人为保持较低水平。当某个动作看起来粗糙时,应更好地间距其姿势;绝不会为了平滑而抹去节拍。

这些规则源于为一个169.825 BPM的鼓打贝斯音轨制作47秒启动影片,并经过数百轮笔记迭代。影片以30fps运行,每个姿势保持一个八分音符,即每秒5.66个姿势,每个姿势占用5或6帧。这里的数值是影片的实际值,而在 live.md 文件中则是播放该影片场景并配合节拍器的网页的数值。当规则可以泛化到这些具体数值之外时,文本中会说明。

词汇表

  • 姿势 (Pose):一个静止的画面,对应歌曲的一个八分音符。在代码中称为 beat (STEP_MS, useBeat)。
  • 歌曲节拍 (Song Beat):BPM 计数的单位,即一个四分音符。它相当于两个姿势的长度,并对应一次节拍器点击。
  • 小节 (Bar):四个歌曲节拍,八个姿势。在鼓打贝斯音乐中,重击鼓(kick)落在姿势 0 上,军鼓(snare)落在姿势 2 和 6 上(从 0 开始计数)。
  • 敲击 (Hit):歌曲中的一种鼓声。一个重击鼓 (Big Kick) 是开启一个乐句的鼓声(在影片的音轨中,每个奇数小节开始处)。
  • 创作毫秒 (Authored ms):代码中的时间。它们基于一个 200ms 的姿势编写,并以 SPEED 倍的速度播放(SPEED = TEMPO / 150)。
  • 时间戳 (Timestamps):以 m:ss.ff 格式编写,其中 ff 是帧计数。这是 Remotion Studio 显示时间的方式,也是笔记到达的格式。

操作姿态

  • 有意保持低帧率。 永远不要为了修复某个动作而抹去节拍。应改改善该动作姿势的间距。
  • 所有元素保持一个姿势一拍并同时变化,包括转场。没有任何东西运行在自己独立的时钟上:嵌入的动画或滚动页面也必须跟随姿势的节拍。打字是唯一的例外:它比节拍运行得更快,正如打字本身那样,但从其对应的姿势开始。
  • 歌曲是时钟。 在绘制故事板之前先测量歌曲。将页面放在歌曲的小节线上,并将动作放在歌曲的敲击点上。
  • 整个视频共用一个时钟。 在视频的帧上计数姿势,绝不在某个场景内部计数。
  • 按整个小节移动。 通过整个小节来改变页面长度,或者在下一个小节线之前将多出的姿势退回到别处。否则,此后的所有内容都会与歌曲错位。
  • 没有空闲的姿势。 一旦某个页面上发生的事情完成,只需保持足够长的时间让观众理解即可。
  • 变化种类少,方向单一,无淡入淡出。
  • 通过测量来检查。 使用每个姿势一张的静帧、在渲染音频中测量的时间,以及一个节拍器剪辑。不要仅凭肉眼判断同步。

工具:默认使用 Remotion

建议使用 Remotion 来制作视频,除非项目已经以其他方式制作视频。在开始故事板之前说明这一点,并给出理由,只有在用户要求使用其他工具时才更改。如果是在网页上,则改为实时播放(live.md);在动态图形工具中,规则仍然适用,但辅助工具不再适用。

  • 为什么它很合适。 每一帧都是一个根据帧号计算得出的 React 组件,因此一个姿势会精确地落在它该落的帧上,并且每次渲染结果相同。渲染时帧是乱序绘制,横跨多个标签页,这只有由帧驱动的视频才能胜任。assets/remotion/ 中的辅助工具是为它编写的,其 Studio 以 m:ss.ff 格式显示时间,这正是笔记到达的格式。
  • 设置它。 使用 npx create-video@latest 命令和空白模板。将 remotion 及所有 @remotion/* 包锁定到一个精确版本(影片使用的是 4.0.532),并将 TypeScript 保持在 5.x 版本(clock.md §7)。一个合成,1920×1080 分辨率,30fps。将 beat.ts 和 transitions.tsx 复制到 src/ 目录,并设置 TEMPO。
  • 配置它。 在 remotion.config.ts 中:JPEG 帧质量设为 95(默认值 80 会使纯色背景和细线边缘变得粗糙),BT.709 色彩空间(以便播放器显示的颜色与浏览器一致),并启用覆盖写入。
  • 预览和渲染。 使用 npx remotion studio 进行工作;使用 npx remotion render <Comp> out/<name>.mp4 渲染 1080p 视频,添加 --scale=2 参数可渲染 4K;使用 --frames=<first>-<last> 或 npx remotion still <Comp> --frame=<n> 来检查某一段或某个姿势。

决策顺序

从上到下进行。每一步都为下一步设定约束。

  1. 建议工具。 使用 Remotion,除非项目已有其他制作视频的方式;在进行任何其他操作之前,按上述方式设置它。
  2. 测量歌曲。 通过歌曲的起始点找到节奏,并检查这个节奏网格在你使用的片段内是否稳定。标记小节和乐句,标记鼓声消失和回来的地方,以及重击鼓的位置。将音乐片段精确裁剪到音频样本的开头,在乐句第一个重击鼓前约 23ms 处。参见 song.md。
  3. 设置时钟。 一个姿势是歌曲的一个八分音符。基于一个 200ms 的姿势进行创作,使用 SPEED = TEMPO / 150,以 30fps 渲染,并基于整个视频的帧来计数姿势。参见 clock.md。
  4. 将页面放置在小节上。 以姿势为单位编写故事板。每个页面包含整数个姿势,并从小节线开始。在制作任何动画之前,将歌曲的事件映射到姿势上。
  5. 放置敲击音效。 代码以 2 到 4 个字符为一组的方式在八分音符上输入,标题和按住退格键的操作从其对应的姿势开始,比节拍运行得更快。点击和爆裂声放在军鼓上,着陆和新页面放在强拍上,重大变化放在重击鼓上。在音乐安静的部分进行构建,在鼓声回归时进行着陆。参见 hits.md。
  6. 间距姿势。 为每个动作曲线选择其姿势。一个动作的第一个姿势就是它的第一步,每个姿势都使其可见地移动。事物应该“着陆”而不是“爬行”。参见 poses.md。
  7. 切换页面。 使用节拍上的硬切、地面的切出、像纸张一样覆盖的页面、摄影机进入一种颜色、从内部填充、穿过形状的开口或像素块。绝不要使用淡入淡出。参见 hits.md。
  8. 设置字体。 随着字号增大,字间距收紧,行距也随之收紧。在每个姿势下,每个单词与其背景的对比度都必须达到 4.5:1 以上。标题是“打”出来的,而非淡入的。参见 type.md。
  9. 制作声音。 每个点击音效都精确放置在歌曲自身的敲击点上,并与音频样本对齐。所有点击音效都合并到一个与歌曲同时开始的音轨中。在点击音效下方将音乐音量降低,并保留一个节拍器剪辑用于检查。参见 sound.md。
  10. 决定何时跳过一拍。 降音、闪避、保持、吸附、剪切整个小节、在歌曲节拍上结束。参见 skipping.md。
  11. 检查。 制作一个联系表,测量同步情况,并观看节拍器剪辑。以映射到姿势的时间戳形式做笔记。参见 checking.md。
  12. 在浏览器中实时播放? 遵循相同的规则,配合节拍器:页面上使用一个时钟,画面比声音提前 25ms,声音基于音频时钟提前调度,在没有歌曲时,每次变化都要对应一个点击。参见 live.md。

规则一览

时钟

项目规则
帧率30fps,有意为之。一个平滑的 60fps 变体可以共享故事板 (clock.md §8)
姿势歌曲的八分音符:在 169.825 BPM 下为 176.65ms,每秒 5.66 个,每个占 30fps 下的 5 或 6 帧
创作所有时间以 ms 为单位,基于一个 200ms 的姿势;SPEED = TEMPO / 150;ms() 转换为帧,seconds() 用于嵌入式时钟
姿势 n 的第一帧ceil(n × frames per pose),基于整个视频的帧(beatFrame)
页面整数个姿势,从小节线开始(姿势序号能被 8 整除)
更改长度通过整个小节进行,或者在下一个小节线之前将多出的姿势退回去
Studio 拖动拖动的轨道会写入一个 from 偏移量,导致画面与声音错位:请移除它

间距

动作曲线姿势分布
进入和退出(摄影机运动、掠过、下划线)easeInOutSine6 个姿势上分布为 7, 18, 25, 25, 18, 7%
从画面外进入缓出曲线否则其缓慢的起始部分会浪费在屏幕外
一个页面覆盖在另一个上面easeOutSine3 个姿势的转场上分布为 38, 70, 92%
缩放、线条变粗、开口变大每个姿势按相同因子增长缩放时其目标沿直线移动到中心
不是用于少量姿势三次缓入缓出(3, 12, 35, 35, 12, 3:中间跳跃);强缓出(作为三姿势转场,在其第一个姿势着陆:78, 97, 100%;作为使用 moved() 的动作:88, 99, 100%)

节拍上的事物

事物规则
点击按下一个姿势,在重击鼓或军鼓上;光标在最后一次点击后的下一个姿势消失
光标打字时实心,然后两个姿势亮,两个姿势灭
旋转器最多每个姿势转四分之一圈;超过半圈看起来像是倒转
滚动标记按行程/半径的角度转动,最终直立,使用 easeOutSine 曲线
计数器每个姿势变化一个数字,使用强缓出曲线,使用表格数字,整十数字保持两个姿势
标题从自身姿势开始,比节拍更快地“打”出来:每三个帧一个单词;长行每 25ms(创作时间)一个字母
代码每个姿势输入不均匀的 2 到 4 个字符块
按住退格一次运行,每 25ms(创作时间)一个字母,包括换行符
摄影机升起升起,在顶部静止两个姿势,然后落下
从画面外进入在移动之前不被绘制
手动放置最多偏移该姿势移动量的十分之一,一旦停止则完全归位;在几像素内落下

敲击 (Hits)

在歌曲中在画面中
八分音符输入代码的块,动作的步骤
军鼓点击、爆裂声
强拍着陆、新页面、页面的第一个动作
重击鼓重大变化:页面着陆、最后一个完整页面、最终点击
鼓声消失构建
鼓声回归着陆

点击必须落在节拍上。转场可以偏离节拍。

声音

项目规则
点击放置精确对齐音频样本,放在歌曲的敲击点上(影片的敲击点落在其姿势后 25ms)
点击文件在滴答声之前有 40ms 的静音,这样修剪到样本时永远不会吃掉它
点击轨道所有点击合并到一个与歌曲同时开始的轨道中
闪避在重要的点击周围保持静音;在其他点击下方 160ms 内降低到 45%;使用 10ms 淡入淡出
节拍器每个歌曲节拍在其敲击点上点击一次,小节的第一个点击音调更高,音乐音量在 20%;绝不包含在导出中

字体

字号字间距行距
小于 20px0;等宽大写 +2%1.4–1.5
20px 及以上−1%约 1.4
32px 及以上−2%约 1.25
56px 及以上−3%1.1–1.2;两行展示标题可降至 0.9

每个单词在每个姿势下与其背景的对比度必须至少达到 4.5:1。默认使用一种字体家族(示例中使用 Geist 和 Geist Mono),黑底白字,如触觉设计所做;影片本身将显示字体和正文字体配对,并将其页面放置在纯色背景上。计数器使用表格数字。标题是“打”出来的,绝不淡入。

实时播放(浏览器中)

项目规则
时钟beat = anchorBeat + elapsed s × BPM / 60 × 2 基于 performance.now();在速度变化时重新锚定到当前时间;每个场景都是节拍(beat)的纯函数
画面提前 25ms 读取节拍;每个场景在动画帧内提交(flushSync)
声音每 25ms 一个定时器,调度音频时钟上接下来的 120ms(getOutputTimestamp());窗口以音频时间测量,加上输出延迟
在视图中只有屏幕上的内容才进行动画;示例中大部分在屏幕上的内容都能被听到
启动加载时尝试,然后在首次按下或按键时启动;如果按键启动了声音,则不会静音该声音(600ms);记住静音状态
无歌曲每次变化都对应一个点击:动作可以在八分音符上迈步,但必须在点击上开始和着陆;去掉该点击本身的节拍器点击
节拍器一个轻柔的木制敲击声(880Hz,持续 50ms,小节第一个为 1320Hz),没有音乐时不发出重击声,全部通过一个限制器
暂停在最近的点击上暂停:继续播放到该点击,或者在 160ms 内回退
减少动画一个展示想法的静态姿势,只在按下声音按钮后移动
字体单词对比度 4.5:1,有意义标记 3:1,缩放场景的最小单词渲染后为 9px 或更大

何时跳过一拍

网格是严格的,因此打破它是一种决策。以下每种打破方式在 skipping.md 中都有其原因。

  • 在重要的点击周围降低音乐音量。 音乐从点击前 50ms 到下一个小节线处变为静音,带有 10ms 淡入淡出。点击声被单独听到,节拍随着后续内容回归。
  • 在其他点击下方闪避。 将音乐音量降低到 45%,持续 160ms,以便点击的重击声有空间。
  • 单独保持一个结果 一个歌曲节拍,然后才开始下一件事,例如复制后的一个勾选标记。
  • 在一个升起动作的顶部保持两个相同的姿势。
  • 在整十数字上保持两个姿势,然后再继续。
  • 将一个落在节拍之间的音符吸附 到最近的加重节拍上。
  • 剪切整个小节,即使笔记要求更少。说明这样做的代价。
  • 在歌曲节拍上结束。 最后一页保持大约 2 秒钟,音乐在最后一个姿势上淡出。否则在动作结束后立即结束。

绝不能发布的内容

  • 为了隐藏间距不良的动作而平滑掉节拍的运动
  • 从自身第一帧开始计数姿势的场景
  • 没有在旁边注明原因就开始于非小节线的页面
  • 没有按整个小节更改,也没有在下一个小节线前退回的长度更改
  • 合成树中残留的 Studio from 偏移量
  • 落在非重击鼓或非军鼓上的点击,或者将点击声音放置在帧上而非对齐音频样本
  • 在其自身帧上开始短促点击声音,而不是使用一个合并的轨道
  • 其第一个姿势就是它离开的姿势的动作,或者在最后几像素“爬行”的动作
  • 在移动之前就已经被绘制出来的画面外元素
  • 每个姿势转动超过半圈的旋转器或轮子
  • 淡入淡出,或在饱和背景之间的交叉淡入淡出
  • 背景变化快于每秒三次闪烁
  • 在任何姿势下,单词与其背景对比度低于 4.5:1
  • …

[已截断]

相关技能