Communitygithub.com

motionkit

透過程式碼製作動態圖像影片的引擎 - 複製參考影片的運動(節奏、佈局、動作),搭配你自己的文字和藝術,或從 44 種現成動作中為影片配上合成配樂。它還可製作透明動畫標題與疊加層,並透過 EditMax 的 MCP 工具將其放置於時間軸上。

motionkit 是什麼?

motionkit 是一個以程式碼為核心的動態圖像製作引擎,專為開發者與 AI 代理(如 Claude Code、Cursor)設計。它能讓使用者透過撰寫 JavaScript 檔案來生成 1920x1080、30 fps 的影片,並自動透過 ffmpeg 編碼為高畫質 MP4。此技能支援多種平台(macOS 原生支援,Linux/Windows 需注意相容性),核心工作流程包含:撰寫「clip」定義影片中的每個動作(move)、編排場景(scene)與時間軸(timeline),最後透過 `build.ts` 一鍵完成渲染、音效合成與同步檢查。 motionkit 的一大亮點是「模仿運動」:使用者可以參考現有影片的動作(timing、layout、movement),並置換自己的文字與圖像。此外,它內建 44 種預製動作(如滑入、彈跳、打字效果等),並能自動產生合成配樂。其進階功能包含:支援真實音樂檔案(透過 beats.py 分析節奏並對齊剪接點)、為產品發表影片設計的舞台系統(包含 3D 鏡頭、玻璃面板、程式碼終端機等視覺元素),以及與 EditMax 剪輯軟體的整合,可產出帶有透明背景的 ProRes 4444 影片作為疊加標題或轉場。 對開發者而言,motionkit 的核心優勢在於將影片製作轉化為可版本控管、可測試的程式碼。每個「move」都是一個獨立的 clip 物件,具有 `draw()`、`on()`、`t()` 等方法,並可透過 `MOVES` 全域物件存取所有品牌設定、顏色與字體。內建的 lint 檢查(閱讀時間、聲音同步)確保輸出品質。使用者只需專注於 `renderFrame(f)` 函數的邏輯,無需手動處理幀序列與編碼細節。

相容平台✓Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/SkidGod4444/motionkit/tree/HEAD/skills/motionkit

在你喜歡的 AI 中提問

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

說明文件

motionkit 是做什麼的?

一部電影就是一個網頁加上一個腳本,腳本中的 renderFrame(f) 繪製第 f 幀(每秒 30 幀,1920x1080),除此之外不做任何事。隱藏的 Chrome 逐步執行各幀,ffmpeg 負責編碼(MP4 會加倍至每秒 60 幀,高畫質,大約每秒 4 到 5 MB),配樂則是合成產生的。

檔案位置

  • 引擎:此檔案旁邊的 engine/ 資料夾(包含動作、渲染器、音效、檢查、字型)。將其路徑設為 $E 並使用完整路徑。從中讀取內容,但切勿將電影放在裡面(設定時會在那裡加入它自己的 node_modules,這是預期行為)。
  • 工作資料夾:使用者正在工作的資料夾。電影(<name>.html、<name>.js)、assets/、ref/ 和 out-<name>/ 都會放在這裡,且所有指令都從此目錄執行。一個網頁會自行在引擎中找到 lib.js、moves.js 和 fonts/。如果工作資料夾是一個 git 儲存庫,請將 out-*/ 和 ref/ 加入其 .gitignore。

首先:設定(每台機器一次)

  1. 執行 sh $E/setup.sh。它只會檢查:bun、ffmpeg(含 ffprobe)、python3、uv、Chromium 瀏覽器(Chrome、Chromium 或 Edge;可用 CHROME_PATH 覆蓋),以及引擎的一個套件。
  2. 如果顯示 Ready,請繼續。如果只有 engine package 遺失(這是全新安裝的正常情況),請直接執行 sh $E/setup.sh --install,無需詢問:它只會將一個套件放入技能自己的資料夾中。如果某個工具或瀏覽器顯示為 MISSING,則向使用者顯示該清單以及它所列印的安裝指令,並在安裝軟體到他們的機器前詢問一次。
  3. 如果同意,執行 sh $E/setup.sh --install(macOS 上為 Homebrew,bun 和 uv 使用官方安裝程式),然後再次執行檢查。標示為 run this yourself 的行需要使用者的密碼(Linux 上的 sudo)或手動下載:切勿執行這些行;提供使用者確切的指令並等待。如果安裝失敗,提供使用者一個他們可以自行執行的指令。
  4. 證明它可以運作:從工作資料夾執行 bun $E/build.ts hello 並確認 out-hello/hello.mp4 存在。

