Communitygithub.com

cxb2523/gsb-828-9f38b84355494457

將程式碼渲染成動畫影片:透過 Canvas 2D 與 Web Audio,以程式碼控制每一幀,並在無頭模式下輸出為 MP4,支援 16:9 / 9:16 / 4:5 等多種比例,適用於動畫解說、技術教學與動態圖表。

gsb-828-9f38b84355494457 是什麼?

這是一項用於製作「程式碼渲染」動畫影片的 AI 技術。其核心理念是讓 AI 模型讀取原始素材(如文章或規格說明),並自動生成 JavaScript 程式碼,這些程式碼會逐幀繪製畫面並合成音效,最終透過無頭瀏覽器與 ffmpeg 渲染為 MP4 影片。與傳統的像素生成不同,此方法的產出是「可編輯的程式碼」,因此任何時刻的畫面都能被輕鬆調整、重新計時或重新渲染。此技能參考了 Addy Osmani 在 LinkedIn 上的想法,並整合了開源專案 riso-windowseat、HeyGen HyperFrames 與 Remotion 的設計概念。 此技能定義了一套嚴謹的合約規範,要求所有動畫作品必須是一個自成一體的 `index.html`,並透過一個「時間軸」物件來同步畫面與音效。為了確保可重現性與可修復性,它禁止在渲染過程中使用 `Math.random()`、`Date` 或 `requestAnimationFrame` 等可能產生不確定性的函式,轉而使用種子亂數產生器。此外,該系統內建了一套完整的品質驗證流程(包括 `verify.mjs`、`lint.mjs`、`review.mjs` 與 `qa.sh`),能自動檢查文字大小、安全區域、重疊、靜止畫面等問題,確保最終產出的影片符合專業品質。 此技能支援多種格式(16x9, 9x16, 4x5, 1x1),並透過同一個時間軸驅動所有格式的佈局。它提供了從 2 分鐘到 40 秒的範例作品,並內建了如 film-fix 這樣的可視化編輯工具,讓審閱者可以直接在畫面上拖曳調整元素,並將修改結果寫回程式碼中。在平台支援方面,它適用於 Claude Code、Cursor 或 Codex 等 AI 編碼模型,開發者可以透過一系列 npm 與 bash 命令來驗證、渲染與交付最終的 MP4 影片。

相容平台✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/cxb2523/gsb-828-9f38b84355494457/tree/HEAD

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

程式碼渲染動畫

概念(Addy Osmani, LinkedIn 2026-09-24):一個編碼模型讀取原始素材(文章、規格書),並編寫出能繪製每一幀畫面及其音效的 JavaScript 程式碼。最終產出是一個程式,因此任何時刻都可以被編輯、重新計時、重新渲染或重新調整格式。Osmani 未公開其提示或工具。此技能的設計來自開源的 riso-windowseat 專案(MIT 授權,僅供參考,請勿複製其說明):https://github.com/sevenevesai/riso-windowseat 。同時也借鑒了 HeyGen HyperFrames (https://hyperframes.heygen.com/guides/pipeline) 和 Remotion 的設計理念。

基本規則:以至少 4 個採樣/秒的頻率逐幀審查,絕不宣稱通過未執行的檢查,並精準修復缺陷。

合約(一切仰賴於此)

每個作品都是一個獨立的 index.html,其中包含一個能讀取 URL 中 ?w=&h= 參數的 <canvas>,並必須公開以下 API:

window.__anim = {
  duration, fps, width, height, canvas,
  ready,            // 在字型/素材載入完畢且 seek(0) 繪製完成前為 false
  seek(t),          // 同步繪製時間點 t 的準確幀
  renderAudio(),    // Promise<base64 WAV>,48 kHz 立體聲,長度正好為 duration
  marks,            // 可見事件(彈出、切換)發生的時間點,音效必須對齊這些時間點
  shots,            // [{id,start,end}] - 驗證時會檢查邊界
};

