cavalry について
JavaScript スクリプトと Cavalry Bridge を使って、アニメーションソフトウェア Cavalry を操作するスキルです。API のパターンとヘルパースクリプトでシーンを作成・確認・レンダリングし、プレビューを反復できます。
主な機能
- 01JavaScript による Cavalry 操作
- 02API ヘルパーでのシーン作成
- 03キーフレームとイージングの設定
- 04レンダーとシーンデータの確認
- 05PNG フレーム列のレンダリング
活用シーン
- 01アニメーションシーンの作成
- 02テキストや図形のアニメーション
- 03コンポジションのプレビューと修正
- 04フレーム列のレンダリング
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jablonkai/ skills --skill cavalryインストール後、Claude Code で/cavalryを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/api-reference.mdreferences/cavalry-types/deformer.d.tsemitter.d.tsLICENSEmodifier.d.tsplugin.d.tsREADME.mdrender.d.tsscript.d.tsshape.d.tsutility.d.tsreferences/cavalry-types/namespaces/api.d.tscavalry.d.tsconsole.d.tsctx.d.tsdef.d.tsinstall.d.tsplugin.d.tsrender.d.tsui.d.tsscripts/cavalry-bridge.jscavalry-helpers.jscavalry-send.sh詳細
Cavalry Control
Cavalry(/Applications/Cavalry.app)は JavaScript でスクリプトを記述できます(2.6 以降は V8 12.4 なので ES2024 に対応しており、let/const、アロー関数、Set メソッドがすべて動作します。api.*/ui.*/cavalry.* モジュールを使用します)。Cavalry Bridge を介して操作します。これは Cavalry 内で動作する小さなスクリプト UI で、127.0.0.1:8731 に POST された内容を実行します。以下の内容はすべて、実際のプロジェクトで Cavalry 2.7.2(2026 年 5 月時点の最新リリース)を使って検証済みです。その他の API の詳細についてはリファレンスファイルを参照してください。
2.7 以降、Cavalry は Canva の傘下にあります。サインインには Canva アカウントを使用し、以前は Professional 向けだった機能はすべてのユーザーに無料で提供されます。そのため、ライセンスの区分を前提に計画しないでください。唯一の注意点は、CLI が 2.7.x に同梱されていないことです(「レンダリング」を参照)。
- references/api-reference.md — モジュール(api、cavalry、ui、WebClient/WebServer、レンダースクリプト、CLI)の要約リファレンスです。以下のチートシートにない機能が必要な場合に参照してください。
- references/cavalry-types/ — JSDoc の例を含む TypeScript 定義として収録された完全な API です。リファレンスに記載のない関数は、こちらを grep してください。
- scripts/cavalry-bridge.js — Cavalry にインストールするブリッジです。
- scripts/cavalry-helpers.js —
setAttrs/keyAttrs(エラーを黙って無視せず、明確に通知しながら設定やキーフレーム化を行います)、newComp/useComp/findComp、splitLetters(文字単位のテキスト)、renderFrames、writeJSONを提供します。各ビルドスクリプトの先頭でapi.load("<abs path>/scripts/cavalry-helpers.js")を実行して読み込んでください。 - scripts/cavalry-send.sh — JS ファイル(または
-c 'inline code')をブリッジに送信し、完了まで待機します。失敗時はゼロ以外の終了コードを返し、JS エラーを表示します。--pingを指定するとブリッジへの接続を確認し、ブリッジと Cavalry のバージョンを報告します。
制御ループ
- ユーザーがブリッジを起動します(セッションごとに一度):Cavalry を起動し、Scripts メニューから
Cavalry Bridge.jsを起動しておく必要があります。これはリモートでは実行できません。bash scripts/cavalry-send.sh --pingに応答がない場合は、ユーザーに Cavalry を開き、Scripts ▸ Cavalry Bridge を実行するよう依頼してください。ブリッジを初めてインストールするには、scripts/cavalry-bridge.js を Cavalry の Scripts フォルダーにCavalry Bridge.jsとしてコピーします(~/Library/Application Support/Cavalry/Scripts/— 存在しない場合は作成してください。Help ▸ Show Scripts Folder でパスを確認できます)。--pingの応答に"version"フィールドがない場合は、古いブリッジが実行されています。現在のブリッジで上書きして、再起動してください。 - ビルドスクリプトをスクラッチパッドに書き、送信します:
bash scripts/cavalry-send.sh /path/to/build.js。各送信にはリクエスト ID が割り当てられ、ユーザーの Cavalry 環境設定フォルダー(ブリッジのGET /get応答から特定)にある専用の結果ファイルを待ち受けます。そのため、複数の呼び出し元が一つのブリッジを安全に共有できます。長時間のレンダリングにはCAVALRY_SEND_TIMEOUT=600(秒)を設定します。 - フィードバックは HTTP ではなくファイル経由で返されます。 ブリッジは、成功/失敗に加えて、スローされたエラーの例外メッセージとスタックを報告します。
console.logの出力は Cavalry の Log ウィンドウに表示されます。そのため、スクリプトの結果を読み取れるよう、すべてのスクリプトで結果をディスクに書き込む必要があります。- 視覚的な状態 → 重要なフレームで
api.setFrame(f); api.renderPNGFrame(dir + "/check_" + f, 50)を実行します(50 = 50% スケールのプレビュー)。その後 PNG を読み込みます。レンダリングはビルド後の別の送信で行ってください。スクリプト内で作成した接続(デフォーマーやスタッガーなど)はスクリプトが返るまで評価されないため、同じ送信内のプレビューは正しくないことがあります。 - データ(ID、バウンディングボックス、属性値)→
api.writeToFile(path, JSON.stringify(x), true)。
- 視覚的な状態 → 重要なフレームで
- 反復します:プレビューを確認し、スクリプトを修正して再送信します。ビルドスクリプトは再実行できるようにしてください。バージョンごとに新しいコンプ(
api.createComp("Thing v2"))を作成し、再実行時に古いコンプへレイヤーが積み重ならないようにします。アクティブなコンプは UI で共有される状態であり、ユーザーや別の呼び出し元が送信の合間に変更する可能性があります。そのため、後続の送信でレンダリングまたは編集を行うスクリプトでは、まずコンプ名で検索してください。ヘルパーのuseComp("Thing v2")を使います。 - 最終レンダリング:PNG フレームのループ + ffmpeg(下記の「レンダリング」を参照)。
送信に失敗すると、標準エラー出力に Cavalry error: <message + stack> が表示されます。行番号は送信したスクリプトと一致します。メッセージなしで失敗した場合は、構文エラー(スクリプトが開始されなかった)か、権限確認ダイアログで拒否された可能性があります。node --check build.js で確認し、Cavalry に開いたダイアログがないかユーザーに確認してもらってください。レンダリング中にタイムアウトした場合、通常は単にレンダリングに時間がかかっています。再送信するとレンダリングが二重に実行されるため、再送信せずにタイムアウトを延長してください。
シーン構築のチートシート(実証済みのパターン)
api.set はエラーを出しません — 名前を確認し、推測しないでください
api.set は、レイヤーに存在しない属性を黙って無視します。名前の誤記や古い Cavalry の属性名でも、何もせず送信は成功と報告されます。そのため、try/catch によるフォールバックの連鎖(「frameRate を試し、だめなら fps」)は役に立たないどころか逆効果です。最初の候補は常に「成功」してしまいます。ヘルパーの setAttrs(id, {...}) を使ってください(api.hasAttribute を確認し、属性名とレイヤーの種類を示してエラーをスローします)。確信が持てない場合は、api.getAttributes(id) で実際の名前を一覧表示してください。
コンプの設定
api.load("/abs/path/to/skills/cavalry/scripts/cavalry-helpers.js");
var comp = newComp("My animation v1", {width: 1920, height: 1080, fps: 30, frames: 240,
background: "#121212"}); // or "transparent"
これは api.createComp + api.setActiveComp + resolution、fps、startFrame/endFrame(両端を含む — 240 フレームの場合は 0~239)、および backgroundColor を設定するものです。新しいコンプの背景はデフォルトで不透明です(ユーザーの環境設定が異なる場合を除き、白)。単色にするには backgroundColor を設定します。大きな BG 矩形は不要です。透明な出力にするには "#00000000"(アルファ 0)を設定します。
シェイプと属性
var rect = api.primitive("rectangle", "Bar"); // also: "ellipse", "polygon", "star"...
setAttrs(rect, {"generator.dimensions": [400, 60], // rectangle: [w, h]
"position": [0, 0], // origin = comp centre, +y = UP
"material.materialColor": "#00ADEF"});
var dot = api.primitive("ellipse", "Dot");
setAttrs(dot, {"generator.radius": [20, 20]}); // ellipse: radius [rx, ry], not dimensions
よく使う属性パス:position [x,y]、position.x、scale.x/scale.y、rotation.z、
opacity、material.materialColor(16 進数文字列)、material.alpha(0~100 のスケール。値が
≤ 1 の場合、ほぼ見えません)、generator.dimensions(長方形)、generator.radius(楕円)。
名前を確認するには、api.getAttributes(id)、api.getAttributeDefinition(id, attr)(型、
デフォルト値、列挙値)を使うか、UI で属性を右クリックして Copy Scripting Path を選択します。
信頼できるサイズ設定:推測せずに測定する
テキストや読み込んだ画像には設定可能なサイズ属性がないため、実際のサイズを測定し、 scale で正規化します:
var bb = api.getBoundingBox(layer, true); // world space: {x,y,width,height,left,right,top,bottom,centre}
var s = 470 / bb.height; // e.g. scale an image or title to 470 px tall
setAttrs(layer, {"scale.x": s, "scale.y": s});
ワールド空間では top > bottom です(+y は上方向)。scale のキーフレームを設定する前に測定してください。
キーフレームとイージング
api.keyframe(layer, 22, {"position.y": 940});
api.keyframe(layer, 50, {"position.y": 150});
api.magicEasing(layer, "position.y", 22, "BounceOut"); // easing is applied per-attr AT a keyframe
api.keyframe が受け取るのはスカラーのリーフ属性のみです。 配列 — {"position": [x, y]}、
{"generator.dimensions": [w, h]} — や、ベクトル属性に対する単独の数値 —
{"rotation": 45}(rotation は 2.x 以降 x/y/z で、2D の回転は rotation.z)— を指定しても、
キーフレームは作成されず、エラーも発生しません。position.x/position.y、scale.x、rotation.z などを
直接キーにするか、ヘルパーの keyAttrs(id, frame, {...}) を使います。これは配列を分割し、単独の rotation を
rotation.z に割り当て、未知の名前を拒否します。同様に、magicEasing にはリーフ属性名を指定します
("rotation" ではなく "rotation.z")。api.getKeyframeTimes(id, "position.x") でキーを確認してください。
(api.set はより柔軟で、配列と単独の rotation の数値をどちらも使えます。)
Magic easing のプリセット:SlowIn/SlowOut/SlowInSlowOut、VerySlowIn/VerySlowOut/VerySlowInVerySlowOut、
SpringIn/SpringOut/SpringInSpringOut、SmallSpring… の各バリアント、AnticipateIn、OvershootOut、
AnticipateInOvershootOut、BounceIn/BounceOut/BounceInBounceOut、None。
制作での使用例:ポップインするテキストには OvershootOut、落下や着地には BounceOut、
バースト(パーティクル、ほこり)には SlowOut、漂いやパルスには SlowInSlowOut を使います。
手軽なパーティクルバースト(パーティクルシステムは不要):小さな楕円を N 個作り、ひとつの原点から
Math.cos/sin(angle) * dist で外側へキーフレーム移動させながら、scale を 0 にし、SlowOut を設定します。
完全な紙吹雪/火花のスニペットはリファレンスを参照してください。
テキスト — 1 文字ずつのアニメーションレシピ
var t = api.create("textShape", "Title");
setAttrs(t, {"text": "EMU 6-DAY RACE",
"horizontalAlignment": 1, "verticalAlignment": 1, // 1 = centre
"fontSize": 150, "font.font": "Nebula Sans", "font.style": "Black",
"material.materialColor": "#FFFFFF", "autoWidth": true, "autoHeight": true,
"position": [0, -190]});
autoWidth を指定しないと、テキストボックス内で長い行が折り返されます。Unicode(アクセント記号、ő/ű)は
そのまま使えます。フォントを使う前に確認してください:cavalry.fontExists(family, style)(両方の引数が必須)
と cavalry.getFontStyles(family) を使い、Cavalry に黙って代替させず、存在するフォントに切り替えてください。
1 文字ずつのアニメーション(各字形を時間差で落下させる)では、テキストレイヤーを 1 つだけ使い、
Sub-Mesh デフォーマーと、その時間オフセットを制御する Stagger を接続します。Sub-Mesh のデフォルトは
Level Mode が Text (Characters)(levelMode 3)なので、各字形には、stagger によってずらされた
Sub-Mesh キーフレームのコピーが個別に適用されます:
var subMesh = api.create("subMesh", "Title bounce");
api.keyframe(subMesh, 34, {"shapePosition.y": 990}); // start above frame (+y = up)
api.keyframe(subMesh, 64, {"shapePosition.y": 0});
api.magicEasing(subMesh, "shapePosition.y", 34, "BounceOut");
api.connect(subMesh, "id", t, "deformers");
api.parent(subMesh, t);
var stagger = api.create("stagger", "Title stagger");
setAttrs(stagger, {"minimum": -12, "maximum": 0}); // frames of per-letter offset
api.flipGraph(stagger, "graph", "vertical"); // so the FIRST letter lands first
api.connect(stagger, "id", subMesh, "shapeTimeOffset");
api.parent(stagger, t);
別の送信でプレビューしてください。 Cavalry が新しい接続(デフォーマー、stagger、ビヘイビア)を評価するのは、
それらを作成したスクリプトが戻った後です。同じ送信内でレンダリングしたフレームではすべての文字が一緒に動き、
stagger が壊れているように見えます。1 回目の送信で作成し、次の送信で useComp(...) とレンダリングを実行してください。
同様に、api.get で取得した Sub-Mesh の値は stagger が適用されていません。1 文字ずつの動きは、レンダリングしたフレームでのみ確認できます。
各字形に個別のイージング、色、パスが必要な場合(またはスクリプトで行を再レイアウトする必要がある場合)は、
ヘルパーの splitLetters(str, style, cx, cy) を使うと、各字形のテキストレイヤーが 1 つずつ作成され、
文字列全体での正確な位置(カーニングを含む)に配置されます。それぞれ開始フレームを設定してキーフレームを作成します:
var letters = splitLetters("NIGHT RUN 2026",
{"fontSize": 150, "font.font": "Helvetica", "font.style": "Bold",
"material.materialColor": "#FFFFFF"}, 0, 40); // line centred on [0, 40]
letters.forEach(function (id, i) {
var y = api.get(id, "position.y"), f0 = 10 + i * 3; // 3-frame stagger
api.keyframe(id, f0, {"position.y": y + 700});
api.keyframe(id, f0 + 20, {"position.y": y});
api.magicEasing(id, "position.y", f0, "BounceOut");
});
画像とその他のアセット
var asset = api.loadAsset("/abs/path/logo.png", false); // false = not an image sequence
var layer = api.addAssetToComp(asset);
var bb = api.getBoundingBox(layer, true);
var s = 470 / bb.height; // scale to ~470px tall
setAttrs(layer, {"scale.x": s, "scale.y": s});
データ駆動型のシーンでは、api.jsonFromAsset(assetId) で JSON/CSV アセットを解析し、
api.loadGoogleSheet(spreadsheetId, sheetId) でライブのシートを取得し、
api.WebClient で任意の HTTP API からデータを取得できます。リファレンスを参照してください。
レンダリング
デフォルト:PNG フレームループ+ffmpeg。 ブリッジ以外に必要なものはなく、確認用のフレームを取得できます。ffmpeg はその後、自分のシェルから実行します。
api.load("/abs/path/to/skills/cavalry/scripts/cavalry-helpers.js");
useComp("My animation v1");
renderFrames(OUT, "name", 0, 239, 100); // OUT/name_0000.png … name_0239.png
renderPNGFrame(pathWithoutExtension, scalePercent) は、アクティブなコンポの現在のフレームをレンダリングします。.png が追加され、表示されているレイヤーのみがレンダリングされます(ソロ状態も反映されます。api.soloLayers([]) で解除してください)。1080p で 240 フレームをレンダリングするには時間がかかるため、CAVALRY_SEND_TIMEOUT を大きくしてください。その後、次のように組み立てます。
ffmpeg -framerate 30 -i name_%04d.png -c:v libx264 -pix_fmt yuv420p out.mp4 # opaque
ffmpeg -framerate 30 -i name_%04d.png -c:v qtrle out.mov # alpha overlay
アルファ付きで出力するには、コンポの背景を透明にします(newComp(..., {background: "transparent"})、つまり backgroundColor を "#00000000" に設定)。デフォルトのままのコンポは、BG レイヤーがなくても不透明な白背景でレンダリングされます。すると PNG にアルファが含まれ、qtrle .mov はオーバーレイとして DaVinci Resolve にそのまま読み込めます(ProRes 4444 の -c:v prores_ks -profile:v 4 -pix_fmt yuva444p10le は、よりファイルサイズの小さい代替形式です)。次のコマンドで確認してください。
ffprobe -v error -show_entries stream=pix_fmt out.mov の結果は rgb24 ではなく、argb/rgba になるはずです。ffmpeg は api.runProcess 経由ではなく、シェルから実行してください。ブリッジなどのスクリプト UI から呼び出すと、「このスクリプトを信頼しますか」というダイアログが表示され、ユーザーが回答するまで処理が停止します。ベクタースナップショットには api.renderSVGFrame(path, scale, skipComps) を使用できます。
レンダーキュー(アプリ内):api.addRenderQueueItem(compId) → 設定 → api.render(itemId) または api.renderAll()。ネイティブの mp4/ProRes/GIF 出力や、複数のコンポまたはシーンの一括処理(ファイルごとに api.openScene(path, true))に便利です。レンダーキューアイテムには Setup/Pre/Post のレンダースクリプトを設定できます(レンダーごとにアセットやテキストを切り替えるなど)。リファレンスを参照してください。
Cavalry CLI — 2.7.x では利用できません。 Scene Group の 2.7.0~2.7.2 のリリースノートには、CLI が欠落している(「現在のバージョンを引き続きご利用ください……再導入に向けて作業中です」)と記載されています。また、2.7 より前のバージョンでは、render/list コマンドに Enterprise ライセンスが必要でした。2.7.2 バンドル内には今も CavalryCLI.app がありますが、その Contents/MacOS/ は空で、実行できるバイナリはありません。ヘッドレス形式で一括レンダリングする場合は、代わりにブリッジ経由で実行中のアプリを操作してください(各シーンを開き、レンダリングして、ffmpeg を実行します)。古いバージョンを使い続ける場合の 2.7 より前の CLI フラグについては、リファレンスを参照してください。
苦労して学んだ注意点
- ブリッジは実行結果を返しません — すべてのスクリプトは、ファイルベースのフィードバック(ステータス JSON、プレビュー PNG、エラー記録ファイル)を前提に設計してください。
console.logが見られるとは想定しないでください。 - ブリッジは
api.execのコードを IIFE でラップします。そのため、トップレベルのvarはそのまま使えますが、送信の間で値は保持されません。状態はシーンまたはディスクに保存してください。 api.set/api.keyframeはエラーを出さずに失敗します。認識されない名前はエラーなしで無視され、keyframeはベクトルに対する配列や単独の数値も無視します。setAttrs/keyAttrsを使用してください。このファイルにある名前は 2.7.2 で使用されているものです(fps、endFrame、text、backgroundColor)。frameRate/string/frameRangeEndに関する以前の記述は、2.7 では誤りです。古いリリースでも動作するスクリプトが必要な場合は、api.getCavalryVersion()で分岐してください。- カラー:2.6 で Linear Gamma(コンポジション設定)と Color Filter シェーダーが削除されました。代わりに、Working Color Space を Linear sRGB に設定した Color Management を使用してください。
material.alphaは 0~100 であり、0~1 ではありません。- 新しいレイヤーはアクティブなコンポジションに追加されます。
api.create/api.primitive/addAssetToCompは、その時点でアクティブなコンポジションに追加します。何かを作成する前に、使い捨ての確認用スクリプトであっても、newComp/useCompを呼び出してください。 - 重なり順とグループ化:後から作成したレイヤーは手前に描画されるため、背景やプレートはその上のテキストより先に作成してください。複数のレイヤー(ローワーサードのプレート、バー、テキストなど)をまとめて移動するには、
api.create("null", "Group")の下にapi.parentで親子関係を設定し、ヌルをアニメーションさせてください。 - ベクトル属性はオブジェクトとして読み取られます:
api.get(id, "position")の値は{x, y}です(resolution、generator.radiusも同様です。backgroundColorは{r,g,b,a}です)。一方、api.setには[x, y]を渡します。数値が必要な場合はposition.x/position.yを読み取ってください。 - プレビューは効率よく行ってください:すべてのフレームではなく、物語の要所となる数フレームを 50%のスケールでチェックレンダリングしてください。フル解像度・全範囲のレンダリングは、プレビューに問題がないことを確認してから一度だけ行ってください。
- 再実行:試行ごとに
api.createCompで新しいバージョンのコンポジションを作成してください。同じコンポジションで再実行すると、すべてのレイヤーが重複します。 - スクリプトからシーンを保存してください —
api.saveSceneAs("/path/scene.cv")— こうすればクラッシュしても作業が失われません。モーダルダイアログが開いているとfalseが返る(または例外が発生する)ため、結果を確認してください。ユーザーに確認せずapi.newScene()/api.openScene()を呼び出さないでください。開いているユーザーのシーンが破棄されます。 - 同梱の型定義はオフラインドキュメントとしても利用できます。ほぼすべての関数に JSDoc の例があるため、シグネチャを推測する前に grep で検索してください。
セキュリティ
ブリッジを実行することは、ユーザーのマシン上でコード実行サーバーを実行することを意味します。ブリッジの起動を依頼する前に、そのことを伝えてください。
- ブリッジは
127.0.0.1:8731にバインドし、ライブセッション内でそこに POST された JavaScript を、ユーザーの権限とユーザーが開いているシーンを使って実行します。JSON 以外の本文は JavaScript として直接実行されます。リクエストには認証がありません。ローカルプロセスはすべて、また共有マシン上の他のユーザーも、ブリッジ経由で Cavalry を操作できます。 - Web ページからは操作できません。
Originヘッダーを含むリクエスト、またはクロスサイトのSec-Fetch-Siteを含むリクエストは 403 で拒否されるため、ユーザーのブラウザーで開いたページからブリッジにアクセスすることはできません。このチェックが唯一の防御策であり、トークンはありません。 - ユーザーが実行するまで、待ち受けは始まりません。
cavalry-bridge.jsを Scripts フォルダーにコピーしただけでは、メニューに追加されるだけです。ユーザーが Scripts ▸ Cavalry Bridge を選ぶとポートが開き、そのセッション中は開いたままになります。 - ブリッジを停止するには、Cavalry を終了してください。 リモートからのシャットダウン機能はなく、プロセスの終了とともにポートが解放されます。