僅在 macOS 上測試過;如果使用者使用 Linux 或 Windows,請告知。

可複製的範例:$E/hello.js(一部在節拍網格上的電影,所有聲音來自片段,場景由程式碼繪製)、$E/title.js(一個可疊加在素材上的透明標題)、$E/moves-demo.js(每種動作各一次)。

建構電影:先有片段

將每個動作的一次使用宣告為一個片段。圖像、聲音和閱讀時間檢查都來自那一行。

const M = window.MOVES, B = M.brand, G = M.grid(120);          // 120 bpm:一拍是 15 幀,一個小節是 60 幀
const K = { line: M.clip("words", G.bar(1) + 2, 10, { text: "Copy the motion.", x: 960, y: 590, size: 110, fam: B.display, stretch: B.wide, weight: 600, color: B.paper, until: G.bar(2) }) };
// 在一個場景中: K.line.draw(X, f)          K.line.on(f) 在其應顯示時為 true
window.timeline = () => ({ fps: 30, total: TOTAL, bpm: G.bpm, sections: [[0, "intro"], [4, "build"], [8, "drop"], [24, "outro"]], cues: M.cues(), captions: M.captions() });

對於發表或產品電影,請先閱讀下方的「發表與產品電影」。使用 bun $E/new.ts <name> 開始一部電影:它會在工作資料夾中寫入 <name>.html 和 <name>.js,內容從 hello 範例複製而來。改寫片段、場景和文案。然後 bun $E/build.ts <name> 會依序執行:時間軸、配樂(sound.py)、聲音檢查、閱讀時間檢查(lint.py)、渲染、合併、同步檢查(sync.py),並寫出 out-<name>/<name>.mp4。沒有 bpm 的時間軸會從 audio.py 獲得一個更簡單的配樂(僅有剪輯)。時間軸開關:sound: "fx"(僅有動作音效,無音樂)、sound: "none"、alpha: true(透明背景,輸出 .mov;請參閱 EditMax 章節)。

電影腳本的結構

$E/hello.js 是範本;請閱讀一次。每個部分的意義:

  • 來自 lib.js 的全域變數:W、H(1920, 1080)、X(畫布 context)、clamp、lerp、TAU、hash、noise、txt(ctx, text, x, y, size, weight, color, fam, italic, align)、rr(圓角矩形)、vignette(strength, innerRadius)。
  • M.clip(move, start, length, options):start 和 length 是以幀為單位;length 是動作播放所需的時間,而不是它停留的時間。它回傳 { draw(ctx, f), t(f) (0..1 貫穿整個動作), on(f), o (即 options) }。在其長度之後,一個片段會繼續繪製其完成狀態:它不會自己隱藏。透過結束場景來隱藏它,或使用 if (K.x.on(f)),其中 on 從 start 開始為 true,直到 until(沒有 until 則永遠為 true)。要在轉場的 from 內部顯示一個已完成的文字動作,請在 t = 1 時繪製它:M.words(ctx, 1, K.line.o)。一次性效果(burst、glitchCut)也需要 K.x.t(f) < 1。
  • 在片段內部呼叫形狀:轉場接受 options 中的 from 和 to;包裝器(punch、shake、sweep、smear)接受 draw。其他所有項目(pathDraw、burst、bars、chart、callout、logoDraw、bounce、ticker)只接受 options。mesh 和 parallax 是背景:直接呼叫它們,M.mesh(ctx, f / 240, { colors }),其中 t 只是一個緩慢增長的數字。
  • 文字選項:x 是行的中心(align: "center" 是預設值),y 是基線。stretch: B.wide 提供寬版裁切,stretch: null 將其關閉。沒有東西會為你測量或自動換行文字:在 150 寬度的字體大小下,13 個字元約佔 1340 像素,因此請渲染一個靜止畫面並檢查每一行是否都位於框架內且有邊距。
  • 場景表:[first frame, draw(f), drawAfterFinish(f)]。renderFrame 會重置 context 狀態(保留那行),繪製當前場景,然後是顆粒效果和暗角,接著是第三個槽位,用於任何必須保持清晰的內容(如標誌)。
  • window.ready:渲染器會等待的一個 promise:Promise.all([M.fonts(), M.levels("out-<name>/levels.json"), M.load({ ... })])。M.levels 僅在需要 M.eq 時才需要,且在檔案存在之前是安全的。
  • 時間軸:sections 是 [beat, name];一個短片可以只使用 [[0, "intro"], [8, "drop"], [16, "outro"]],並且每個 drop 的開始會獲得半拍靜音(在那裡保持或 mute: 片段)。sync 列出剪輯點和低音重擊必須同時發生的幀:從 drop 開始使用每個場景的第一幀。「變化」是根據影片幀與幀之間的通常差異來判斷的,因此一個恰好完成在同步幀上的轉場在那裡不會留下任何變化,並且檢查會印出 NO CHANGE:添加一個短暫閃爍或在那個幀上開始下一個動作。cuts 是每個場景的開始。
  • 顏色:從 M.brand(B)讀取所有內容。M.rgba(B.ink, 0.7) 以指定不透明度給出一個顏色,M.mix(a, b, t) 給出兩個顏色之間的一個顏色。M.setBrand 會就地更改 M.brand;在任何片段被宣告之前呼叫它,並同時設定 accentOnDark(用於深色背景的強調色;它不跟隨 accent)。對於沒有寬版裁切的字型,設定 wide: null。
  • 長度:閱讀時間設定了最小值:一個 3 個字的行在出現後大約需要 1.3 秒,因此在 120 bpm 下,每個小節一行。配樂大約在最後一幀之後半秒結束並在那裡被截斷;以一個 outro 部分結束,使剪輯落在其尾部。
  • 建構結果:即使檢查失敗,build.ts 也會以代碼 0 退出;失敗會在其上方細節之後列印為 WARNING: 行。將每個警告視為失敗:讀取輸出(過濾掉 frame n/total 進度行),修正,重新建構直到沒有任何警告。靜止畫面會寫入為 out-<name>/fNNNN.jpg;請閱讀它們。bun $E/pure.ts <name> 確認幀在單獨渲染和在其他幀之後渲染時結果相同。
  • sound.py 需要 numpy,並且總是透過 uv run 執行;其他 Python 腳本僅使用標準函式庫。