不變條件:

  • seek(t) 是 t 的純函數。螢幕上的每個數值都由 t 計算得出:prog(t,a,b)、緩動函數、pop(t,at)。幀之間不得有狀態殘留,不得使用 requestAnimationFrame 計時,不得使用 Date,不得進行物理模擬模擬除非從 0 重新計算或使用閉合形式解。
  • 渲染過程中絕不能呼叫 Math.random()。請改用 rngFor('stable-key')(基於 mulberry32 的種子亂數),並在載入時使用固定的鍵來創建隨機佈局。更改鍵會改變輸出,因此在獲得批准後應凍結鍵。
  • 一個單一的時間軸物件(TL)包含所有場景範圍和事件時間點。畫面與配樂都讀取同一個時間軸物件。這就是同步的運作原理:音效在與畫面事件相同的數值時間點上被排程。
  • 在 ready=true 之前等待字型載入完成(document.fonts.load('700 40px "DejaVu Sans"'))。否則,最初的幾幀會使用後備字型渲染。使用 fc-list : family(Linux/macOS)或 C:\Windows\Fonts(Windows;Segoe UI、Bahnschrift、Consolas 和 Georgia 始終存在,DejaVu 則非如此)檢查已安裝的字型。如果設計需要特定字型,請將其嵌入為 base64 的 @font-face 並以同樣方式載入。
  • 作品中不得有網路請求。任何素材都必須以 data URI 的形式嵌入。

可編輯元素(用於影片修復)

review/film-fix.html 允許審閱者直接在畫面上移動和縮放元素,並將修改結果寫回 index.html。若希望啟用此功能(範例作品已啟用,請從那裡複製),需在作品中添加:

  • const TUNE = /*TUNE*/{}/*END-TUNE*/; 寫在同一行,以格式和元素 ID 作為鍵:{"16x9":{"title":{"dx":0,"dy":0,"s":1}}}。它會為每種格式分別設定鍵值,因為每種比例都有各自的佈局。dx 和 dy 以畫布像素為單位。該工具只會改寫兩個標記之間的文字。
  • 圍繞每個主要元素使用 E(id, [x,y,w,h], draw)。它會查找 TUNE[fmt][id],然後保存狀態,根據 dx/dy 平移,圍繞盒子中心縮放 s,呼叫 draw() 並恢復狀態。它還會將 {id, box} 推入 __anim.elements,其中 box 是轉換後的畫布像素座標(來自 ctx.getTransform(),因此相機移動也會被考慮在內)。seek(t) 在每幀開始時會清空此列表。
  • __anim.elements、__anim.format(16x9、9x16、4x5、1x1)和 __anim.tune(getter/setter,用於操作即時的 TUNE 物件,供工具即時預覽使用)。
  • 規則:ID 在整部影片中必須唯一;沒有隨機性;當 TUNE 為空時,E 不套用任何轉換,因此畫面能保持位元組相同。可透過在添加 E 前後計算幀的雜湊值來證明,然後執行 verify.mjs。

不包含 E 的影片仍可在 film-fix 中運作:其「微調」模式會框選一個區域,並將拖曳或縮放動作轉換為供 AI 使用的數值修正。

專案設定

需要 Node 18+、ffmpeg(libx264、aac、blackdetect、freezedetect、ebur128)和本機的 Chrome/Chromium。工具鏈會在 Windows、macOS 和 Linux 的常見安裝位置尋找 Chrome;可設定 CHROME 環境變數來覆蓋。qa.sh 需要 bash(Windows 上為 Git Bash),並會選取系統字型用於顯示時間碼;可設定 QA_FONT 環境變數來覆蓋。無需全域安裝任何軟體;puppeteer-core 是唯一的 npm 依賴。

SKILL_DIR=<此技能資料夾路徑>
mkdir -p anim && cp -r "$SKILL_DIR/harness" anim/harness
cd anim/harness && npm install && cd ..
mkdir -p works/<名稱>
cp "$SKILL_DIR/example/browser-10s/index.html" works/<名稱>/index.html   # 從可用的範例開始
# 所有工具鏈指令都在 anim/ 目錄下執行;輸出會放在 out/ 目錄

