章节作画规范(paint-mv-animate)
SkillMediaPaints one chapter (src/ch/cNN_name.js) of a paint-mv music video in the PDoomVideo style: watercolour-and-ink frames drawn with p5.brush, the song's own characters (designed from its lyrics) acting on the beat, camera moves and motivated transitions, each frame a pure function of song time. The PDoomVideo ANIMATION_GUIDE as a skill, with the engine API reference, a guide to designing characters in the house style, a techniques catalogue and the nine original chapters as examples. Use when painting, fixing or polishing an MV chapter, shot or character, when briefed as a chapter subagent by paint-mv, or when editing src/ch/*.js or src/characters.js in a project made with paint-mv.
Available today. Use it from your connected AI after setup.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use the 章节作画规范(paint-mv-animate) skill
What this skill tells your AI
The instructions your AI receives, as published by lintsinghua/paint-mv-skills in skills/paint-mv-animate/SKILL.md and read by ahel’s review.
项目把一首歌画成手绘水彩动画:p5.brush 在 headless Chrome 里离线逐帧绘制,所以质量优先于速度,但有预算。
镜头清单在项目的 STORYBOARD.md。画风照原作,内容照歌词:角色、场景、道具是这首歌的分镜从歌词里设计出来的;
画法、动作、镜头语言和下面的规则与原作一致。创作方向(源自原作):可爱、卡通、色彩欢快、动画活泼、每个镜头都有事发生,
大胆、有野心。
开工前按顺序读:
- 项目
STORYBOARD.md:开头的"歌曲信息"、角色表、你的章节表格,以及前后相邻章节的"出"列(Out,衔接要对上)。 - 项目
src/song.js(BPM、首拍offset、时长、wipes、meter)和src/lyrics.js(每句歌词的起止时间)。 - 项目
src/characters.js(本片主要角色)和src/sets.js(共享场景/道具),并看角色模型表node render.mjs --loop=cast --sheet=0,1,2,3,4,5 --cols=3 --out=out/check/cast.jpg。 - 本文件。需要具体签名时查 api.md;要新建客串角色时读 characters.md;想找某种镜头/转场/表演的
现成做法查 techniques.md,它会指到
examples/里原作九个章节的具体函数(原作 P(doom) MV 的完整源码, 画风与难度的标杆;它的角色和场景属于那首歌,学做法,不搬内容)。
章节文件怎么工作
每章一个文件 src/ch/cNN_name.js,包在 IIFE 里,私有 helper 随便起名不会冲突:
// src/ch/c03_takeoff.js
(() => {
const B = n => OFF + n * BEAT; // 第 n 拍的歌曲时间
const gym = t => { ... }; // 私有 helper
function stable(t, lt, dur) { ... } // 一个镜头
function singularity(t, lt, dur) { ... }
chapter('takeoff', 38.5, 59.0, [[38.5, stable], [41.5, singularity], ...]);
CAST.sydney = (x, y, s, t, o = {}) => { ... }; // 只有终场要复用的客串角色才导出
})();
chapter(name, start, end, shots)注册章节。镜头函数以fn(t, lt, dur)调用(歌曲时间、镜头内时间、镜头长度), 必须画出整帧(包括背景)。切镜发生在每个镜头的起始时间;第一个镜头的起始时间必须等于章节start。 最后一章的end写DUR + 1,保证最后一帧也有章节覆盖。- **帧是并行、乱序渲染的。**每个镜头必须是
t的纯函数:不能有跨帧状态,不能用Math.random()。 稳定的逐物体随机用hash(i),手绘抖动用jit(a)。jit每秒重播种 12 次,线条会像手绘动画一样"沸腾",这是想要的效果。 - **只改你自己的章节文件。**缺共享 helper 就在 IIFE 里私有实现一个。发现共享文件真有 bug,报告给总控,不要改。
共享文件:
core.js、clawd.js、cast.js、props.js、timeline.js、lyrics.js、song.js、characters.js、sets.js、studio.html、render.mjs。
画布与布局
- 1920×1080,y 向下,原点左上。没开相机时一切都在这个空间里画。
- **有歌词时,卡拉 OK 条盖住底部 y≈975–1070。**脸和关键动作放在 y≈960 以上。
- 每帧底下已经铺好纸纹,最上面会乘一层纸颗粒和暗角。
- 时间线自动叠加:卡拉 OK、
SONG.wipes处的笔刷擦除转场、SONG.meter窗口里角落的小计量器 (镜头里画了meterProp时自动隐藏)。
绘画 API(core.js)
paint(pts, o) 用点列 [[x, y], ...] 画一个形状:
| 选项 | 含义 |
|---|---|
wash, washOp | 平涂色(0–255)。角色颜色和必须实在可读的东西用它。 |
fill, fillOp, bleed, tex, border | 水彩填充,边缘洇开、有颜料肌理。背景、光晕、阴影、光斑用它。bleed ~.05–.3,tex ~.3–.9,border ~.2–.8。 |
hatch: { d, a, o, b, c, w } | 排线(间距、角度、{rand, gradient}、笔刷如 'charcoal'/'HB'、颜色、粗细)。干笔肌理,少用。 |
ink, sw, br | 描边颜色(默认墨色)、粗细(~.4–2)、笔刷(默认 'ink',或 'inkfine')。ink: null 表示不描边。 |
curv | 让轮廓平滑穿过各点(0–1),而不是直线段。 |
其他:
- 几何:
rectPts(x, y, w, h, jitter)、ellPts(cx, cy, rx, ry, n, jitter, rot)、rrPts(x, y, w, h, r, jitter)(圆角矩形)、starPts(cx, cy, r, inner, n, rot)、heartPts(cx, cy, r)。 - 线:
inkLine(pts, sw, colour, brush = 'ink', curvature)。笔刷:'ink'、'inkfine'、'dry'(毛糙), 以及内置'2B'、'HB'、'charcoal'、'marker'、'spray'、'rotring'、'cpencil'、'pen'。 - **变换:**p5 的
push()/pop()/translate()/rotate()/scale()对所有笔刷调用都生效。 - 调色板
PAL:paper, ink, clay, clayDk, clayLt, night, indigo, rose, ochre, sap, teal, violet, cream, sky。mixCol(a, b, k)混合两个十六进制色。任何十六进制色都可以,但保持和谐(柔和、温暖、水彩感)。 互补色直接混合(夜蓝渐变到晨桃)中途会发灰,中间加一个过渡色分两段混(夜蓝 → 紫玫瑰 → 晨桃)。 不要纯黑纯白:用PAL.ink/PAL.night和PAL.cream。 - 计时:
bpOf(t)给出拍位置(BPM 见song.js;拍长BEAT,小节 4 拍)。另有:beatN(t)整数拍号;pulse(t, k)每拍为 1 然后衰减,pulse2八分音符版,用来做打点。seg(t, a, b)是 t 在 [a, b] 里的 0..1 进度;kf(t, [[t0, v0], [t1, v1], ...], easeFn)关键帧插值,值可以是数组。- 缓动:
ease(smoothstep)、easeOut、easeIn、backOut(过冲)、elasticOut。还有lerp、clamp、frac、wob(t, freq, phase)、hash(i)、TAU。
- 相机:
camBegin(cx, cy, zoom, rot)把世界点 (cx, cy) 放到屏幕中心;camEnd()恢复。推、摇、俯仰、甩、拉远都用它。shakeXY(t, amount)给出 [dx, dy] 震动,打点时加到 cx/cy 上。只能一层,务必配对camEnd()。 - 字(Permanent Marker,带墨色投影)。字会自动跟随当前相机,但不跟随你自己的
push/translate,所以字的坐标给世界坐标。letter(txt, x, y, size, colour, { pop, rot, alpha, ink:false, stroke, font, align, screen:true }),pop是 0..1 带过冲的出现进度。sfx(txt, x, y, size, colour, age, { life, rot })漫画拟声字:弹出、晃动、淡出。- 字在
flushLetters()时合成进画面。它在镜头之后自动调用,所以之后画的东西(擦除转场)会盖住字。 需要让后面的颜料盖住某个字时,镜头中途自己调用flushLetters()。
- 全屏效果(屏幕空间,在相机之外调用):
flash(k, colour)全屏平涂强度 k;iris(cx, cy, r, colour)圆外全涂;irisShape(pts, colour)任意星形轮廓外全涂(嘴形揭幕、心形、钥匙孔)。
角色
本片的角色由分镜的角色表决定,是从歌词设计出来的:
- 主要角色在
src/characters.js(共享,只读;缺表情、姿势或钩子就报告给总控)。先读它的头部注释,再看模型表。 - 客串由首次登场的章节在自己的 IIFE 里实现,导出到
CAST(签名(x, y, s, t, o),要参加谢幕的支持o.bow0..1); 之后要用它的章节调用CAST.name并写一个简化的后备画法。按原作画风设计和实现角色的方法见 characters.md。 - 群演是主角的变体:换
col/dk/lt、戴帽子、缩小。
所有角色遵守同一套约定(原作 Clawd 和研究员的约定),所以下面这些工具对谁都通用:
name(x, y, u, o):(x, y) 是两脚之间的地面点;u是尺寸单位(各角色身高几 u 写在它的头部注释里)。- 姿态:
dy(单位 u,负为上)、sq(压扁;负为拉长)、rot、flip、sx/sy、aL/aR(手臂角:0 平伸,正为抬起,负为下垂)、walk(相位)、noShadow。 - 脸:
eyes:normal, look(+lookX/lookY−1..1), happy, closed, wink, narrow, angry, scared, spark, heart, x, swirl, dot… (角色画不出的眼型会退回 normal);mouth;blush;颜色col/dk/lt。 - 钩子:
draw(u, sw)在身体局部坐标画配饰;手臂末端的armL/armR(u, sw)(研究员是handL/handR)在手臂坐标系 (+x 沿手臂向外)里被调用,用来拿道具。 - 表情符号:
emote+emoteK在头边弹出反应符号:sweat, spark, heart, anger, music, swirl, zzz, !, ?, !?, !!。 - **情绪切换:**脸绝不硬切。用
mood(t, [[t0, 'normal'], [t1, 'scared', 'sweat'], [t2, 'happy', 'heart']]), 返回{ eyes, squint, take, emote, emoteK },展开进角色就得到"闭眼-压扁-弹出"的切换。 - 跳舞:
move(style, t, seed)返回与节拍同步的姿态偏移:bounce, hop, roof(举手), sway, spin, wave, walk, run, idle, stomp, shimmy, mix。dx以 u 为单位:const m = move('bounce', t); hero(x + m.dx * u, y, u, { ...m, ...mood(t, keys) })。 - 尺寸参考(按身高约 8u 的角色):小 u≈6–10,正常 u≈16–22,主角/特写 u≈30–60。副歌和舞蹈镜头里领舞的主角要大(约占画面高度 40%)。
引擎自带的参考角色:原作那首歌的演员,分镜用到时才出现;设计新角色时照它们的结构写。
- Clawd
clawd(x, y, u, o):方块身体 10u × 6u,连腿 8u 高;另有帽子hat(party, hard, crown, halo, wizard, hood, top, fedora, band, sweatband, cat, masq, mask, bowtie)、便当盒嘴lid、noLegs;dancer(x, y, u, style, t, extra)=clawd+move。 - 研究员
researcher(x, y, s, o):白大褂、圆眼镜的小个子人类,约 13.2s 高;自己的眼型 dot, wide, star, swirl, closed, sad, x, heart, look, 外加brows、hairUp、glassesTilt、sit、back、spin;mood()用在它身上要映射眼型名(techniques.md「表演」)。 - 完整选项见 api.md。
场景与道具
- 本片反复出现的场景和道具在
src/sets.js(共享,只读);只在本章出现的场景画在本章里。 - 画在场景里的字(招牌、片名、墙上的字)写完
letter()就调用flushLetters(),否则字会浮在之后画的角色上面(c01curtainUp)。 - 引擎自带原作的剧场道具(
props.js),分镜用到时才用:stageBack(t, o)/stageFront(t, o)(舞台后景与木地板 / 两侧幕布与垂幔,o.curtain合幕、o.alarm警报色、o.spots聚光灯)、meterProp(x, y, s, pdoomAt(t), o)(计量器,配合SONG.meter)、pumpProp+pumpH(t)(下压落在每拍上的打气筒)、sunburst、spotlight。它们也是写新场景的范例:背景画得比画面大 (相机移动和震动不露边)、后景在角色之前画、前景框在最后画。
风格规则
- **观感:**手绘水彩加墨线,像绘本。角色用平涂
wash加墨线轮廓(sw 按大小约 0.8–1.6)。背景用柔和的水彩fill, 通常不描边或细描边。光晕和光线用低不透明度的 fill。肌理来自 fill 和偶尔的排线,而不是噪点。 - **颜色:**每章的调色在 STORYBOARD.md 里。欢快饱和但柔和。角色与背景的对比必须清楚。
- **运动:**一切都在动:相机漂移或推进,角色随拍弹跳(
pulse、move),打点落在拍上。用压扁拉伸、预备动作和过冲 (backOut、elasticOut)。镜头里的重要动作放在拍点上(拍点在OFF + n × BEAT;章节里写const B = n => OFF + n * BEAT)。 - **可读性:**每个镜头一个清晰的焦点动作,轮廓要大。镜头很短(1.4–4 s),笑点必须一眼看懂。
- **少字:**拟声字全片只有少数几个大的(FOOM、BOOM、CHOMP、SLAM…),偶尔一个短词或符号。不要标签、说明文字、 重复歌词的牌子(卡拉 OK 已经有了)。
- **性能:**目标每帧 ≤ 2.5 s,绝不超过约 4 s。渲染日志会打印 ms/frame。开销来自 fill 形状和笔画的数量:几百个没问题,
几千个不行。宁可更少、更大的形状。水彩
fill的开销随顶点数增长,大形状用wash,fill 保持低多边形。
检查你的作品
在项目根目录运行(渲染器用真 GPU;多个代理可以同时渲染):
node render.mjs --sheet=38.6,39.3,40.1,40.9,41.6,42.3 --cols=3 --w=640 --out=out/check/c03_a.jpg
node render.mjs --stills=39.5,43.2 --out=out/check/c03_full
联系表(sheet)把几个时刻拼成一张图(并打印每帧 ms),用 Read 工具打开,仔细看。检查:
- 每个镜头的首帧、末帧和中间几帧;
- 连续时刻的运动是否读得通(例如打点前后每 0.1 s 一帧);
- 进出本章的转场(与相邻章节的"出"和开场对上);
- 没有重要内容落在卡拉 OK 带下面;
- 页面报错:渲染日志里的
[page error]表示你的代码抛了异常(该帧会画坏)。
反复迭代,直到每个镜头都好看:有魅力、可读、有生气、造型一致。看着不对就改:比例、对比、杂乱、僵硬。
交付时汇报
完成后简短汇报:每个镜头一句话说明画了什么;检查过的联系表路径;最慢帧的 ms;导出的 CAST 角色及其签名;
与相邻章节的衔接状态(本章首帧/末帧是什么);发现的共享文件问题(只报告,不修改)。
Signals
- GitHub stars
- 25
- Forks
- 5
- Last commit
- Sep 2026
Advanced
- Item type
- skill
- Key
paint-mv-animate- Source
- github.com/lintsinghua/paint-mv-skills