發表與產品電影:展示產品

一部由大字和背景組成的電影會顯得很基本。有效的發表電影會將產品保持在螢幕上,處於一個具有深度和緩慢移動鏡頭的統一視覺世界中,使用簡短精簡的文字,並將鏡頭保持 2 到 6 秒(幾個小節),而不是每拍一個剪輯。對於發表或產品電影,請勿從 hello 開始:

  1. 首先決定外觀:一個背景世界,一個強調色,一種字體家族。用兩行文字告知使用者。
  2. 對 6 到 8 個真實事物的鏡頭進行分鏡:產品真實的螢幕或輸出、真實的指令、真實的程式碼、真實的結果。切勿繪製產品沒有的介面,切勿發明數字,切勿使用其他公司的標誌。說明文字是小的標籤(txt,等寬字體,左下角),而不是標題。
  3. 首先建構一個主要鏡頭,渲染靜止畫面,並在建構其餘部分之前將其與真實的發表影片進行比較。

網頁必須在 moves.js 之後載入 stage.js(由 new.ts 建立的頁面已經這樣做)。以 M.frame(); M.reset(X); 開始 renderFrame。舞台元件(全部在 MOVES 上):

元件說明
backdrop(ctx, f, { base, lights[], streak })近乎黑色,帶有緩慢的光池:所有事物所在的背景世界
glass(ctx, x, y, w, h, { r, top, bottom, rim, shadow })一個深色圓角面板,帶有光暈邊緣和柔和陰影
camera(ctx, cam, draw) 搭配 cam(a, b, t, ease)觀察更大場景中的一個點:{ x, y, zoom, rot };從極近距離開始並拉遠,或緩慢漂移
tilt(ctx, draw, { depth, focus, blur, fog, top, height, width, cx, roll })將全幀場景繪製為一個傾斜的平面,遠端邊緣較小、較暗且失焦
tiles(ctx, f, { cells: [{ draw(ctx, t), label, bg, len, hold }], cols, x, y, w, gap })一個小螢幕網格,每個都在即時播放一個動作(切勿在 tile 內部放置轉場)
code(ctx, { lines[], x, y, size, lead, hot, shown })JavaScript,帶有低調的語法顏色和一行高亮

舞台元件本身不產生任何聲音:在時間軸上手動添加提示,{ f, kind, gain },種類如 type、tick、pop、ding。

