Opus 5.5 を使えば、Claude Code のセッション1回で 2.5D モーションスタジオを構築できます。異なる深度にフラットなレイヤーを重ね、キーフレームされたカメラをイージング付きで動かす単一の HTML ファイルと、全フレームをキャプチャして ffmpeg にパイプで渡すわずか25行の Playwright スクリプトです。今回のビルドでは、ラップトップで6秒間の720p MP4(180フレーム)を約20秒でレンダリングしました。
このガイドでは、使ったプロンプト、エージェントが書いたコード、遭遇したバグ、そして「ショットの説明」機能の背後にある Opus 5.5 の API 呼び出しを Apidog CLI で検証する方法を紹介します。
2.5D モーションスタジオとは?
2.5D モーションスタジオとは、空、丘、都市、タイトルカード、手前の草木といったフラットな2Dレイヤーを3D空間内の異なる深度に配置し、そこを仮想カメラが移動するアニメーションツールです。カメラに近いレイヤーは遠いレイヤーより速く動くため、3Dモデルを一切使わずにパララックス(視差)と奥行き感が生まれます。
一度きりのアニメーションではなく「スタジオ」と呼べる要素は次の3つです。
- シーングラフ:
z深度、サイズ、位置、HTML コンテンツを持つレイヤーの集合。 - タイムライン:カメラのキーフレーム(ドリー、トラック、オービット)と各レイヤーのキーフレーム(不透明度、スケール、位置)で、それぞれにイージングカーブが設定される。
- 決定論的レンダラー:任意の時刻
tに対して正確なフレームを描画するrenderAt(t)関数。これにより、エクスポート結果がマシンの再生速度に依存しなくなる。
3つ目のポイントはほとんどのチュートリアルが省略している部分であり、MP4 がカクつくかどうかを決める要素です。
Opus 5.5 で Claude Code をセットアップする
この種の作業には Opus 5.5 から始めるのが最適です。Anthropic の モデル概要 では、「長時間実行のエージェント型コーディングやナレッジワーク向け」とされ、100万トークンのコンテキストウィンドウ、最大出力128K、入力/出力トークン100万あたり4ドル/20ドルという料金設定です。
エイリアスの変更でプロジェクト途中にモデルが切り替わらないよう、モデルを固定します:
claude --model claude-opus-5-5 --effort high
effort のドキュメント によると、Opus 5.5 のデフォルトは medium です。投影計算を正しく行う必要がある最初のビルドでは high に上げましょう。キーフレームや色の細かい調整では medium に戻します。モデルの背景を先に知りたい場合は、Claude Opus 5.5 とは何か や、プロンプトキャッシングがコストに与える影響 を参照してください。
スタジオを構築するプロンプト
曖昧なプロンプト5つより、具体的なプロンプト1つが勝ります。以下が今回のビルド仕様を1つのプロンプトにまとめたものです:
Build a 2.5D motion studio as one file, studio.html, with no dependencies.
- Scene: 6 layers (sky, moon, hills, city skyline, a title card "Ship it.",
foreground foliage), each at its own z depth, using CSS perspective and
preserve-3d.
- Camera: keyframes for x, y, z (dolly) and yaw (orbit around the card).
- Per-layer tracks: opacity, scale, y. Easings: linear, easeInOutCubic,
easeInOutSine, easeOutBack.
- A timeline bar with keyframe markers, a playhead, click to scrub,
space to play or pause.
- renderAt(t) must be pure in t. Expose window.__seek(t) that resolves after
the frame paints, and a ?capture mode that hides the UI.
Then write export.mjs: Playwright opens ?capture, seeks every frame at 30 fps,
screenshots it and pipes PNGs to ffmpeg as H.264.
最後の2行が最も重要です。エクスポートを後付けではなく設計の一部にするためです。
エージェントが書いたコード
スタジオは約150行です。重要なのは次の3つの要素です。
キーフレームとイージング
各トラックはソートされたキーフレームのリストです。sample() は t を囲む2つのキーを見つけ、外側のキーのイージングを適用し、すべての数値フィールドを補間します:
function sample(keys, t) {
if (t <= keys[0].t) return keys[0];
for (let i = 0; i < keys.length - 1; i++) {
const a = keys[i], b = keys[i + 1];
if (t <= b.t) {
const p = (easings[a.ease] || easings.linear)((t - a.t) / (b.t - a.t));
const out = {};
for (const k in b) if (typeof b[k] === 'number' && k !== 't') out[k] = a[k] + (b[k] - a[k]) * p;
return out;
}
}
return keys[keys.length - 1];
}
深度のトリック
カメラは動きません。世界が逆方向に動きます。各レイヤーには z 方向の translate と、それを補正するスケールを与えます:
const PERSPECTIVE = 1200; // must match #viewport perspective
world.style.transform =
`translate3d(${-cam.x}px, ${-cam.y}px, ${cam.z}px) rotateY(${-cam.yaw}deg)`;
const comp = 1 - L.z / PERSPECTIVE;
el.style.transform = `translate(-50%,-50%) ` +
`translate3d(${x * comp}px, ${y * comp}px, ${L.z}px) scale(${comp * s})`;
CSS の perspective を1200pxに設定すると、z = -900 のレイヤーは 1200 / 2100、つまり元のサイズの57%でレンダリングされます。そのスケールと x/y オフセットに 1 + 900/1200 = 1.75 を掛けると、これが打ち消されます。カメラが静止しているときは各レイヤーがオーサリング時のサイズと位置で表示され、カメラが動くときはそれぞれの速度で動きます。これがこの錯覚のすべてです。

