Back to skills
SKILL.md
Paint Video
ASecurity用代码做手绘风格的动画短片:水彩、水墨用 p5.js + p5.brush,蜡笔绘本用自写的 2D canvas 蜡笔引擎,皮影用自写的皮影引擎,打斗用自写的设色水墨引擎;逐帧画,无头 Chrome 渲染,ffmpeg 合成 MP4。用户说「做个短片 / 动画 / MV / 开场动画 / 十几秒的视频」时用这份;配整首歌的长片或 MV 另看同目录的 mv-workflow.md,蜡笔画风看 crayon.md,皮影看 piying.md,打斗和雾山五行风看 wushan.md,科普讲解片(原理科普、HTML 演示 + 代码合成声效 + edge-tts 配音,允许字幕和标注)看 explainer.md。
- 2 stars
- 0 votes
- 0 copies
- 0 views
- Added October 2, 2026
Works with
Security analysis
92/100- Installs packages at runtime which could introduce malicious dependencies
Pro scans all 6 files and shows the line behind each finding
npx -y skills add nzl153/paint-video-skill --skill paint-video --agent claude-codeAre you the author of Paint Video?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/nzl153-paint-video)---
name: paint-video
description: 用代码做手绘风格的动画短片:水彩、水墨用 p5.js + p5.brush,蜡笔绘本用自写的 2D canvas 蜡笔引擎,皮影用自写的皮影引擎,打斗用自写的设色水墨引擎;逐帧画,无头 Chrome 渲染,ffmpeg 合成 MP4。用户说「做个短片 / 动画 / MV / 开场动画 / 十几秒的视频」时用这份;配整首歌的长片或 MV 另看同目录的 mv-workflow.md,蜡笔画风看 crayon.md,皮影看 piying.md,打斗和雾山五行风看 wushan.md,科普讲解片(原理科普、HTML 演示 + 代码合成声效 + edge-tts 配音,允许字幕和标注)看 explainer.md。
---
# 手绘动画短片
整条路线:每一帧都是一个 **t(秒)的纯函数**,用笔刷画出来(墨线、平涂、水彩晕染、排线)。无头 Chrome 并行把帧渲染成图片,ffmpeg 再合成 MP4。画面里没有一张图是 AI 生图,全部是代码画的。
五套引擎:
| 画风 | 引擎 | 从哪起步 |
|---|---|---|
| 水彩、水墨、版画 | p5.js + p5.brush,速度随镜头复杂度波动较大 | [ClaudeAnimationBase](https://github.com/JohnHeibel/ClaudeAnimationBase)(MIT) |
| 蜡笔、绘本、连环画 | 纯 2D canvas,每帧 10–50ms | 本仓库的 `crayon-kit/`,见 `crayon.md` |
| 皮影、剪影、戏台 | 纯 2D canvas,每帧 2–10ms,锣鼓用代码合成 | 本仓库的 `piying-kit/`,见 `piying.md` |
| 设色水墨打斗(雾山五行风) | 纯 2D canvas,每帧 20–100ms | 本仓库的 `inkfight-kit/`,见 `wushan.md` |
| 科普讲解片(图示、字幕、配音) | 纯 2D canvas,每帧 60–130ms,声效代码合成,旁白 edge-tts | 本仓库的 `explainer-kit/`,见 `explainer.md` |
参照作品:[PDoomVideo](https://github.com/JohnHeibel/PDoomVideo),一支两分半的 MV,由 Claude 在 Claude Code 里生成。那个仓库没有许可证,只能学,不能拿代码去发布;ClaudeAnimationBase 是同一作者后来整理出的 MIT 底座。
## 环境
- 这份 skill 来自 [paint-video-skill](https://github.com/nzl153/paint-video-skill) 仓库。文中的 `crayon-kit/`、`piying-kit/`、`inkfight-kit/`、`examples/`、`tools/` 都在那个仓库里,不在 skill 目录下;本机找不到就问用户仓库 clone 在哪,或者重新 clone 一份。
- Node 18 以上,Chrome 或 Chromium(找不到时给 `render.mjs` 传 `--chrome=<路径>`,或设 `CHROME_PATH`),ffmpeg 在 PATH 里。
- 字幕工具要 Python 3 和 numpy、Pillow。
- `npm install` 会下载并执行第三方代码,装之前先问用户。开新项目时把已经装好的模板目录连 `node_modules` 一起复制,就不用每次重装。
## 开工步骤(水彩 / 水墨)
1. **复制底座**到新项目目录,排除 `out`、`docs`、`.git`。Windows 上用 `robocopy <底座> <新目录> /E /XD out docs .git /XJ`,退出码 1 表示复制成功。
2. **先通读新目录里的 `ANIMATION_GUIDE.md`**。那是完整的规则和 API:`paint()`、`inkLine()`、相机、转场、`clawd()` 的全部参数,都在那份里,这里不重复。
3. **先写 `STORYBOARD.md`,给用户过目,再写代码。** 格式按 guide 里的来:一句话梗概、世界与配色、贯穿母题、情绪弧、逐镜头的「读点」时间表。写之前先读同目录的 `directing.md`(导演手册:景别、轴线、剪辑点、节奏、构图、表演),逐镜表按它的格式写,写完按它末尾的检查表过一遍。
4. 改 `src/config.js` 里的时长和 bpm。场景写成新文件 `src/scenes/<名字>.js`,用 IIFE 包起来,最后调用 `shots([...])`。在 `studio.html` 里用它**替换** `demo.js` 那一行。
5. 一个镜头一个镜头地做,每做完一个就用下面的检查循环看一遍。
6. 出片:`node render.mjs --clip --out=out/video.mp4`。时长较长的用 `--frames --workers=4` 并行渲染,再 `--encode` 合成。
本仓库 `examples/brush/` 有两支完整可跑的场景:`stars.js`(《拾星》,14.5 秒,bpm 90)和 `ink.js`(《落款》,水墨,16.8 秒,bpm 80)。放进 `src/scenes/`,在 `studio.html` 里替换 `demo.js`,再把 `config.js` 的时长和 bpm 改成文件头写的值。只参考写法,别照抄故事。
**给底座打的两个补丁**(`render.mjs` 里两处 ffmpeg 参数和一处 Chrome 参数):
- 输出改成标准 tv range:把 `'-pix_fmt', 'yuv420p'` 换成 `'-vf', 'scale=out_range=tv,format=yuv420p', '-color_range', 'tv'`。否则有的播放器会放成全黑。
- Windows 双显卡笔记本加 `--force_high_performance_gpu`,否则 Chrome 落在集显上越跑越慢。`crayon-kit/render.mjs` 已经带了这两处。
## 最要紧的规则
### 各套画风都适用
模型做动画最容易翻车的地方:
- **帧是 t 的纯函数**:帧会乱序并行渲染,所以不能有跨帧状态,不能用 `Math.random()`,随机值全部由时间和元素编号算出来。
- **纯 2D,不做 3D 投影**:纵深靠遮挡、大小和冷暖。
- **不写字**:画面里不要标签、招牌、带字的对话气泡。要表达什么就画出来,用 emote(`!`、`?`、汗滴、灯泡)。MV 的歌词字幕是例外:单独做一层叠上去,做法见 `mv-workflow.md` 的字幕一节。
- **每个镜头都要发生一件事**,一次只让观众看一件事,先有原因,再有反应。
- **时间感是最常翻车的地方**:观众只看一遍。给每个「读点」留出找到、看懂、停一下的时间。快的是动作,慢的是意义。第一版几乎总是太快。
- **每个接缝都要转场**:包括开头和结尾,不能硬切开始、戛然而止。转场要跟故事有关,别每次都用同一种。打斗片例外:镜头之间直接切在动作中间,不加装饰转场,见 `wushan.md`。
- **角色要大**:按 1080p 画面里的高度看,中景的角色约占画高 1/3–1/2,特写的脸要占到画高一半以上,小于 1/10 只用于远景建立镜头。不同引擎的尺寸参数含义不同,数值不能混用。
### p5.brush 专用(水彩 / 水墨)
来自底座的 `ANIMATION_GUIDE.md`:
- 固定的随机值用 `hash(i)`,线条抖动用 `jit()`。每个独立元素开头调用一次 `boilSeed(key)`,否则一个动的东西会让后面所有静止的东西跟着乱抖。
- **全部用笔刷画**:只用 `paint()` 和 `inkLine()`,不要 p5 原生的 `rect`、`ellipse`、`fill()`,那样看起来像 2000 年代的 Flash。
- 转身靠画好的几个关键视角切换(`turn()`)。
- `clawd()` 的 `u`:中景 20–28,特写 40–70,小于 12 只用于远景。
蜡笔引擎的 API、随机数和角色尺寸是另一套,见 `crayon.md`。
## 默认审美
用户没给风格时,按下面这套来。它偏「纸和墨」:低饱和的暖色、手作痕迹、克制的动效。用户有自己的风格说明时以用户的为准。
**配色**:在场景文件开头覆盖调色板。底座默认的调色板偏饱和(天蓝、紫、玫红):
```js
Object.assign(PAL, {
paper: '#f7f2e6', cream: '#faf7f0', ink: '#2e2a24',
clay: '#bb5f3c', clayDk: '#8f4428', clayLt: '#d98a6c', // 陶土色;Clawd 的身体用的就是 clay
sky: '#a9bfc4', sap: '#8a9a6a', ochre: '#c9a25a', teal: '#6f9590',
rose: '#c98a8a', indigo: '#4a5670', night: '#262a38', violet: '#7d6f8f'
});
```
每支片从里面挑 3–5 个颜色就够,不要全用上。颜色要有一条随剧情变化的弧线,比如冷暗的开头走向暖亮的结尾。
**质感**:底座自带纸张底纹和颗粒,保留。排线(`hatch`)适合版画感,但只用在阴影和局部,别铺满全画面。
**丰富 ≠ 廉价**:
- 可以热闹:一个镜头里可以有很多东西在动,这是「表演型」画面。
- 不能廉价:不要为了热闹撒彩纸、星星、火花来填空。特效必须是剧情里的东西,比如星星掉下来、灯亮起来。
- 自检方法:去掉这个元素,画面会不会少了意思?只少了热闹,就删掉。
- **克制不等于空**:留白要有层次,画面里要有环境、前中后景和质感,不是一大片纸色上摆两三个孤零零的物件。一个镜头只看一件事,但这件事要放在一个有生活气的世界里。
**笔触要像手画的**:角色按 guide 用平涂加墨线没问题,但墨线别全片一个粗细:`inkLine(pts, sw, colour, brush)` 的 `sw` 按远近和主次变化,细节换 `'inkfine'`、`'dry'` 笔刷。背景和环境用水彩 `fill`、`wash` 铺出干湿层次。所有东西都是同一根粗黑线加平涂,就会像剪贴画。
**一条不变的约束**:每支片先定一个贯穿全片的不变量,比如同一个构图位置、同一个形状、同一个角色姿势,转场和匹配剪辑都围着它做,结尾呼应开头。
**音乐和节奏**:底座里的一切都卡 bpm。没有配乐时 bpm 取慢一点(80–100),节奏沉稳些。
## 测试片学到的
### 《拾星》(`examples/brush/stars.js`)
Clawd 捧着空罐子接住掉下来的星星,一罐星光把夜色一点点焐暖。
- **emote 要手动关掉**:底座里的 excited、proud 表情会在头边自动冒 `spark` 火花,是廉价装饰。用 `emotions(t, [[0, 'excited', { emote: null }], ...])` 关掉。`!`、`?`、爱心这类有意义的反应可以留着。
- **「变暖」别整片天换成暖色**:夜空的底色直接混向棕黄色,整个画面会变成一片土褐色,发脏。天还是夜色,只把冷色减弱(混向 `#4b4356`),暖意放在地平线的晕染和发光物体上。
- **道具要一眼认出来**:暗背景上的玻璃罐一开始画成灰色,看着像垃圾桶。改成偏冷的浅色玻璃,加一道高光才认得出。
- **光圈收尾要分三段**:先收到主体上,停一下,再合上。一口气收到 0 会像突然黑屏。
- **Clawd 的手臂很短**,够不到头顶上的东西,举起来的效果像顶在头上。要「捧着」的话得自己画抱的动作。
- **实测速度**:14.5 秒、348 帧,4 个进程并行约 6 分钟,每帧约 1 秒(RTX 4060 笔记本),ffmpeg 编码 10 秒。联系表每帧 0.1–1.7 秒,很便宜,多看几轮。
### 《借露》(反面教材)
蜗牛背露水去救一株缺水的幼苗。规矩全守住了(没字、没火花、色板对、角色没跑形),片子还是没戏:
- **故事靠的状态变化必须夸张到一眼看出**:幼苗从头到尾差不多精神,「缺水低头 → 喝水挺起」对比不出来,整支片就没了因果。关键状态的前后两帧并排放,外行一眼分不出差别就是不够,要改到夸张。
- **推动剧情的道具至少要有角色的头那么大**:露水比蜗牛的眼睛还小、颜色又淡,落到壳上就看不见了。道具小就推近镜头,或者加亮、加轮廓。
- **分镜写了移动,画面就得真移动**:分镜写蜗牛背水走到根边,实际位移很小,大部分时间像静图。对照分镜逐条核对「谁从哪到哪」。
- **画面太空**:整片只有一株苗、一只蜗牛、一片叶子,见上面的「克制不等于空」。
- **转场要有来由**:一条和剧情无关的绿色色带横扫全屏,只是换了个样子的硬切。
写完的自检:把每个镜头的首帧和尾帧并排看,**说不出这一镜发生了什么变化,就是没戏**,回去放大事件。
### 《落款》,水墨(`examples/brush/ink.js`)
- **换画风只需要换调色板和画法,底座不用动**:全片只用墨的三档浓淡加 Clawd 的朱红。「万墨丛中一点红」是水墨的经典手法,Clawd 正好就是那一点红。
- **墨晕开**:形状的每个点从落墨点出发,按距离先后铺开(`bloom()`),比「整个形状淡入」更像墨在纸上化开。
- **水墨的层次**:先铺浅色底,再在山头叠一层重墨往下淡,山脚压一道纸色的雾,远山配合视差移动得慢一些。
- **世界跟着角色一笔一笔长出来**:它走到哪里,哪里才画出芦苇、河和石头,事件本身就是在画画。这个做法也保证了每个镜头的首尾帧有明显变化。
- **构图约束也要考虑终点**:角色要看得见前方的路,就得站在行进方向的后侧,走到最后自然落在画面那一侧的角上。
## 第二套画风:蜡笔绘本
用户要童趣、绘本、蜡笔、连环画,或者不想再要水彩时,**读同目录的 `crayon.md`**。这套不用 p5.brush,是自写的 2D canvas 蜡笔引擎(`crayon-kit/`),比水彩快一个数量级。里面有:引擎原理和 API、角色参数、变身梗和分格砸格的做法、蜡笔字幕、用探针让字幕避开人物。
## 第三套画风:皮影
用户要皮影、剪影、戏台,或者想做一支不配歌、只用锣鼓的短片时,**读同目录的 `piying.md`**。引擎在 `piying-kit/`:每片皮单独刻好镂空再乘到透光的幕上,角色是带操纵杆的关节小人,锣鼓点用 numpy 合成。里面有:引擎原理和 API、角色参数、动作砸在锣鼓点上的做法、幕后机位的镜像和剪影轮廓光、合成器。
## 第四套画风:设色打斗(雾山五行风)
用户要打斗、武侠、动作戏,或者点名雾山五行、设色水墨时,**读同目录的 `wushan.md`**。引擎在 `inkfight-kit/`:人物用毛笔勾线加平涂和暗面,动作走穿过关键帧的平滑曲线,脚单独按踩点走,冲击帧用反相和朱红,最后火墨炸满屏。里面有:引擎和人体的 API、怎么编一场打斗(先定接触点再倒推站位,用 `probe.mjs` 拿数字对)、镜头安排、踩过的坑。
## 第五套:科普讲解片
用户要科普视频、讲清楚某个发现或原理时,**读同目录的 `explainer.md`**。底座是 `explainer-kit/`(《用光,打开大脑》,2026 诺奖光遗传学,3:05)。每场一种画风,允许字幕、章节和图示标注;画面、声效、旁白共用一份 `timeline.js`;旁白读得比画面长时,按句子时长把那一段画面拉长。
## 检查循环(每个镜头都要做,别省)
只读代码看不出动画效果,必须渲染出来看:
```bash
node render.mjs --sheet=0.1,0.8,1.6,2.4 --cols=4 --w=480 --out=out/check/sheet.jpg # 联系表:每个镜头的首、中、尾帧
node render.mjs --strip=2.1:2.6 --cols=6 --w=320 --out=out/check/strip.jpg # 连续帧:转身、起跳、转场
node render.mjs --sheet=2.3 --crop=760,420,500,400 --w=500 --out=out/check/face.jpg # 局部放大:表情、手、接触点
```
联系表的 `ms/frame` 全是 0,说明页面根本没画东西(多半是场景文件有语法错误),先看终端里的 `[page error]`。
用读图工具打开,逐条检查:
- 事件读不读得出来,角色够不够大,和背景分不分得开;
- 有没有瞬移、突变的帧;
- 身体和支撑物:坐下、站上、靠上某个物体,要有抬腿、屈膝、落座的过渡,身体不能贴着物体平移上去,也不能和它重叠;
- 肢体长度不变:伸手够东西时胳膊不能拉长,够不着就让角色走近或弯腰;
- 挥动、翻转的道具不能穿过场景里的物体;
- 动作有没有预备和跟随,是不是左右对称地同时在动;
- 接缝处有没有转场;
- 有没有字;
- 颜色有没有发脏(黄色叠在蓝色上会变绿,发光要用 `glow()`);
- 配色是否守住定好的色调。
## 性能与已知坑(p5.brush)
- **续渲只看帧文件在不在**:改了代码再跑 `--frames`,已经存在的帧会原样保留,成片里混进旧画面,而且不报错。底座的 `render.mjs` 就是这样,改代码后先把 `out/frames` 挪走(别删)再渲。`crayon-kit` 的 `render.mjs` 会给帧记代码签名,代码变了就停下,让你选 `--force`(旧帧挪到 `out/frames_old_*`)或 `--keep-frames`(已经手动挪走改过的那几镜)。
- **页面报错**:`crayon-kit` 的 `render.mjs` 遇到页面报错会失败退出;底座的只打印一行 `[page error]` 然后照常出图,要自己留意。
- 联系表会打印每帧耗时,目标 ≤1.5 秒/帧。水彩 `fill` 最贵:大面积的形状用 `wash`,`fill` 少用而且边数要少。
- 有独显时每帧约 1 秒。明显变慢时用 `node gpu_probe.mjs <chrome路径>` 查 Chrome 落在了哪块显卡上。
- `studio.html` 会从 Google Fonts 加载 Permanent Marker 字体,只有 `letter()` 用得到。不写字的片直接删掉那一行 `<link>`,免得网络不通时卡在加载上。
- 点坐标里出现 NaN 时,报错信息是 `Failed to construct 'OffscreenCanvas'`,堆栈指向场景代码而不是 NaN 本身,要回头查几何计算。
- 相机放大到 2 倍以上时,离原点很远的长线条会塌缩成一个点。超长的边用 `inkLine` 分段画。
- 偶尔出现 5 条 `WebGL: INVALID_OPERATION` 警告是无害的,其他页面报错是真错误。
- **大形状只用 `fill` 几乎是透明的**:远山、水面这种上千像素宽的形状,只给 `fill` 会淡得看不见。要先用 `wash`(`washOp: 255`,颜色调浅)铺底,再叠一层 `fill` 做晕染和边缘。
- **`inkLine` 只给两个点时什么都不画,而且不报错**:直线也要给 3 个点以上,最好带一点起伏,更像手画。涟漪、水痕、地面短线最容易中招。
- **同一个形状别分段画**:山头的暗部分成几段画,重叠处会出现竖条接缝。整条一次画完,边缘的起伏用 `hash(i)` 做。
- **`inkLine` 分段时检查最后一段的点数**:切片切到末尾可能只剩 1 个点,会直接报错 `spline() requires at least 2 points`。
## 长片或 MV
**先读同目录的 `mv-workflow.md`**:音乐分析、拍号驱动的逐镜表、零件库、分批做、分段渲染、局部返修、成片调色、歌词字幕。
另一种做法是由主代理写好分镜和风格说明,按章节分给多个子代理,各改各的章节文件。用户没要求就一个人从头做到尾,全片更统一。
歌词对时、音频节拍分析还可以参考 [paint-mv-skills](https://github.com/lintsinghua/paint-mv-skills)(MIT,中文)。
## 交付
- 交付物是 `out/video.mp4`,再附一张联系表截图。
- 汇报时说清三件事:总时长、每帧平均耗时、自己检查时发现并修掉了什么。没看过的部分要明说没看过。
Files in this skill
- SKILL.md
- crayon.md
- directing.md
- mv-workflow.md
- piying.md
- wushan.md
Attribution
Comments
Loading comments…