animate 是做什麼的?
你可以用程式碼製作短篇動畫影片:一個 <canvas>,以程序化方式繪製和配樂,具決定性、逐幀進行,並使用無頭瀏覽器和 ffmpeg 渲染。故事遵循從頂尖短篇動畫中研究的文法;視覺是一種風格——一個從 styles/ 選取或根據使用者參考圖使用 new-style.md 製作的可插拔套件。
需求: Node 18+、含 Chromium 的 Playwright (npm i -g playwright && npx playwright install chromium)、PATH 中的 ffmpeg。請先檢查它們;如果缺少任何一個,請告知使用者安裝指令並停止。如需語音旁白,則需 Python 搭配 faster-whisper (pip install faster-whisper) 來計算每個字的時間;若無此套件,則會估算字詞時間。
以下所有路徑均相對於此技能的基本目錄。成品位於使用者的專案中,路徑為 pieces/<name>/(如有需要請建立 pieces/);使用者建立的風格則位於 <project>/styles/<name>/。
不可妥協事項
- 在分鏡核准前絕不進行動畫製作。 確認環節的存在,是因為錯誤的故事或視覺會導致整個建置過程白費。
- 在繪製任何東西之前,先看參考圖——使用者的參考圖,或風格的
sample.png和示範。然後對照 grammar/FRAME.md 和風格自身STYLE.md中的檢查清單,檢查每個關鍵畫格。 - 螢幕上的每個事實陳述都必須透過網路搜尋進行驗證,並在摘要中列出其來源。永遠不可在沒有來源的情況下聲稱「第一」。
- 單一檔案,所有程式碼: 一個
index.html;所有內容都在程式碼中繪製和合成——程式碼中沒有圖片/音訊/影片檔案、沒有data:URI、沒有 URL、沒有網頁字型。tools/build.mjs會強制執行此項。有兩個可選擇的例外,且均為使用者自己的素材:資產(位於<piece>/assets/中的螢幕截圖和標誌,由建置過程嵌入)和音樂曲目(在匯出時合併)。請參閱下方的資產和音樂。 - 決定性: 僅使用種子亂數 (
RNG(...)),沸騰索引B用於每步驟的變化。絕不使用Math.random()或真實時間。 - 參考媒體留在本地。 除非使用者要求標示來源,否則絕不提交、複製其程式碼,或在發布的輸出中提及藝術家姓名。
- 「完成」是可衡量的:
tools/review.mjs通過,且你已查看過連續輸出頁。報告數字,而非「看起來不錯」。 - 詢問時提供選項。 使用 AskUserQuestion 並先提供建議的預設值,讓使用者可以一鍵回覆。
流程
0. 需求輸入
詢問 intake.md 中的問題——僅詢問你無法推斷的問題。關於視覺風格,請建立畫廊(node tools/gallery.mjs <out.png> [<project>/styles],約 3 秒),展示它並詢問:要這些中的哪一個,或者提供參考圖? 然後你自己根據 FRAME.md 和風格的檢查清單研究參考圖。
1. 故事確認(文字,快速)
從 grammar/FORMATS.md 中選擇格式——劇情引擎會決定它(歷史是以形變連接的年表;任務則在節拍上進行剪接;列表是目錄)。閱讀 grammar/STORY.md。在網路上驗證事實。然後向使用者展示:
- 一句話的想法和可說出的裝置(「火花每經過一個時代就獲得一道光芒,最終成為答案」)
- 格式及原因、風格、長度、BPM
- 編號的節拍表:時間、發生什麼事、進入下個節拍的橋接、聲音的角色(靜默在哪裡、最響亮的拍點在哪裡)
詢問:核准 / 更改節拍 / 不同角度。 修改直到獲得核准。這是「我意思是 X,不是 Y」這類問題成本最低的時候。
2. 視覺確認(2–4 個風格畫格)
在 pieces/<name>/ 啟動成品:從風格的示範(styles/<name>/demo/)複製 piece.json 和 src/——它已經使用該風格的套件,並以形變連接各個時代——或從 templates/beat-cut/ 複製給在節拍上強烈剪接的成品(8 分音符、奇數畫格上的 16 分音符衝刺、每個鏡頭一個時代、來自攝影機的取景),或從 templates/piece/ 複製給最簡單的剪紙形變。在 piece.json 中設定 "style",然後重新撰寫 brief.md 和 LOG.md(範本中的內容是佔位符)。
- 內建風格: 閱讀其
STYLE.md和示範,並使用其套件繪製。 - 使用者的參考圖: 遵循 new-style.md。先測量參考圖(
tools/measure/refs.mjs);成品自身的風格畫格就是比對後的畫格,所以這是一次確認:一起呈現比較頁和畫格。成品獲得核准後,儲存風格資料夾(該成品成為其「經過驗證的」案例)。
繪製 2–4 個最不同的節拍,作為全尺寸畫格。每個都必須通過 FRAME.md 和風格的檢查清單。使用 node tools/still.mjs pieces/<name> 2.25,9.75,16.5 look.png --scale 0.5(或使用設定了 TIMELINE.board 為那些節拍的 tools/storyboard.mjs)並排渲染它們,然後顯示圖片。詢問:每個畫格的讚 / 更改視覺 / 嘗試另一種風格。
3. 分鏡確認(每個節拍)
以核准的視覺風格繪製每個節拍。用每個節拍的一個關鍵時間填寫 TIMELINE.board({ t, title, sound, next })並渲染整個分鏡板。詢問按面板編號的讚/倒讚和備註;重新繪製被倒讚的面板;重複此過程。這些面板就是影片本身的場景函數,因此之後無需重新繪製任何東西。
4. 建置
- 撰寫
pieces/<name>/brief.md(規格、風格、節拍、橋接、音效計畫、聲明與來源)。維護一個LOG.md。 - 動畫化場景:使用
TT、ev(t, d)、popS(t)、書寫效果(請參閱 craft.md → 動作)來進行計時動作。在src/bridges.js中連接時代、攝影機和橋接(用於形變連接的格式)或設BRIDGES = []給硬剪接。 node tools/build.mjs pieces/<name>→index.html(風格的套件從piece.json的"style"引入)。- 在建置時測試拼貼:
node tools/tile.mjs pieces/<name> tile.png <frames...>——查看它們;在每個形變和剪接處進行拼貼。 - 在
src/score.js中配樂(請參閱 craft.md → 音效)。 node tools/export.mjs pieces/<name> --share→ 畫格、audio.wav、音軌分軌、renders/final.mp4、renders/share.mp4(--formats 9:16,1:1,16:9給所有格式;--blur 4給 1 秒一格的風格使用運動模糊)。使用--only-audio疊代混音(重新渲染配樂並重新混入,快約 4 倍)。node tools/review.mjs pieces/<name>→ 每個鏡頭的連續輸出頁、剪接網格、形變網格、故事弧線、錨點、文字(被截斷 / 重疊 / 低於手機 UI)和音效(最響亮的時刻、它之前的靜默、LUFS)。修正直到通過,查看連續輸出頁,在LOG.md中寫入一個每個鏡頭的 PASS 表格。相信文字檢查勝過你的眼睛:一個被裁切的標籤在連續輸出頁上看起來可能還好。
5. 交付
發送 renders/share.mp4。用幾行文字說明:它是什麼、檢查的數字、審查發現並修正了什麼、最弱的鏡頭,以及你尚未驗證的內容(例如你無法聆聽配樂)。詢問應該改變什麼:故事、視覺、轉場、節奏、音效。
6. 學習
每次執行後,將內容附加到成品的 LOG.md。如果有新的錯誤或有效的方法,請為 craft.md、風格的 STYLE.md 或套件變更提出一行規則,並在使用者同意時加入。
語音旁白(可選)
在需求輸入時詢問(問題 3)。使用語音時,語音就是時鐘:每個節拍約在前一句結束後 0.3 秒開始,重要時刻落在口說文字上。腳本就是分鏡板:每個節拍一行。
- 將腳本寫入
pieces/<name>/voice/script.json:{ "lead": 1.2, "tail": 1.8, "lines": [{ "id": "open", "text": "As it is spoken.", "after": 0.45 }, ...] }。按照應該被說出的方式書寫數字和名稱。保持在 ≤ 3.5 字/秒;對於需要時間來理解的畫面(計數、字幕、回報前的靜默),給台詞一個較長的after。 - 在語音存在之前進行建置:
node tools/voice.mjs pieces/<name> --scratch使用作業系統自身的語音(切勿發布它)製作一個計時用語音,以便可以對整個成品進行動畫化和審查。 - 錄音檔: 每行一個檔案,儲存為
voice/<id>.mp3,並在該行設定"file"。- ElevenLabs 連接器(如果使用者有:Claude 的設定 → 連接器 → ElevenLabs,透過 OAuth 登入,無需 API 金鑰):使用其語音搜尋找到 2–3 個適合的敘述者,為每個敘述者產生一行(每行一個變體,不是四個),讓使用者選擇,然後使用該語音和模型為每行產生一次。產生結果會為每個錄音檔提供一個短期有效的下載連結:將每個儲存到
voice/。每產生一個字元約花費一個點數;在完整執行前告知總數。如果連接器是在工作階段期間新增的,其工具可能只有在應用程式重新啟動後才會出現。 - 使用者自己的錄音,每行一個檔案(或從一個錄音檔中剪出)。
- 絕不從使用者未指出的網站獲取語音,也絕不在未經所有者同意的情況下克隆語音。
- ElevenLabs 連接器(如果使用者有:Claude 的設定 → 連接器 → ElevenLabs,透過 OAuth 登入,無需 API 金鑰):使用其語音搜尋找到 2–3 個適合的敘述者,為每個敘述者產生一行(每行一個變體,不是四個),讓使用者選擇,然後使用該語音和模型為每行產生一次。產生結果會為每個錄音檔提供一個短期有效的下載連結:將每個儲存到
node tools/voice.mjs pieces/<name>→voice/voice.wav(帶有停頓的台詞)和voice.json(每行和每個字的開始和結束時間,由本機的 faster-whisper 在音訊中聽到)。建置過程將其注入為VOICE。- 在檔案頭中:
VL(id)是一行,VW(id, word, n)在其第 n 個word開始時;節拍在前一行的t1 + 0.3開始;TIMELINE.narration來自VOICE.lines,DURATION來自VOICE.duration。在命名它們的字詞上提示書寫效果和閃爍。將回報的強音放在命名它的台詞之前的停頓中:語音下的強音會被壓低。 piece.json:"voice": { "file": "voice/voice.wav", "music": -12 }。匯出會將語音混合在配樂之上(配樂在musicdB,在語音說話時進一步壓低)至 −14 LUFS。審查會僅根據配樂來判斷故事弧線和回報的時間窗口(renders/audio-score.wav;語音比任何強音都大聲)以及帶有其 NARRATION 檢查的語音。- 重新錄音和腳本編輯: 取代一個台詞的檔案(或編輯其文字),重新執行
voice.mjs,重新建置:從VOICE觸發的場景會隨之移動。一個改變的字詞可能會孤立指向它的提示;檢查那些節拍。
音樂:使用者自己的曲目(可選)
在需求輸入時詢問(問題 6)。沒有曲目時,配樂會在程式碼中以 120 BPM 的格線作曲。使用曲目時:
- 將其放入成品中:
pieces/<name>/audio/track.wav(WAV、MP3 或 M4A),並在piece.json中:"music": { "file": "audio/track.wav", "start": 12.0, "gain": 0, "fade": 0.5 }(start= 歌曲中成品開始的位置)。 node tools/beats.mjs pieces/<name>/audio/track.wav pieces/<name> --start 12 --dur <piece length>→beats.json:節奏、第一個節拍、每個節拍和小節、最強的強音、最響亮的時刻和音量下降處。如果使用者知道節奏,請傳遞--bpm 128;否則檢查其「其他節奏」行(兩倍 / 一半 / 三分之二拍是常見的錯誤)。- 建置過程將其注入為
BEATS;起始檔案頭從中讀取BPM和GRID0,而onBeat(t)將時間對齊到歌曲的 8 分音符格線。將剪接、形變和提示放在onBeat(...)上。歌曲決定弧線: 將轉折點放在一個音量下降處(BEATS.quiet),將回報點放在其最響亮的強音(BEATS.loudest、BEATS.hits),而不是放在作曲配樂會放置的位置。 - 配樂(
src/score.js)只保留音效(to = 's'):匯出會用曲目取代音樂匯流排,將音效分軌混合在上面,並標準化至 −14 LUFS。審查的格線使用TIMELINE.gridOffset;其 SOUND 部分會指出最響亮的時刻是否落在回報橋段中。 - 瀏覽器預覽仍然播放作曲配樂(頁面中沒有音訊檔案);匯出才有曲目。
資產:真實的螢幕截圖和標誌(可選)
在主體是產品時,於需求輸入時詢問(問題 7)。然後:
node tools/capture.mjs <url> pieces/<name>/assets/home.png [--selector "css"] [--full] [--dark] [--hide "css"]對使用者自己的網站進行螢幕截圖(一個頁面、一個區塊、標誌)。在擷取他們不擁有的任何內容之前先詢問。他們給你的標誌檔案也放入assets/(PNG、SVG、JPG、WEBP)。tools/build.mjs將assets/中的所有內容嵌入到index.html中(成品保持為一個檔案,且畫布保持可被檢查讀取);window.renderFrame在圖片解碼後出現,每個工具都會等待它。- 使用
drawAsset('home.png', x, y, w, h, { fit: 'cover' | 'contain', r })在風格自身的框架內(撕紙照片、帶邊框的插圖、印刷卡片)繪製它們,使它們融入視覺風格。asset(name)回傳該圖片。 - 僅限真實產品 UI: 為真實的螢幕製作動畫(裁切、推近、揭曉、用主角指向它們);絕不繪製虛假的螢幕畫面,使其看起來像是產品。從螢幕截圖中取樣品牌顏色。
格式:一個成品,多種形狀(可選)
在需求輸入時詢問(問題 2,多選)。在 piece.json 的 "formats": ["9:16", "1:1", "16:9"] 中列出它們(第一個是主要格式)。起始檔案頭讀取 ?format= 並設定 W、H、SAFE 和 CX;場景使用 LX(fraction)、LY(fraction) 放置事物,並使用 UNIT 調整大小,並在需要時根據形狀使用 PORTRAIT / WIDE 來選擇不同的排列方式(9:16 上三欄,16:9 上三行)。絕不將 9:16 的渲染裁切為 16:9。
node tools/export.mjs pieces/<name> --formats 9:16,1:1,16:9 --share→renders/final.mp4(主要)和final-<w>x<h>.mp4給其他格式。- 審查每個:
node tools/review.mjs pieces/<name> --format 16:9(tile/still/textcheck/storyboard也接受--format)。文字檢查在這裡最為重要:適合 16:9 的寬字幕可能會在 9:16 上跑出去。
動作:彈簧與模糊
- 彈簧(
kit/core.js):springMove(t0, a, b, SPRING.snappy)以輕微的超射和穩定來移動一個數值;springTrack([[t, v], ...])跟隨一個數值通過許多目標而不會跳躍。預設值:snappy(UI)、smooth(卡片、攝影機)、heavy(大字、標誌)、playful(吉祥物)。閉合形式,因此畫格保持決定性。在二格一變的樣式中,它們每 2 畫格步進一次,就應該如此。 - 運動模糊:
export.mjs --blur 4平均每畫格 4 個子畫格——僅用於一秒一格的樣式(STYLE.ones:數學、等角);手繪風格在二格一變時故意清晰(匯出時會發出警告)。
平行代理程式(長成品)
一個 45–60 秒的成品有 10–15 個場景;將
…
[為翻譯而截斷]