分享一个做视频的skill,这条白板视频,每一笔都是代码画的
我对 AI 说了一句话:「做一期白板视频:为什么定了计划总是坚持不下去。」
Spots 我对 AI 说了一句话:「做一期白板视频:为什么定了计划总是坚持不下去。」

一条 1920×1080 的成片 ,131.9 秒,7 个场景,74 条字幕,配了音、烧了字幕、压了配乐;横竖两张封面,4:3 和 3:4,同一个函数一次出;一份发布稿,五个候选标题挑一个主用,视频号简介、小红书、B 站、公众号摘要、推特各写一版;一份资料来源,哪个数字来自哪篇论文、哪句是估算,逐条列清。全程没有打开过剪辑软件。画面上每一笔,都是代码画的。
,131.9 秒,7 个场景,74 条字幕,配了音、烧了字幕、压了配乐;横竖两张封面,4:3 和 3:4,同一个函数一次出;一份发布稿,五个候选标题挑一个主用,视频号简介、小红书、B 站、公众号摘要、推特各写一版;一份资料来源,哪个数字来自哪篇论文、哪句是估算,逐条列清。全程没有打开过剪辑软件。画面上每一笔,都是代码画的。 这个技能叫 whiteboard-video,开源在 github.com/trustfuture/simon-skills 的 skills/whiteboard-video 目录下,MIT 协议。 成片前 20 秒,未加速、未抽帧。线条是一笔一笔长出来的,人物贴纸是"擦"出来的,字幕跟着人声走。 第 20–40 秒。第一个场景画满之后,镜头切到第二个场景,三圈模型从外圈往内圈逐笔成形。 这就是这个技能要解决的全部问题:让一个不会画画、也不想开剪辑软件的人,把"讲清一件事"变成一条能发的视频。 一、先说结论:它不是"生成视频",是一条可拆的流水线 现在讲"AI 做视频"的产品不少,但绝大多数是黑盒——你给它提示词,它给你一段画面,中间发生了什么你不知道,想改一个细节只能重新抽卡。 whiteboard-video 是反过来的:它是一条十步的流水线,每一步都能单独跑、单独看、单独改。
| 环节 | 用什么 | 备注 | | --- | --- | --- | | 手绘线条 | rough.js(Excalidraw 的底层库) | 场景同时导出 Excalidraw 格式,装 Obsidian 插件能直接打开改 | | 逐笔动画 | Playwright 在 Chromium 里逐帧截图 | 4 路并行,2.5 分钟的片子渲染约 35 秒 | | 人物与道具贴纸 | 本地 codex CLI 生图 | 一次 2×2 四宫格同风格,自动抠白底、去杂点 | | 公司 / 产品 Logo | Wikimedia Commons 官方 SVG | 出处自动记录 | | 配音与字幕 | 火山引擎语音合成 | 原生 1.2 倍语速,返回逐字时间戳直接对字幕 | | 合成 | ffmpeg | 拼帧、烧字幕、说话时自动压低配乐 | 绿框那三步决定"画面长什么样",其余七步是纪律性工序。整条链跑完约 40 秒(TTS 命中缓存时),渲染整期只跑 35 秒——比大多数人的导出时间短一个量级。 关键差别在于:这里的每一步都是一个可以单独调用的命令。 stills 这一步是整个技能最"反 AI"的设计:它不让你一路跑到底,而是逼你在渲染前先用静态图确认每一屏的排版。 排版错了,改的是代码,不是抽卡。 官方给每期的交付定义是"四件套":成片 + 两张封面 + 发布稿 + 资料来源。后两件经常被忽略,但恰恰是对做内容的人最值钱的部分。
上面这张是横版(4:3)与竖版(3:4)两张封面,由同一个 cover 函数一次生成,不是做两遍。左图那行"道理都懂 / 为什么没变化?"是脚本里的两行字符串,绿色高亮和马克笔底色是自动加的;右下角的 "Your Brand" 是占位品牌,改 config.json 一处就换成你自己的。
发布稿那一份更实用——它按平台拆好了:视频号简介、小红书标题+正文+标签、B 站标题、公众号摘要、置顶评论,另外给五个候选标题标好类型(数字反差 / 结论前置 / 生活单位换算 / 提问)。这是把一个内容团队里"运营岗"的活,固化进了模板。 整条链上最反直觉的一条规矩,写在 SKILL.md 第 3 条: 先旁白后画面。 旁白按 | 切 beat,每 beat 34 句配一组元素;68 个场景,成片 2~2.5 分钟。 旁白按 | 切 beat,每 beat 34 句配一组元素;68 个场景,成片 2~2.5 分钟。 每个 beat 都要有能画出来的东西,抽象句并入相邻 beat。 每个 beat 都要有能画出来的东西,抽象句并入相邻 beat。 这句话看着像写作建议,其实是给内容做减法的手术刀。它意味着:任何一句"没有画面可画"的旁白,不允许只当文字存在——要么给它配个画面,要么把它并进旁边那句。 结果就是这类视频里不太会出现"综上所述,我们可以看出……"这种句子,因为它画不出来。内容是倒逼出来的,不是先写文案再找配图。 旁白和场景写在同一个文件里,scenes.js 是这一期唯一的手写源:
看第四行那个 |——它就是 beat 的分界。坐标是写死的(zones(s, 640, 560, 520) 是三个同心圈的圆心和半径),贴纸按名字引用(s.image(1560, 400, 'stuck'))。一行代码一个元素,不需要任何可视化编辑器。 这是全篇技术含量最高的一段,也是我认为这个技能真正不可替代的地方。 普通做法是"把一张画好的图加上手绘滤镜",看起来像白板,但没有过程。而这个技能要的是"边画边讲"——线条必须按人手的顺序长出来。 先看这一段单独的逐笔动图(11 秒,同样未加速): 一个场景从空板到画满的全过程。左上角那支黄色铅笔会跟着当前正在描的路径移动——这个细节是渲染器算出来的,不是贴上去的装饰。 上面是同一场景在 0.6 秒、3.2 秒、6.4 秒、9.6 秒的四个状态:空白 → 主视觉逐笔描出 → 圈线和标签 → 卡片、箭头落位。左上角那支铅笔会跟着当前正在描的路径移动。 要做到这个效果,渲染器里有三件事必须做对,而这三件事都是踩出来才知道的: 第一,按子路径顺序描边。 rough.js 画出来的每条线是 toPaths 的结果,而一条"线"内部往往由多个子路径组成。描边顺序错了,就会出现这一笔还没画完、下一笔已经冒出来的错位。 第二,双描边拆 A/B 层。 Excalidraw 风格的手绘线是"双描边"效果(看起来像用笔描了两遍)。这两遍不能同时动画,否则会像两条线在赛跑。 第三,也是最硬的一条坑,SKILL.md「已知坑」里原话是: SVG dash 在每个子路径 M 处重起,rough.js 又双描边:整条 path 一起 dashoffset 会"所有边同时长、每边描两遍"。渲染器已按子路径拆节点、双描边拆 A/B 层,别回退。 SVG dash 在每个子路径 M 处重起,rough.js 又双描边:整条 path 一起 dashoffset 会"所有边同时长、每边描两遍"。渲染器已按子路径拆节点、双描边拆 A/B 层,别回退。
翻译一下:如果你偷懒,把整条路径当成一个对象做"描边进度"动画,你会得到一屏幕同时伸长、每边描两遍的线。要解决它,必须把路径按 M 命令拆成节点逐个控制。 并行出帧靠两点,别动:render.html 的 seek()每帧把底色矩形原地重插,强制整屏重画(否则 Chromium 只重画变化区域,帧会跟出帧顺序有关)。 并行出帧靠两点,别动:render.html 的 seek()每帧把底色矩形原地重插,强制整屏重画(否则 Chromium 只重画变化区域,帧会跟出帧顺序有关)。 浏览器为了省算力只重绘变化区域,这在交互场景里是优点;但在"逐帧截图"的场景里,它会让每一帧的内容取决于你截图的顺序——并行之后,帧序就乱了,视频会闪。 解法是每帧强制整屏重画。 这几条加起来,解释了为什么这个技能能把"2.5 分钟的片子"压进"35 秒渲染":4 路并行,每路一个独立 Chromium,再配上逐帧一致性校验(ffmpeg -f framemd5 对比必须 0 帧不同)。 这些不是"用 AI 生成视频",这是把渲染工程做扎实。 绝大多数自动配音加字幕的流程是这样的:先算这句話大概几秒,再按字数把字幕平均切进去。结果是字幕永远比声音慢半拍或快半拍。 这个技能绕开了这个猜测环节——火山引擎的语音合成会返回逐字时间戳,字幕直接用真实时间戳排。
| 环节 | 具体做法 | | --- | --- | | 切句 | lib/captions.cjs 按 6~20 字切成短句,不按标点硬切 | | 烧录 | 底部字幕区(y≥960)烧进画面 | | 导出 | 同时出一份 字幕.srt | | 校验 | tts-volc.mjs 按逐字数校验,半段音频自动重试 | 字幕文本必须用原文。 TTS 会把 @grok 念成 "atgrok"——所以字幕不能跟着 TTS 的发音走,得用你写的那份原文。 TTS 会把 @grok 念成 "atgrok"——所以字幕不能跟着 TTS 的发音走,得用你写的那份原文。 改旁白必须重跑 TTS。 时间戳来自配音,旁白改了不重跑,字幕就会全体错位。 时间戳来自配音,旁白改了不重跑,字幕就会全体错位。 缓存键包含文本 + 音色 + 语速。 换语速会触发全部重配——所以"把语速从 1.2 调到 1.15"不是免费操作。 换语速会触发全部重配——所以"把语速从 1.2 调到 1.15"不是免费操作。 这是第 2 场景完全画完的样子:三圈模型、三行说明、右下角卡片、左下角人物贴纸全部到位,字幕压在最底部。注意最下方那条字幕带——那片区域是禁区,下一节讲。 这是我认为这个技能最值得抄的一点。它没有写一本"内容规范手册",而是把规矩变成了会报错的代码。 元素压进去就报警告,而 wb scenes 输出的警告必须清零才算过关。这比"请注意不要遮挡字幕"这种文档约定有效一百倍。 右上角的手写水印(第一个场景里逐笔画入)、片尾品牌卡(5.5 秒、静音)、封面上的品牌标——这三个都由工具自动生成,不用每期写。名字、品牌色、slogan 在 config.json 的 brand 字段里。 换账号的成本是:改 brand.name(水印与片尾的手写名)、brand.accent(品牌色)、brand.slogan 三行。 期目录就是工程目录,scenes.js 是唯一手写源。scenes/、旁白稿.md、字幕.srt、封面-*.png 全是生成物——不手改、不外拷。只有 README.md(资料来源)和
发布.md(文案)是人写的。 这条规矩解决的是所有自动化流程最经典的死法:改了一次生成物,下次重跑就覆盖,然后没人知道哪份是真的。 数字、日期、价格必须有来源,出处写进期目录的 README.md;估算值在旁白和文案里都要标"据报道 / 估算"。而且旁白、字幕、封面、发布文案里的数字,从同一处取。 公司、产品、模型用真实 Logo;人物、器械、物件用贴纸。 公司、产品、模型用真实 Logo;人物、器械、物件用贴纸。 理由也很直白:AI 画的拟人机器人和通用小人,观众认不出是谁。 所以讲到具体公司时主角用 Wikimedia Commons 上的官方 SVG(wb logo 去取),讲到公众人物时用真人照片参考生成的漫画像。