工具鏈指令

指令功能成功訊號
node harness/verify.mjs works/<n>/index.html --format 16x9以採樣點和場景邊界為基礎,進行前向、後向和冷啟動跳轉,然後比較像素雜湊值verify: OK,退出碼 0
node harness/shoot.mjs works/<n>/index.html --format 9x16 --times 1.5,3.4 或 --range 3:4:0.1 --out out/stills在精確時間點以原生 Canvas 格式輸出 PNG手動檢查
node harness/audio.mjs works/<n>/index.html --twice僅渲染配樂(秒為單位),檢查準確長度和執行結果的確定性無 FAIL。Chrome Web Audio 每次執行可能相差 1 LSB;容差為 1
node harness/render.mjs works/<n>/index.html --format 16x9 --fps 30 --out out/<n>-16x9.mp4逐幀呼叫 seek 並將 PNG 傳送給 ffmpeg(libx264 crf18、yuv420p、+faststart),然後將 renderAudio 的結果混入 AAC顯示幀數和時間。頁面錯誤時退出碼為 2。使用 --from/--to 或 --silent 可渲染不包含音訊的範圍
node harness/lint.mjs works/<n>/index.html --all-formats對頁面本身進行品質把關(16:9 + 9:16;若指定 --format 則只檢查一種):文字大小、安全區域、文字重疊、靜態拉伸、空白幀、場景列表、音效對齊事件時間點、螢幕上的文字數量lint: OK (0 FAIL),退出碼 0
node harness/review.mjs works/<n>/index.html --format 16x9 --out out/review-16x9,然後 --check out/review-16x9/REVIEW.md每個場景輸出一張聯絡表(第一幀、6 張均分幀和最後一幀,並燒錄時間碼)、lint JSON 以及包含每個場景固定問題的 REVIEW.md。--check 在遇到空白或 TODO 答案、評分低於 4、lint 失敗、場景章節被刪除或影片自聯絡表生成後已被修改時,判定為失敗review check: OK,退出碼 0
bash harness/qa.sh out/<n>-16x9.mp4 4ffprobe、blackdetect、freezedetect、EBU R128 音量、音訊/影片長度、以 N 採樣/秒的速度生成帶有燒錄時間碼的聯絡表參見下方 QA 章節

格式:16x9 1920x1080、9x16 1080x1920、4x5 1080x1350、1x1 1080x1080。幀預設以 JPEG q95 的品質傳送給 ffmpeg,在 1080p 下約為 16 fps,因此一個 40 秒的作品每種格式約需 60-75 秒。--png 品質無損,但速度約慢 4 倍。

長時間渲染:AI 的 shell 呼叫可能會超時(通常為 120 秒),並終止其子程序。請增加呼叫的超時時間,或將完整渲染以分離方式執行並輪詢日誌(以下為 Linux/macOS 範例;Windows 上請使用背景 shell 或 Start-Process):

setsid nohup bash -c 'node harness/render.mjs works/<n>/index.html --format 16x9 --out out/<n>-16x9.mp4; echo RENDER_DONE' > out/render.log 2>&1 < /dev/null & disown
tr '\r' '\n' < out/render.log | tail -2

同時,應將撰寫作品的 heredoc 與執行作品的指令分開,以避免超時導致檔案遺失。

完成標準

確切的指令在 AGENTS.md(與其他編碼工具共享的單一來源)中:每種格式皆需驗證、audio --twice、lint --all-formats 且無 FAIL、執行 review.mjs 接著 review.mjs --check 通過、渲染、QA。絕不能在閘門檢查失敗時聲稱完成,也絕不能為了通過檢查而削弱閘門或編輯工具鏈的閾值。

