Communitygithub.com

kosta4a/stopmotion-video

為音樂節拍剪輯的停格動畫風格影片,預設使用 Remotion 製作。

stopmotion-video 是什麼?

這個技能專為製作或審查停格動畫風格、低幀率、與音樂同步的影片而設計。它提供了一套完整的規則與輔助工具,基於 React 的影片框架 Remotion,確保每一幀都是精確的 React 組件,因此每個動作都能精準落在指定的幀數上。技能核心是將歌曲的八分音符作為一個「姿勢」(pose),所有畫面變換都以此為單位同步進行,刻意保持低幀率以呈現停格動畫的韻律感。它涵蓋了從測量歌曲 BPM、設定時鐘、規劃頁面佈局、到安排點擊音效與轉場的完整決策流程。此外,還包含了在網頁上即時播放的 live.md 支援,讓相同規則也能應用於瀏覽器環境。對於開發者而言,這提供了一個結構化、可重複的方法來製作與音樂節拍完美同步的動態影片,特別適合音樂影片、宣傳片或任何需要與節奏緊密結合的動畫專案。

相容平台~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/kosta4a/stopmotion-video/tree/main/skills/stopmotion-video

在你喜歡的 AI 中提問

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

說明文件

停格動畫影片 (Stop-motion video)

使用此技能製作的影片,動作就像配合歌曲拍攝的停格動畫。每個姿勢(pose)維持一個八分音符的長度,然後所有事物一起改變。頁面在小節線處進入。每次點擊聲都落在鼓點上,重大的變化落在重大的大鼓聲上。幀率刻意保持在低位。當某個動作看起來不流暢時,會更好地調整其姿勢間距;節拍感絕不會被平滑掉。

這些規則源自製作一部 47 秒的宣傳影片,該影片配合一首 169.825 BPM 的鼓打貝斯 (Drum and Bass) 曲目,經過數百輪筆記反覆調整而成。影片以 30fps 運行,每個姿勢維持一個八分音符,即每秒 5.66 個姿勢,每個姿勢佔用 5 或 6 幀。此處的數值即為該影片的設定,而 live.md 中的則是那些在網頁上配合節拍器播放影片場景的數值。當某條規則可推廣應用於其他情況時,文字中會特別說明。

詞彙表

  • 姿勢 (Pose):一個靜止的畫面,對應歌曲的一個八分音符。程式碼中稱之為一個「拍」(STEP_MS, useBeat)。
  • 歌曲拍 (Song beat):BPM 所計數的拍子,即一個四分音符。它等於兩個姿勢的長度,並會得到一個節拍器點擊聲。
  • 小節 (Bar):四個歌曲拍,即八個姿勢。在鼓打貝斯中,大鼓落在姿勢 0,小鼓落在姿勢 2 和 6(從 0 開始計數)。
  • 敲擊 (Hit):歌曲中的一個鼓聲。重大大鼓 (Big kick) 是指開啟一個樂句的大鼓(在該影片的曲目中為每隔一小節出現一次)。
  • 創作毫秒 (Authored ms):程式碼中的時間單位。它們是以 200ms 的姿勢為基礎撰寫,並以 SPEED 倍速播放 (SPEED = TEMPO / 150)。
  • 時間戳記 (Timestamps) 的格式為 m:ss.ff,其中 ff 是幀數計數。這是 Remotion Studio 顯示時間的方式,也是筆記送達時的格式。

操作姿態

  • 刻意保持低幀率。 絕不能為了讓動作流暢而平滑掉節拍。相反地,應該更好地調整動作的姿勢間距。
  • 所有事物都維持一個姿勢的長度然後一起改變,轉場也不例外。沒有東西有自己的時鐘:嵌入的動畫或滾動頁面也必須跟隨姿勢的步伐。打字是唯一的例外:它比節拍更快,就像真實打字一樣,從其姿勢開始。
  • 歌曲就是時鐘。 在分鏡之前先測量它。將頁面放在小節線上,並將動作安排在敲擊點上。
  • 整部影片共用一個時鐘。 根據影片的幀數來計數姿勢,絕不根據某個場景自身的幀數。
  • 以整個小節為單位移動。 以整個小節為單位來改變頁面長度,或者在下一個小節線之前將姿勢補回到別處。否則其後的所有內容都會與歌曲脫節。
  • 沒有無意義的姿勢。 一旦某個頁面上的動作完成,只需維持足夠觀眾理解的長度即可。
  • 少數幾種變化類型、同一方向、無淡入淡出。
  • 透過測量來檢查。 使用每姿勢一張靜止圖、在渲染音訊中測量時間、以及使用節拍器剪輯。不要用眼睛判斷同步性。

