手繪動畫
一種完全以程式碼製作短篇卡通的方法,源自 JohnHeibel/PDoomVideo(一部由 Claude Opus 繪製的 156 秒音樂影片)及其通用化工具包 ClaudeAnimationBase(MIT 授權),後者已在此以 template/ 形式整合。
運作方式: 每一幀都是時間 t 的純函數。studio.html 會載入 p5.js、p5.brush 以及你的場景檔案;每個鏡頭會以水彩填色與沸騰墨線在整個 1920×1080 畫格上繪製。render.mjs 驅動無頭 Chrome 來渲染連續圖片以供審查,或平行渲染每一幀,再由 ffmpeg 編碼成 MP4(若提供音訊則包含音訊)。
工作流程
0. 建立專案結構
bash ~/.claude/skills/painted-animation/scripts/new_project.sh <project-dir> # 加上 --keep-demo 可保留範例場景的連接
這會複製範本、斷開示範場景的連接、執行 npm install 並檢查 node / ffmpeg / Chrome。然後先在設計任何東西之前,完整閱讀 <project-dir>/ANIMATION_GUIDE.md:其中包含規則、動畫原則以及完整的引擎 / Clawd API。使用「Read」工具查看 docs/emotions.jpg 和 docs/views.jpg(角色設定稿)。
對於音樂影片或任何長度超過約 20 秒的內容,也請閱讀 references/music-video.md。
對於每部音樂影片,在開始腳本規劃或場景實作之前,請完成以下前置作業:
- 要求使用者同時提供歌曲音訊檔案及其
.lrc歌詞檔案。當預期要有 LRC 檔案時,請勿自行編造、轉錄或近似處理缺失的歌詞。 - 務必詢問創作方向,即使使用者已提及部分內容。請用一條自然語言問題,在一則訊息中提問,涵蓋所需的故事、整體氛圍,以及任何希望包含或避免的重複視覺概念。請勿將其拆分成獨立的問題、欄位、檢查清單或五部分表單。告訴使用者可以一句話回答,或留空。範例:「您希望這部 MV 有什麼樣的故事和整體感覺?有沒有任何想包含的、或想避免的重複畫面?如果沒有偏好,可以留空。」
- 確認收到的檔案,並根據單一回覆推斷出的創作方向進行摘要。在內部提取故事、情緒、氛圍/意象、視覺主題與要避免的內容;請勿要求使用者分別回答這五個類別。將空白回覆視為「沒有偏好」,而非遺漏輸入,並在腳本規劃階段提出適當的詮釋。如果缺少音訊或 LRC 檔案,暫停音樂影片工作流程並要求提供。
- 使用
scripts/beat_grid.py測量節奏;歌詞行的速度通常比你想像的快。將提供的音訊和 LRC 視為時間點的唯一真相來源。
1. 先寫腳本,再寫程式碼
在專案中撰寫 STORYBOARD.md(格式見指南:一句話摘要、世界觀與色調弧線、主題、角色的情緒弧線,然後是每個鏡頭及其閱讀需求——觀眾必須理解的計時列表)。實際範例:examples/xiaozhen/(一部 31 秒的歌詞影片:包含腳本與完整場景程式碼)。
對於音樂影片,請將使用者的故事和情緒轉化為連貫的視覺敘事與情緒弧線,而非逐字圖解每句歌詞。在開始建造前,將腳本展示給使用者並讓他們給予回饋,除非他們直接說可以了。
2. 逐鏡頭建造
- 在
src/config.js中設定duration、bpm、offset(以及audio)。 - 每個場景/章節一個以 IIFE 包裝的檔案,放在
src/scenes/中,結尾為shots([[t0, fn], ...]);將其<script>標籤加入studio.html。 - 先鎖定關鍵姿勢,以靜態圖檢查,然後再加入它們之間的動作。
3. 渲染並查看——每個鏡頭,多次
光看程式碼無法看到動作。在專案目錄中執行:
node render.mjs --sheet=0.1,0.8,1.6,2.4 --cols=4 --w=480 --out=out/check/a.jpg # 關鍵影格
node render.mjs --strip=2.1:2.6 --out=out/check/strip.jpg # 某個時刻的每一幀
node render.mjs --sheet=2.3 --crop=760,420,500,400 --w=500 --out=out/check/face.jpg # 全解析度細節
使用「Read」工具打開每張圖片,並確實根據指南中的檢查清單進行核對:事件是否清楚?角色夠大嗎?閱讀需求的時間安排是否讓初次觀看的觀眾能夠理解?是否有預備動作與跟隨動作,沒有突然的跳動?腳和持握的道具是否接觸地面?每個接縫處是否有轉場?沒有文字、沒有 3D、沒有純黑/純白、沒有模糊的光暈?修正後再次查看。預算:每個鏡頭至少一張連續圖片,每個關鍵動作和轉場一條條狀圖,每個承載故事的表情一個裁切。
連續圖片、靜態圖、短條狀圖以及其他少數幀的預覽可以在目前的雲端環境中渲染和審查。請刻意將這些預覽渲染保持得小而精確;它們是為了視覺驗證,並非渲染完整影片的替代品。
4. 渲染影片
切勿直接開始完整渲染。在預覽審查完成後,告知使用者完整的雲端渲染可能會非常緩慢,並明確詢問他們希望最終影片在本地端還是雲端渲染。等待他們的回答;腳本批准、預覽批准或先前的一般性指示均不構成渲染授權。
- 如果使用者選擇本地端渲染,則禁止進行雲端完整渲染。除非使用者之後明確更改選擇,否則不要在雲端啟動完整序列。完成經過驗證的專案檔案,詢問本機作業系統(若未知),並提供該平台確切的必要條件、
npm install、幀渲染和編碼指令。保留預覽圖片,以便使用者知道已批准的內容。 - 如果使用者選擇雲端渲染,確認預期的時長、幀數和可能的成本/時間,然後使用可恢復的多工作進程渲染器繼續進行。
- 如果使用者未回答或給出模糊的回答,請勿執行完整渲染。再次明確要求選擇本地端或雲端。
node render.mjs --clip --out=out/video.mp4 # 短片,單一工作進程
node render.mjs --frames --workers=4 && node render.mjs --encode --out=out/video.mp4 [--audio=assets/song.mp3] # 長片,平行 + 可恢復
報告輸出路徑、長度和毫秒/幀。提供 open out/video.mp4。
最重要的規則
以下是指南規則的濃縮版;指南具有權威性。
- 手工媒材。 只能使用
paint()/inkLine()(p5.brush)——絕不能使用純 p5 的rect/ellipse/fill。僅限平面 2D:轉彎需透過繪製關鍵視圖(turn()、spinView()),絕不使用 3D 投影。透過glow()(疊加模式)實現光線,因為顏料混合會使黃色疊加藍色變成綠色。 - 無文字。 沒有字幕、標籤、招牌或對話框;用畫的來呈現。反應是繪製的表情符號(
!、汗滴、愛心……)。唯一的例外是歌詞影片中的卡拉 OK,以及最多幾個大型音效字。 - 每個鏡頭都有事發生——一個事件,因果關係,鋪陳與回報。
- 為觀眾預留時間。 列出每個鏡頭的閱讀需求;一次一個閱讀需求;快速動作,停留的意義;引導視線;閱讀需求決定鏡頭長度。這是生成式動畫最常失敗的地方。
- 活著。 沒有東西是靜止的(透過
feel()產生的待機動作、漂移的鏡頭、沸騰效果);表情透過emotions()改變,絕不能直接切換;角色要夠大(中景 u≈20–28,特寫 40–70);所有東西都要跟隨節奏。 - 每個接縫處都有轉場,選擇符合故事的類型(筆刷擦拭、虹膜/造型虹膜、甩鏡、匹配剪輯、動作剪輯、鏡頭跟隨)。
- 一部完整的作品。 一個世界、一條色彩弧線、一條情緒主線、相連的場景、一個與開頭呼應的結尾。
引擎注意事項
t的純函數。 幀是平行且無序渲染的:幀與幀之間沒有狀態,不能使用Math.random(),不能使用逐幀物理。使用hash(i)來獲得穩定的隨機性,使用jit()來產生沸騰效果。- 在每個獨立的背景元素/道具之前使用
boilSeed(key),否則移動中的物體會導致其後繪製的所有東西每一幀都重新沸騰(抖動)。 - 不要將全域變數命名與 p5 的相同。 p5 將
line、OVERLAY、text、color、scale…… 定義為全域變數;一個頂層的function line()會失敗,顯示「Cannot redefine property」,且頁面永遠無法就緒(渲染器超時)。將輔助函式保留在場景 IIFE 內部,或給予不同的名稱。 - 在門口、窗戶和螢幕中的圖層: 先畫出內部的東西,然後再將周圍的牆壁/框架畫回去覆蓋它,這樣開口以外的任何東西都不會顯示出來(火車的
inDoor加上門框、電視邊框片段)。 - 點列表中的
NaN會拋出「Failed to construct 'OffscreenCanvas'」——請防護退化幾何形狀。 - 在相機縮放 ≳ 2 的情況下,大型形狀會失去輪廓線;輪廓線寬度會隨縮放而變化,因此在特寫鏡頭中需要調低
sw。 - 成本 = 填色與描邊的數量。目標是 ≤ 約 1.5–2.5 秒/幀(日誌會列印毫秒/幀)。偏好使用更少、更大的形狀。在沒有真正 GPU 的機器上,水彩填色很慢——可以考慮減少
fill形狀,或是最後手段使用--soft-gl。 - macOS:渲染器使用
--use-angle=metal並自動在/Applications/Google Chrome.app尋找 Chrome;否則請傳入--chrome=<路徑>。
改編
- Clawd 是預設主角,但不是必需的。新的角色、道具和表情使用相同的工具(
paint、ribbon、through、glow)繪製,並在各鏡頭間保持角色設定一致;新的情緒會加入src/clawd.js中的EMO。 src/karaoke.js(範本)透過core.js中的window.overlayHook鉤子為歌詞影片添加逐字卡拉 OK 效果。LOOPS.name = t => {...}; LOOPS.name.len = 4;+node render.mjs --loop=name --png可以製作無縫循環的 GIF(ffmpeg -i out/loop_name/f%04d.png …)。- 較高的推理努力往往會產生更華麗、更詳細的場景。