手绘白板讲解视频
把一句话选题做成一条 2 分钟上下的讲解片(默认竖屏 1080×1920,可切横屏 1920×1080),画面上每一笔都是代码画的:标题一个字一个字写出来,铅笔跟着笔尖走,人物和道具是贴纸,公司用官方 Logo,底部是烧录字幕,右上角是品牌水印。
本技能自带完整的渲染与配音管线,不需要出镜、不需要剪辑软件、不需要联网生视频。全链路在本机跑:Playwright 逐帧截图 → ffmpeg 拼帧烧字幕 → 火山引擎语音合成。
什么时候用 / 不用
用:概念解释、机制拆解、「为什么……还是……」这类认知类选题;要新建一期、改场景、换贴纸或 Logo、重出片、改字幕、调语速、出封面、写发布文案。
不用:真人出镜口播、探店、剧情短剧、直播切片;也不要指望它自动发布——本技能只到成片和发布包为止,发不发、发到哪由用户决定。
先确认环境(缺一样都会中途失败)
| 依赖 | 说明 | 缺失表现 |
|---|---|---|
| Node.js ≥ 20.12 | 用到 process.loadEnvFile |
脚本直接报错 |
| ffmpeg / ffprobe | 拼帧、烧字幕、混音、量时长 | 渲染阶段 spawn ffmpeg ENOENT |
| 浏览器 | 逐笔渲染 | 默认 Playwright Chromium(postinstall 自动装);下不动就设 WB_BROWSER_CHANNEL=msedge 用本机 Edge |
| 字体(可选) | 手写笔迹。本包不含 ttf(平台禁二进制),需自行放 assets/fonts/Xiaolai-Regular.ttf |
不缺:回退系统字体,能出片但没手写感 |
| 火山引擎语音合成凭证 | 旁白;填在 <技能根>/.env |
配音阶段报「缺少火山凭证」 |
| codex CLI(可选) | 只用于出贴纸(wb image) |
不用贴纸可跳过 |
ffmpeg 不在 PATH 上时,用环境变量指过去:FFMPEG_BIN=D:\tools\ffmpeg\bin\ffmpeg.exe(ffprobe 默认取同目录)。
首次使用先跑:npm install(在 <技能根>/ 下)。装 Playwright 浏览器时如果系统盘紧张,先设 PLAYWRIGHT_BROWSERS_PATH 指到空间充足的盘。
命令
下文用 wb ... 指代 node <技能根>/scripts/wb.mjs ...。参数全在 <技能根>/config.json,改配置不改代码。
wb new "<标题>" # 建期目录:scenes.js 模板 + 发布.md 模板
wb list # 列出所有期
wb scenes <期> # 生成 Excalidraw 场景 + script.json + 旁白稿.md
wb stills <期> # 每个 beat 一张静帧,逐张看排版
wb image <期> name="英文描述" # 贴纸(一次 ≤4 张,走本地 codex)
wb logo <期> logo-x="Commons 文件名.svg" ... [--vs=a,b] [--search="词"]
wb tts <期> [scene] # 火山配音(改过的旁白才重配)
wb render <期> [scene] # 渲染 → <后台>/<期>/work/out/master.mp4
wb mix <期> [gain] # 混 BGM → <后台>/<期>/outputs/final.mp4
wb cover <期> # 按 config.cover.ratios 出封面(默认 封面-3x4.png + 封面-9x16.png)
wb build <期> # scenes → tts → render → mix → cover 一条龙,出片后自动 clean
wb clean <期> # 清静帧与已不在场景表的旧分段
wb open <期> # 打开期目录与后台目录
<期> 可以写目录名、标题或日期的任意子串(wb build 薄肌)。wb render <期> 03-xxx 只重渲一段并自动重拼 master,接 wb mix 就是新成片。
目录约定:每期内容在 config.json 的 dirs.projects(默认 <技能根>/episodes/<日期 标题>/),中间产物与成片在 dirs.build(默认 <技能根>/build/<日期 标题>/)。dirs.projects 可以指到 Obsidian 仓库里,期目录就能在 Obsidian 里直接看、直接改场景图。
硬规矩
- 一处为主。 期目录就是工程目录,
scenes.js是唯一手写源(旁白、场景、封面都在里面);scenes/、旁白稿.md、字幕.srt、封面-*.png是生成物,不手改、不外拷。README.md(资料来源)和发布.md(文案)由人写。 - 先旁白后画面。 旁白按
|切 beat,每 beat 3~4 句配一组元素;6~8 个场景,成片 2~2.5 分钟(1.2 倍语速约 500~600 字)。画不出来的句子并进相邻段。 - 画布由
config.json的canvas决定(当前竖屏 1080×1920;横屏 1920×1080)。字幕安全线是captions.safeTop(竖屏 1700 / 横屏 960),右上 320×130 是水印区,元素都不进去;wb scenes的 ⚠ 必须清零。改画布要同步改safeTop/baselineY/maxChars。 - 公司、产品、模型用真实 Logo;人物、器械、物件用贴纸;文字、箭头、框用 Excalidraw。 讲到具体公司时主角用官方 Logo(
wb logo),不用生图拟人机器人代指。 - 事实先查再写。 数字、日期、价格要有来源,写进期目录
README.md;估算值在旁白和文案里都标「据报道 / 估算」。查证动作见 @references/common/fact-check.md。 - 品牌层自动带,不用每期写。 右上角手写水印、片尾品牌卡、封面品牌标都由工具生成,名字与品牌色在
config.json的brand。 - 交付四件套:成片 + 两张封面 +
发布.md,主用标题和视频号简介直接贴在回复里。
一期的流程
1. 立题与查证
明确选题、观众、口吻(默认冷静科普口吻)。搜 2~3 轮,官方页优先且读到全文,二手站数字只做线索。数字、日期、来源列进期目录 README.md 的「资料来源」,口径单列一节。
2. 写旁白(scenes.js)
口语短句,每句一个信息点。数字用中文读法利于 TTS(「三百美元」)。结构:开场定义/反差 → 分解(三要素/两列对比/时间线)→ 怎么算/怎么做 → 数字与门槛 → 冷水/边界 → 一句话总结 + 评论区问题。
3. 出 Logo 与贴纸
先列本期出现的公司/产品/模型 → wb logo,一期一条命令取齐(标志 logo-<名>、字标 logo-<名>-word,两家对比加 --vs=a,b)。再列物件 → wb image,描述只写主体,一次 ≤4 张;逐张 Read 检查杂点与断块。
4. 画场景
抄 @references/scene-patterns.md 的版式坐标再微调;一屏 10~20 个元素。wb stills 后逐张看静帧:重叠、越界、文字超宽、太挤,改到满意。API 见 @references/dsl.md。
5. 封面
scenes.js 末尾 cover 函数:s.coverLayout({ ratio, title, sub, sticker })。标题 ≤2 行、每行 ≤8 字;贴纸用主角。看两张 png:文字没撞贴纸、高亮压在钩子行、品牌标在角上。
6. 出片与验收
wb build。先跑音画同步自检:node scripts/lib/render.js "<期目录>" --check-sync,逐段确认「画完时刻 ≤ 旁白结束时刻」,有 ✗ 就说明画面落后于旁白,回去减元素或调 render.pen。再抽 2~3 帧看(字幕在底、水印在右上、贴纸擦出正常),看 字幕.srt 前几条(原文拼写、数字未拆)。机器检查与交付边界按 @references/common/delivery-qa.md;时长以 ffprobe 为准。
7. 发布文案与标题
按 @references/publish.md:5 个标题候选选 1 主用;写视频号简介、小红书标题正文标签、B 站标题、公众号摘要、评论区置顶。数字与期目录 README.md 一致;合规自查按 @references/common/compliance.md 勾完,填 发布.md。
修改类请求怎么接
| 用户说 | 做法 |
|---|---|
| 改某句旁白 / 加一段 | 改 scenes.js → wb build(只重配改过的场景) |
| 用错了图 / 要真 Logo | wb logo 取官方 SVG → scenes.js 把 s.image 名字换成 logo-* → wb stills → wb render && wb mix && wb cover |
| 换贴纸 / 人物太丑 | wb image 重出 → wb stills → wb render && wb mix(封面用到再 wb cover) |
| 画得太快/太慢 | config render.pen(speed 描边 px/s、charSeconds 每字秒数区间、minSeconds、gapSeconds)→ wb render && wb mix |
| 渲染太慢 / 机器吃紧 | config render.workers(默认 4;1 = 串行)。出帧与路数无关、逐帧一致 |
| 语速快/慢 | config tts.speed(默认 1.2)→ wb build,全部场景自动重配、字幕同步 |
| 配乐大/小 | config bgm.gain → wb mix;试听 wb mix <期> 0.4 |
| 字幕字号/位置 | config captions.fontSize / baselineY → wb render && wb mix |
| 水印位置/关掉/换 logo | config brand.watermark.position/enabled、brand.logo → wb render && wb mix |
| 片尾卡 slogan / CTA / 时长 / 不要 | config brand.slogan、brand.endCard.* → wb render <期> 99-brand && wb mix |
| 封面文案 / 贴纸 / 画幅 | scenes.js 末尾 cover 函数 → wb cover;画幅 config cover.ratios,标签 cover.seriesTag |
| 标题 / 发布文案 / 换平台 | 改 发布.md,不用重出片 |
| 在 Obsidian 里改了图 | wb render && wb mix 直接读 .excalidraw.md;结构性改动回 scenes.js |
| 换声线 / 换曲 | .env 的 VOLC_TTS_VOICE 或 config tts.voice / assets/bgm.mp3(换曲先 volumedetect 量电平再定 gain) |
| 换账号品牌 | config brand.name/accent/slogan,其余不动 |
已知坑
- Logo 一律走 Commons API(
wb logo),很多官网有验证页拦截,别去官网抓图。 - 火山 TTS 偶尔只返回半段:
tts-volc.mjs按逐字数校验自动重试;全量重配用FORCE_TTS=1。缓存键含文本+音色+语速,改语速会全部重配。 - 字幕文本必须用原文,TTS 会把
@grok读成atgrok;字幕时间来自 TTS 逐字时间戳,改旁白必须重跑 tts。 - 不要并发跑
wb image:codex 没按指定路径落盘时会兜底抓「最近生成的图」,并发会互相抓错。一张一张串行跑。 - SVG dash 与 rough.js 双描边已按子路径拆节点、双描边拆 A/B 层,别回退。rough.js
toPaths不带 dasharray,虚线在render.html手动设。 - 文本宽度是估算值,居中用
align:'center'才准;左对齐长文本别超 1920。 - Playwright 截图偶发超时,渲染器带 3 次重试;再挂重跑
wb render。 - 渲染耗时实测:64 秒竖屏片 1921 帧、4 路并行约 60 秒;
wb build全程约 40 秒(TTS 命中缓存时)。 - 音画不同步的坑(已修,别回退):
render.pen.overflow原本默认 0.35,含义是「一段画不完就溢出到下一段前 35%」,实测一条 64 秒片子 16 段里 12 段画面落后、最严重 1.48 秒。现在固定 0,并靠syncTail=0.3提前落笔。改完必须用--check-sync复验。 - 并行出帧靠两点,别动:
render.html的seek()每帧把底色矩形原地重插,强制整屏重画;每路单开一个 Chromium。改渲染器后用render.workers=1和4各出一遍,ffmpeg -f framemd5对比必须 0 帧不同。 templates/scenes.js用require()直接跑会在templates/下生成产物,冒烟测试请用wb new。- 配音的
TTS_ENGINE=say备用通道只在 macOS 可用(系统say命令),Windows / Linux 上不可用。
参考资料
- @references/dsl.md — 场景与封面 API、颜色、尺寸经验、目录约定
- @references/scene-patterns.md — 版式坐标(9 种可直接抄的布局)
- @references/stickers.md — 贴纸、真实 Logo、真人漫画像的做法与检查
- @references/publish.md — 标题五公式与各平台规格
- @references/common/fact-check.md — 查证与来源台账
- @references/common/compliance.md — 发布前合规自查
- @references/common/delivery-qa.md — 成片机器验收与交付边界
examples/— 一期完整示例(旁白、7 个场景、封面函数、资料来源、发布稿)。为满足平台「禁止二进制文件」的要求,示例不含贴纸
来源
原版:simon-skills / whiteboard-video,作者 SimonTalk(X: @HanZhang415188),MIT。来源与改动说明见 NOTICE.md。