工作流程

  1. 簡報。 包含原始素材的內容、目標受眾、長度、格式、是否包含旁白。從原始文章中提煉關鍵論點。每個場景都必須展示正在被講述的內容。 故事優先於正確性。 工具鏈只能確保影片技術上的正確,無法讓它變得好看。在製作故事板之前,請回答簡報中的四個問題:
    • 角色是誰?他們在每個場景中的感受是什麼?
    • 哪個日常物品可以代表每個抽象概念?(原始範例使用了帶有火漆封印的信件、沙盒和一個由房屋組成的小鎮。)
    • 什麼樣的持久性 HUD 能將場景串聯在一起?通常是一個章節轉盤和一個輸入的標題。
    • 最後一幀如何與第一幀形成迴圈? 一部不具備以上任何元素的影片可以通過所有閘門檢查,但仍然會被觀眾滑過。
  2. 先有 STORYBOARD.md 再有程式碼。 每個場景一行:時間範圍 | 螢幕內容 | 動作 | 音效提示。列出每個事件時間點(這些將構成 TL 和 marks)。每隔幾秒規劃一個切換或新的動作,避免靜態拉伸。規劃轉場為推入或重疊,絕不能過渡到空白幀。除非獲得批准,否則不得以淡出至黑色結束。
  3. 時間軸模組,然後是每個場景的純 t 函數 sceneN(t),最後是一個合成器,負責繪製背景、活躍的場景以及轉場效果。
  4. 先處理最難的幀。 構建內容最密集的時刻,在每種格式下 shoot 並檢查、修復。然後再繼續開發其他部分。
  5. verify 在每種格式下執行,然後 audio --twice,最後 render 每種格式。
  6. QA(見下方),精準修復缺陷,僅重新渲染需要的部分,重新執行 QA。
  7. 交付 將 MP4 作為附件,並附上 QA 結果與已知的弱點。

包含旁白時

先處理 TTS,然後取得逐詞時間戳記(使用任何能返回逐詞時間的工具轉錄 WAV 檔案),再根據這些詞語設定 TL。場景、彈出效果和卡拉 OK 式字幕高亮都對齊詞語時間點。規則:使用方框顯示卡拉 OK 式字幕,只能有一個字幕軌,說話時對應的數字要顯示在螢幕上,並透過聽覺確認專有名詞。將旁白 WAV 嵌入為 data URI,並在 renderAudio() 內部進行混音(使用 ctx.decodeAudioData 解碼,並在時間點 0 處排程),或者使用 ffmpeg 進行混流,並在旁白下方降低配樂音量。確保整個影片中旁白的音量一致。

從單一時間軸產生多種格式(非裁剪)

  • 使用相同的 TL、相同的場景,但採用不同的佈局。計算 portrait = H > W、S = min(W,H)/1080(文字與形狀的縮放比例)、M = 0.06*min(W,H)(安全邊距)。根據長寬比進行佈局:16:9 中的行在 9:16 中變成列,樹狀圖的展開範圍變窄,依此類推。
  • 文字:使用自動調整大小以符合容器(measureText 迴圈,針對 W-2M),確保文字永遠不會被邊緣裁切。字型大小來自 S。在直向模式中,使用更大的字型並填滿高度。
  • 不要將「直向」視為單一情況。4:5 (1080x1350) 比 9:16 矮得多。如果將範例的 9:16 直列佈局直接套用到 4:5,會導致像素網格和標語被擠到畫面底部並相互重疊。應根據長寬比進行分支處理(H/W:約 0.56、1.25、1.78),或根據可用高度進行佈局,並對你將交付的每種格式都執行 shoot。
  • 在檢查邊距時,記得考慮彈出效果的過衝(easeOutBack 約 1.1 倍)和「熱門」放大效果。使用 shoot 驗證每種格式下內容最密集的畫面。