知っておくべきバグ
最初のバージョンでは各レイヤーのスケールだけを補正し、オフセットを補正していなかったため、丘が上昇して空の3分の2を覆ってしまいました。修正方法は、x と y にも comp を掛けることです。コードを読んでではなく、レンダリングされたフレーム1枚からこのバグに気づいたので、カメラの動きを信用する前に、エージェントに冒頭・中盤・終盤の静止画をレンダリングさせるようにしましょう。
タイムラインを MP4 にエクスポートする
リアルタイム再生は requestAnimationFrame を使用するため、マシンが高負荷だとフレーム落ちします。エクスポートではそれは許されません。そこでエクスポーターはフレームインデックスから時刻を制御し、各フレームの描画が完了してから Playwright のスクリーンショット を撮ります:
import { chromium } from 'playwright';
import { spawn } from 'node:child_process';
import { pathToFileURL } from 'node:url';
const out = process.argv[2] || 'out.mp4';
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1280, height: 720 } });
await page.goto(pathToFileURL('studio.html').href + '?capture');
const { duration, fps } = await page.evaluate(() => window.__scene);
const ffmpeg = spawn('ffmpeg', ['-y', '-f', 'image2pipe', '-framerate', String(fps),
'-i', '-', '-c:v', 'libx264', '-pix_fmt', 'yuv420p', '-crf', '18', out],
{ stdio: ['pipe', 'ignore', 'inherit'] });
const frames = Math.round(duration * fps);
for (let i = 0; i < frames; i++) {
await page.evaluate(t => window.__seek(t), i / fps);
ffmpeg.stdin.write(await page.screenshot({ type: 'png' }));
}
ffmpeg.stdin.end();
await new Promise(r => ffmpeg.on('close', r));
await browser.close();
window.__seek(t) は2つのアニメーションフレームの後に解決するため、各スクリーンショットは新しい transform を反映します。image2pipe(ffmpeg フォーマットのドキュメント 参照)は stdin から PNG を読み込み、yuv420p はブラウザで再生可能なファイルにします。
今回の実行結果:180フレーム、1280x720、30 fps、6.0秒、420 KB の MP4 を実時間20秒で生成。Playwright にブラウザがまだない場合は、npx playwright install chromium を一度実行してください。