配合真實歌曲剪輯

  1. 使用者提供音樂檔案,且必須有使用它的權利;請告知使用者,並將授權要求的版權資訊放在結尾卡片上。切勿從串流網站下載歌曲。
  2. uv run $E/beats.py <file> 會列印速度和每個小節的音量。選擇一個從一個小節開始的片段,並在幾小節後有一個段落變化,然後執行 uv run $E/beats.py <file> --from <seconds> --len <seconds> --out beats.json,它會寫入 beats.json 和 beats.js。
  3. 在網頁中於電影腳本之前載入 beats.js。將每個場景的開始寫為一個節拍編號:const b = (n) => window.BEATS.frames[n]。在音軌變化的位置,在小節(每 4 拍)上進行剪輯。
  4. 在時間軸中設定 music: { file, from }、sound: "fx"(動作本身的聲音會保持,但音量較低,在音樂之下)或 "none"、一個 bpm,以及 sync: [真實剪輯的幀]。建構過程會將音軌混音至 -16 LUFS,淡化其結尾,並檢查每個剪輯是否在一幀內有一個低音重擊。不要僅僅為了通過檢查而添加閃爍。

供 EditMax 使用的動作(影片編輯器時間軸上的片段)

當使用者在 EditMax 中編輯並希望在剪輯中加入一個動作時使用此功能:覆蓋在素材上的動畫標題、開頭或結尾卡片、鏡頭之間的動態文字節拍。它需要在同一個代理工作階段中有兩樣東西:一個 Shell(用於渲染)和 EditMax 的 MCP 工具(inspect_timeline、import_media、...;從 EditMax 的代理標籤頁設定)。EditMax 自己的應用程式內聊天拒絕 shell 指令,因此無法執行此技能:請告知使用者並告訴他們連接一個代理,例如 Claude Code。如果 EditMax 工具遺失或沒有回應,無論如何都渲染片段並提供使用者檔案路徑以供拖入。

1. 選擇方法。 當 EditMax 自己的工具能涵蓋該動作時,優先使用它們,因為文字在應用程式中仍可編輯:

想要的使用
一個淡入、滑入、彈出、打字或擦入的標題或下方第三行add_text / add_lower_third + set_text_animation
兩個片段之間的溶解、推入、滑動、擦除或光圈轉場set_transition
一個移動、縮放或淡化的片段set_keyframe、set_clip_transform、set_clip_fades
其他任何東西:重擊、擾亂、翻牌、文字輪盤、劃掉、高亮、路徑或標誌繪製、圖表、計數器、爆發、多動作序列、完整的開頭或結尾使用此技能渲染一個片段

2. 讀取專案。 inspect_timeline:注意幀大小、幀率、軌道以及動作發生的時間。此引擎渲染 1920x1080,每秒 30 幀。其他尺寸的 16:9 專案可以乾淨地縮放它。對於垂直或方形專案,直接說明片段將被加上信箱黑邊,或者必須使用 set_clip_transform 放大並裁切,並且原生標題可能更適合。

3. 渲染。

  • 疊加層(位於素材之上):bun $E/new.ts <name> --overlay,以 $E/title.js 為模型。時間軸有 alpha: true 和 sound: "fx";建構過程會寫出 out-<name>/<name>.mov(帶有透明背景的 ProRes 4444)。規則:清除幀(X.clearRect),切勿填充它;不使用 grain、vignette、mesh、parallax 或全幀轉場;在文字下方放置一個柔和陰影或底板,使其在任何素材上都可讀;將文字保持在幀中間的 90% 內;在結尾淡出。
  • 全幀(開頭、結尾、鏡頭之間的卡片):bun $E/new.ts <name>,以 $E/hello.js 為模型;建構過程會寫出 out-<name>/<name>.mp4。
  • 音效(在時間軸中設定 sound:):對完整的配樂(單獨存在的開頭或結尾)省略它;對於僅包含動作聲音(任何覆蓋在素材、語音或使用者音樂上的內容)使用 "fx";對靜音使用 "none"。如果編輯中已有音樂,請詢問使用者想要哪一種。
  • 建構,修正每個 WARNING,並在繼續之前閱讀靜止畫面。

4. 將檔案放在持久的位置。 EditMax 會引用已匯入的檔案,不會複製它們。將完成的檔案從 out-<name>/ 複製到使用者保留的資料夾(詢問一次;建議 ~/Movies/Motions/),並從那裡匯入。

5. 放置它而不移動使用者的剪輯。 使用時間的 insert_clip 是一個撕裂...

相關技能