工具:預設為 Remotion

預設提議使用 Remotion 來製作影片,除非專案已經以其他方式製作影片。在分鏡之前說明理由,並且只有在使用者要求其他工具時才更改。若是在網頁上,則改為即時播放 (live.md);若在動畫工具中,規則仍然適用,但輔助工具不適用。

  • 為什麼它合適。 每一幀都是一個根據幀數計算的 React 組件,所以一個姿勢能精準落在它所在的任何幀上,並且每次渲染結果都相同。渲染過程會跨多個分頁、不按順序繪製幀,這只有由幀驅動的影片才能承受。assets/remotion/ 中的輔助工具是為它編寫的,其 Studio 顯示時間的格式 m:ss.ff 也正是筆記送達的格式。
  • 設定方式。 npx create-video@latest 並選擇空白範本。將 remotion 和所有 @remotion/* 套件鎖定在同一個精確版本(該影片使用 4.0.532),並將 TypeScript 保持在 5.x 版本 (clock.md §7)。一個合成 (composition),1920×1080 解析度,30fps。將 beat.ts 和 transitions.tsx 複製到 src/ 目錄下,並設定 TEMPO。
  • 配置方式。 在 remotion.config.ts 中:JPEG 幀品質設為 95(預設值 80 會使純色背景和細字體的邊緣變得粗糙),BT.709 色彩空間(讓播放器顯示與瀏覽器相同的顏色),並開啟覆蓋寫入。
  • 預覽與渲染。 使用 npx remotion studio 進行工作;使用 npx remotion render <Comp> out/<name>.mp4 渲染 1080p 影片,加上 --scale=2 參數可輸出 4K;使用 --frames=<first>-<last> 或 npx remotion still <Comp> --frame=<n> 來檢查某個片段或某個姿勢。

決策順序

從上到下進行。每個步驟都會限制下一步。

  1. 提議工具。 Remotion,除非專案已經以其他方式製作影片;然後在執行其他任何操作之前,按照上述方式進行設定。
  2. 測量歌曲。 從歌曲的起音 (onsets) 找出節奏 (tempo),並確認該節奏網格在你使用的片段內保持一致。標記出小節和樂句、鼓聲消失和回來的地方、以及重大大鼓的位置。將曲目片段精確地裁剪到樣本起點,即樂句第一個大鼓前的一個頭髮絲(23ms)處。參見 song.md。
  3. 設定時鐘。 一個姿勢就是歌曲的一個八分音符。以 200ms 的姿勢配合 SPEED = TEMPO / 150 進行創作,以 30fps 渲染,並根據整部影片的幀數來計數姿勢。參見 clock.md。
  4. 將頁面放在小節線上。 以姿勢為單位撰寫分鏡表。每個頁面包含整數個姿勢,並從一個小節線開始。在開始製作任何動畫之前,先將歌曲的事件映射到姿勢上。
  5. 安排敲擊點。 程式碼以每八分音符 2 到 4 個字元的不均勻區塊輸入;標題和按住退格鍵的動作會比其姿勢的節拍更快。點擊聲和爆裂聲放在小鼓上;著陸和新頁面放在強拍上;重大變化放在重大大鼓上。建構過程在音樂安靜的段落進行,而著陸則在鼓聲回來時發生。參見 hits.md。
  6. 間隔姿勢。 為每個姿勢選擇曲線。一個動作的第一個姿勢就是它的第一步,每個姿勢都讓動作有明顯的移動。事物是「著陸」而不是「爬行」。參見 poses.md。
  7. 更換頁面。 使用節拍上的硬切、地面的剪切、像紙張一樣覆蓋的頁面、攝影機進入某種顏色、從內部填充、穿過形狀的開口、或像素塊。絕不使用淡入淡出。參見 hits.md。
  8. 設定字體。 字體間距隨尺寸增大而收緊,行距也隨之收緊。每個詞在每個姿勢下都必須與其背景達到至少 4.5:1 的對比度。標題是打字輸入的,而非淡入顯示。參見 type.md。
  9. 製作音效。 每個點擊聲都落在歌曲本身的敲擊點上,並對齊到樣本;所有點擊聲都烘焙到一個與歌曲同時開始的音軌中。在點擊聲響起時將音樂音量降低,並保留一個節拍器剪輯用於檢查。參見 sound.md。
  10. 決定何時跳過一個節拍。 段落中斷 (Drop)、降低音量 (Duck)、保持 (Hold)、對齊 (Snap)、剪掉整個小節、在一個歌曲拍上結束。參見 skipping.md。
  11. 檢查。 製作一張連續劇照 (contact sheet),測量同步性,並觀看節拍器剪輯。以映射到姿勢的時間戳記形式記錄筆記。參見 checking.md。
  12. 在瀏覽器中即時播放? 同樣的規則,但配合節拍器:一個基於頁面時間的時鐘、畫面比聲音提前 25ms、聲音提前在音訊時鐘上排程、並且在沒有歌曲時每次變化都伴隨一個點擊聲。參見 live.md。

規則一覽

時鐘

項目規則
幀率30fps,刻意為之。一個流暢的 60fps 變體可以共用分鏡表 (clock.md §8)
姿勢歌曲的八分音符:在 169.825 BPM 下為 176.65ms,每秒 5.66 個,在 30fps 下佔 5 或 6 幀
創作所有時間皆以 200ms 姿勢為基礎的 ms 表示;SPEED = TEMPO / 150;ms() 轉換為幀數,seconds() 用於嵌入式時鐘
姿勢 n 的第一幀ceil(n × 每姿勢幀數),基於整部影片的幀數 (beatFrame)
頁面包含整數個姿勢,從一個小節線開始(姿勢號可被 8 整除)
更改長度以整個小節為單位,或者在下一個小節線之前將姿勢補回
Studio 拖曳拖曳過的音軌會寫入一個 from 偏移量,導致畫面與聲音錯位:請移除它

間距 (Spacing)

移動類型曲線姿勢分佈百分比
進出(鏡頭移動、掃過、底線)easeInOutSine在六個姿勢上分佈為 7, 18, 25, 25, 18, 7%
從畫面外進入ease-out否則它緩慢的起始部分會在視野外浪費掉
頁面覆蓋另一頁面easeOutSine在一個三姿勢的轉場中分佈為 38, 70, 92%
縮放、線條變粗、開口變大每次增長相同倍數縮放時,目標沿直線朝向中心移動
非少量姿勢三次方 in-out(3, 12, 35, 35, 12, 3:中間躍進);強 ease-out(著陸在它的第一個姿勢:作為三姿勢轉場為 78, 97, 100%,作為使用 moved() 的移動為 88, 99, 100%)

節拍上的事物

項目規則
點擊聲按壓一個姿勢,落在大鼓或小鼓上;游標在最後一次點擊之後的那個姿勢消失
游標打字時為實心,之後以兩個姿勢開啟、兩個姿勢關閉的方式閃爍
旋轉器每個姿勢最多旋轉四分之一圈;超過半圈會讓人感覺是反轉
滾動標記以其行進距離除以半徑的角度旋轉,最終直立停止,使用 easeOutSine 曲線
計數器每個姿勢顯示一個數字,使用強 ease-out、表格數字 (tabular figures),整數值停留兩個姿勢
標題從其姿勢開始,比節拍更快地打出:每三個幀打出一個單詞;長行每 25ms(創作時間)打出一個字母
程式碼以每個姿勢 2 到 4 個字元的不均勻區塊輸入
按住退格鍵一次連續動作,每 25ms(創作時間)刪除一個字母,換行符號也是
鏡頭升起升起,在頂部死寂維持兩個姿勢,然後下降
從畫面外出現在移動完成之前不被繪製
手動放置最多偏移該姿勢移動量的十分之一,一旦停止則完全不偏移;最終定位在幾像素範圍內向下

敲擊點 (Hits)

在歌曲中在畫面中
八分音符程式碼以區塊輸入,移動的步驟
小鼓點擊聲,爆裂聲
強拍著陸,新頁面,頁面的第一個動作
重大大鼓重大變化:頁面著陸,最後一頁完整展示,最終點擊聲
鼓聲消失建構過程
鼓聲回來著陸

點擊聲必須在節拍上。轉場可以偏離節拍。

音效 (Sound)

項目規則
點擊聲放置位置對齊到樣本,落在歌曲的敲擊點上(該影片的敲擊點比其姿勢晚了 25ms)
點擊聲檔案在 tick 聲前有 40ms 的靜默,這樣裁剪到樣本時才不會吃掉它
點擊聲音軌所有點擊聲烘焙到一個與歌曲同時開始的音軌中
音量降低 (Ducking)在重要的點擊聲周圍完全靜音;在其他點擊聲下,音量降為 45% 持續 160ms;淡入淡出時間為 10ms
節拍器在每個歌曲拍的敲擊點上播放一個點擊聲,小節的第一拍音調更高,音樂音量設為 20%;絕不包含在最終匯出中

字體 (Type)

尺寸字間距 (Tracking)行距 (Leading)
小於 20px0;等寬大寫字母 +2%1.4–1.5
20px 及以上−1%約 1.4
32px 及以上−2%約 1.25
56px 及以上−3%1.1–1.2;兩行顯示標題可降至 0.9

每一個詞在每個姿勢下都必須至少達到 4.5:1 的背景對比度。預設使用同一種字體家族(範例中使用 Geist 和 Geist Mono),黑底白字,如同觸覺設計(tactile-design)的做法;該影片本身則是用一種展示字體搭配其文字字體,並將頁面設定在純色背景上。計數器使用表格數字 (tabular figures)。標題是打字輸入的,絕不淡入顯示。

即時,在瀏覽器中 (Live, in a browser)

項目規則
時鐘beat = anchorBeat + elapsed s × BPM / 60 × 2 基於 performance.now();在節奏變化時重新錨定到當前時間;每個場景都是 beats 的純函數
畫面讀取 beats 的時間提前 25ms;每個場景在動畫幀回調內提交 (flushSync)
聲音每 25ms 一個計時器,在音訊時鐘上排程接下來的 120ms (getOutputTimestamp());時間視窗以音訊時間測量,並加上輸出延遲
可視範圍僅有螢幕上顯示的內容進行動畫;範例中螢幕上顯示最多的內容就是聽到的內容
開始在載入時嘗試,然後在第一次按下或按鍵時再次嘗試;啟動聲音的按鍵不會靜音(600ms);記住靜音狀態
無歌曲每次變化都伴隨一個點擊聲:移動可以踩在八分音符上,但必須在點擊聲上開始和結束;移除該點擊聲本身的節拍器點擊
節拍器一個安靜的木質敲擊聲(880Hz 持續 50ms,小節第一拍為 1320Hz),沒有音樂時不發出重擊聲,全部通過一個限制器 (limiter)
暫停在最接近的點擊聲處暫停:播放到該點擊聲為止,或者在 160ms 內逐漸恢復
減少動畫 (Reduced motion)一個能展示概念的靜止姿勢,僅在按下聲音按鈕後才移動
字體單詞對比度 4.5:1,有意義的標記 3:1,縮放場景的最小單詞渲染後為 9px 或更大

何時跳過一個節拍 (When to skip a beat)

網格是嚴格的,因此打破它是個有意識的決定。以下的每一種破格方式在 skipping.md 中都有其原因。

  • 在重要的點擊聲周圍讓音樂靜音。 從點擊聲前 50ms 到下一個小節線完全靜音,帶有 10ms 的淡入淡出。這個點擊聲被單獨聽到,節拍隨著後續內容回來。
  • 在其他點擊聲下降低音樂音量。 將音樂音量降低到 45% 持續 160ms,讓點擊聲的重擊有空間。
  • 在下一件事開始前,單獨維持一個結果 一個歌曲拍,例如複製後的打勾勾動畫。
  • 在升起頂端維持兩個相同的姿勢。
  • 在整數上維持兩個姿勢 然後再繼續。
  • 將落在節拍之間的筆記對齊 到最接近的重拍。
  • 剪掉整個小節, 即使筆記要求更少。說明這樣做的代價。
  • 在一個歌曲拍上結束。 最後一頁維持約 2 秒,音樂在最後一個姿勢上淡出。否則在動作結束後立即結束。

絕不允許出貨 (Never ship)

  • 為了掩蓋間距不良的移動而將動作平滑化以脫離節拍
  • 場景從自己的第一幀開始計數姿勢
  • 沒有在旁邊寫下理由就從非小節線開始的頁面
  • 不是整個小節且未在下一個小節線前補回的長度變化
  • 合成樹中遺留的 Studio from 偏移量
  • 不在大鼓或小鼓上的點擊聲,或者點擊聲是放在某個幀上而非對齊到樣本
  • 短點擊聲從自己的幀開始而非使用一個烘焙好的音軌
  • 一個移動的第一個姿勢就是它離開的位置,或者在最後幾像素中緩慢爬行
  • 從畫面外出現的東西在移動之前就被繪製出來
  • 旋轉器或輪子每個姿勢旋轉超過半圈
  • 淡入淡出,或在飽和背景之間交叉淡入淡出
  • 背景變化快於每秒三次閃爍
  • 在任何姿勢下,任何字詞與其背景的對比度低於 4.5:1
  • [ ...(翻譯截斷)]

相關技能