都市のビルは手前の暗い丘よりも動きが少なく、丘は最終的にフレーム外へ完全に出ていきます。これがパララックスです。
Opus 5.5 API で「ショットの説明」機能を追加する
次に、ライターがショットの説明を入力するとキーフレームが返ってくる機能を追加しましょう。スタジオは Messages API を直接呼び出します:
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2026-10-21" \
-H "content-type: application/json" \
-d '{
"model": "claude-opus-5-5",
"max_tokens": 4096,
"output_config": { "effort": "low" },
"system": "Return only JSON: {\"camera\": [{\"t\",\"x\",\"y\",\"z\",\"yaw\",\"ease\"}], \"tracks\": {}}. Times in seconds, 0 to 6.",
"messages": [{ "role": "user", "content": "Slow push-in, slight orbit right, title fades up in the first second." }]
}'
effort のドキュメントに関わる2つの重要なポイントがあります。1つ目は、Messages API リクエストでは effort レベルを output_config.effort に指定するということ。2つ目は、Opus 5.5 は常にアダプティブシンキングを使用する(thinking: {"type": "disabled"} を指定すると400が返る)ため、シンキングトークンが max_tokens に含まれるということです。値が低すぎると、stop_reason: "max_tokens" で JSON が配列の途中で切れてしまいます。ショットリストの生成は小さく仕様が明確なタスクなので、まずは low effort が妥当な設定です。確定する前に自分で比較してみてください。
Apidog CLI で Claude Code に API 呼び出しをテストさせる
応答が途中で切れたり余計な説明が付いたりすると、レンダリング時に sample() が壊れます。そのため、この呼び出しにはコントラクトテストが必要です。同じ Claude Code セッションで、Apidog CLI を使ってテストを書いて実行できます。
一度だけインストールしてログインします:
npm install -g apidog-cli@latest
apidog auth login --with-token <YOUR_TOKEN>
apidog skill install
次に、エージェントに1つのプロンプトを与えます:
In Apidog project <projectId>, create an endpoint for POST /v1/messages and a
test case that sends the "describe a shot" request above with model
claude-opus-5-5. Assert: status 200, body.model is "claude-opus-5-5",
stop_reason is not "max_tokens", and content[0].text parses as JSON with a
non-empty camera array whose t values rise from 0 to 6. Use an environment
with base URL https://api.anthropic.com and the key in a variable. Run it and
summarize failures.
エージェントはペイロードのスキーマを推測ではなく読み取りで取得し、ファイルを検証してから、すべてを作成・実行します:
apidog cli-schema get endpoint-create
apidog cli-schema get test-case-create
apidog cli-schema validate test-case-create --file case.json
apidog environment create anthropic --project <projectId> --base-url https://api.anthropic.com
apidog endpoint create --project <projectId> --file endpoint.json
apidog test-case create --project <projectId> --file case.json
apidog run --test-case <caseId> --project <projectId> -e <envId> -r cli,json
キーは --env-var "api_key=$ANTHROPIC_API_KEY" で実行時に渡し、プロジェクト内に保存されないようにしましょう。システムプロンプトや effort レベルを変更したら、どちらも出力に影響するため、テストケースを再度実行してください。同じ apidog run に -r junit を付けると CI に組み込めます。詳しくは Apidog CLI の CI/CD ガイド と Claude Code での Apidog CLI を参照してください。Apidog の Free プランは最大4ユーザーまで利用できます。
スタジオの次の展開
- レイヤーを増やす。計算式はそのまま。雨、2つ目のタイトル、ロゴなどを追加し、それぞれに
zを与えれば、補正が残りを処理してくれます。 - レンダラーを置き換える。シーンを React コンポーネントとして書きたい場合は、Claude Code と Remotion の記事がそのルートを解説しています。本記事の単一ファイル方式はビルドステップが不要です。Claude Code の概要 ではセッションと権限について説明されています。
FAQ
モーショングラフィックスにおける 2.5D とは何ですか?フラットな2Dアートワークを異なる深度に配置し、動くカメラで撮影する手法です。3Dモデル、ライティング、3Dエンジンを使わずに、パララックスと奥行きの手がかりを得られます。
このビルドになぜより小さなモデルではなく Opus 5.5 を使うのですか?最初のビルドでは、投影計算、キーフレームエンジン、キャプチャパイプラインを一気に組み合わせる必要があります。Anthropic は Opus 5.5 を長時間実行のエージェント型コーディング向けに位置づけており、今回のセッションでは1つのプロンプトと1回の修正ラウンドで動作するスタジオが得られました。その後の小さな編集には、より低い effort レベルで十分です。
フレームをキャプチャする代わりに画面を録画してはいけないのですか?画面録画はリアルタイムに従うため、遅いフレームはフレーム落ちやフレームの重複になります。i / fps にシークして各フレームをキャプチャすることで、どのマシンでも同一の出力になります。
「ショットの説明」機能は API キーなしで使えますか?いいえ。この機能は Messages API を呼び出すため、自分の Anthropic API キーが必要です。スタジオ本体と MP4 エクスポートはキーなしでローカルに実行できます。
ショットリストの呼び出しにはどの effort レベルを使うべきですか?タスクが短く出力スキーマが固定されているため、まずは low から始めてください。Apidog のテストで不正な形式や指示外のキーフレームが見られた場合のみ、レベルを上げてください。