程序化音效(OfflineAudioContext)

  • new OfflineAudioContext(2, 48000*duration, 48000),建立音訊圖,await ctx.startRendering(),編碼為 16-bit PCM WAV,傳回 base64(參見範例中的 wavB64)。
  • 來自範例的模式:每個場景有一個背景和弦(失諧的正弦波/三角波對,緩慢起音,在切換處重疊)、在每個 marks 事件上有一個撥弦聲(三角波 + 2 個泛音,5 毫秒起音,指數衰減)、基於種子亂數的打字噪音爆破點擊聲、用於撞擊效果的音高下降重擊聲,以及主音軌上的 DynamicsCompressor。
  • 噪音來自種子亂數產生器,絕不使用 Math.random。
  • 音量:目標是集成式響度約 -14 LUFS,峰值低於 -1 dBFS,以符合社交媒體平台的規範。原文測量結果為 -14.3 LUFS;內附範例較為安靜,為 -17.2 LUFS,峰值 -1.4。可在頁面中透過主音量增益進行設定,使其保留在程式碼中。
  • 如需位元精確的音訊,可在純 JS 中將樣本合成為 Float32Array(使用閉合形式的包絡函數對多個振盪器進行加總),而非使用 Web Audio 節點。
  • Tone.js 的 Tone.Offline (https://tonejs.github.io/docs/14.9.17/functions/Offline.html) 是一個選項,適用於需要更豐富音樂 API 的情況。由於作品無法存取網路,因此必須內聯嵌入。

QA(審查是我們自己的責任,而非觀眾的)

自動化閘門檢查,全部基於最終的 MP4 檔案:

  • 每種格式的 verify 都必須通過。頁面錯誤即視為失敗。
  • ffprobe:檢查是否為預期尺寸,30/1 fps,nb_frames = 持續時間 x fps,音訊持續時間 = 影片持續時間。
  • blackdetect:不得有任何黑色畫面(除非有經批准的戲劇性黑色節拍)。freezedetect(> = 2 秒靜止):不得有任何靜止畫面。
  • ebur128:集成式響度和真實峰值必須在目標範圍內。不同段落之間需保持一致。

Lint(品質把關)

node harness/lint.mjs works/<n>/index.html --all-formats [--json out/lint.json] 每 0.1 秒(--step)對作品執行 seek,並從外部包裝 fillText/strokeText,因此它可以在任何遵循合約的作品上執行,無需修改。以下情況會被判定為 FAIL:

  • text-size:靜止文字小於 min(W,H) 的 2.6%(在 1080p 下為 28 px);小於 3.3%(36 px)則為 WARN。被縮小至 0.5 倍以下的文字視為總覽圖,僅發出警告。
  • safe-area:靜止文字距離邊緣小於 4%;4-6% 則為 WARN。
  • text-overlap:兩個不同的可見字串在超過 0.2 秒的時間內重疊範圍超過較小方框的 15%。短暫交錯則為 WARN。
  • static-stretch:超過 3 秒沒有任何可見變化(2 秒則為 WARN)。
  • empty-frame:任何接近均勻的幀,包括第一幀和最後一幀。
  • shots-coverage:shots 陣列中存在間隙或重疊,或某個場景持續時間少於 1 秒。
  • audio-sync:少於 70% 的 marks 在 ±40 毫秒內有對應的聲音起始點(通過標準為 90%);峰值超過 -1 dBFS 則為 WARN。
  • density:直向模式螢幕上超過 18 個詞,橫向模式超過 30 個詞(僅為 WARN)。

在你要交付的每種格式中,lint 都必須以 0 FAIL 通過,作品才算完成。lint 只會偵測繪製在作品自身 canvas 上的 fillText/strokeText 文字,因此形狀碰撞和繪製到離屏 canvas 的文字仍需透過視覺審查來確認。example/browser-10s 在三種格式中皆通過檢查;example/browsers-40s 在此閘門建立之前製作,因此無法通過(標籤尺寸為 17-28 px,其中一個標籤在 9:16 格式中被裁切,文字)

…

[為翻譯而截斷]

相關技能