Anda bisa membangun studio motion 2.5D dengan Opus 5.5 dalam satu sesi Claude Code: satu file HTML yang menumpuk lapisan datar pada kedalaman berbeda, menggerakkan kamera ber-keyframe melewatinya dengan easing, dan skrip Playwright berisi 25 baris yang menangkap setiap frame lalu menyalurkannya ke ffmpeg. Build kami merender MP4 720p berdurasi 6 detik (180 frame) dalam sekitar 20 detik di laptop.
Panduan ini menunjukkan prompt yang kami gunakan, kode yang ditulis agen, bug yang ditemuinya, dan cara memeriksa panggilan API Opus 5.5 di balik fitur "describe a shot" dengan Apidog CLI.
Apa itu studio motion 2.5D?
Studio motion 2.5D adalah alat animasi yang menempatkan lapisan 2D datar (langit, bukit, kota, kartu judul, dedaunan latar depan) pada kedalaman berbeda dalam ruang 3D, lalu menggerakkan kamera virtual melewatinya. Lapisan yang lebih dekat ke kamera bergeser lebih cepat daripada yang jauh, sehingga menghasilkan efek parallax dan kesan kedalaman tanpa model 3D apa pun.
Tiga bagian menjadikannya sebuah studio, bukan sekadar animasi sesaat:
- Scene graph: lapisan dengan kedalaman
z, ukuran, posisi, dan konten HTML. - Timeline: keyframe untuk kamera (dolly, truck, orbit) dan untuk setiap lapisan (opacity, scale, posisi), masing-masing dengan kurva easing.
- Renderer deterministik: fungsi
renderAt(t)yang menggambar frame yang tepat untuk waktutmana pun, sehingga ekspor tidak pernah bergantung pada seberapa cepat mesin memutarannya.
Poin ketiga itulah yang paling sering dilewatkan tutorial, dan poin itu menentukan apakah MP4 Anda patah-patah atau tidak.
Menyiapkan Claude Code dengan Opus 5.5
Opus 5.5 adalah model yang tepat untuk memulai pekerjaan semacam ini. Ringkasan model-model Anthropic menggambarkannya sebagai model "untuk pekerjaan coding agentic dan pengetahuan yang berjalan lama", dengan jendela konteks 1 juta token, output maksimal 128K, dan harga $4 / $20 per juta token input / output.
Kunci modelnya agar perubahan alias tidak menggantinya di tengah proyek:
claude --model claude-opus-5-5 --effort high
Opus 5.5 secara default menggunakan effort medium, menurut dokumentasi effort. Naikkan ke high untuk build pertama, saat agen harus mendapatkan matematika proyeksinya dengan benar. Turunkan kembali ke medium untuk penyesuaian kecil pada keyframe dan warna. Jika Anda ingin memahami model ini lebih dulu, baca apa itu Claude Opus 5.5 dan bagaimana prompt caching mengubah biayanya.
Prompt yang membangun studio
Satu prompt yang spesifik mengalahkan lima prompt yang samar. Berikut spesifikasi build kami, ditulis sebagai satu prompt:
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.
Dua baris terakhir paling penting: keduanya menjadikan ekspor bagian dari desain, bukan tambahan belakangan.
Kode yang ditulis agen
Studionya berisi sekitar 150 baris. Tiga bagian yang penting.
Keyframe dan easing
Setiap track adalah daftar keyframe yang terurut. sample() mencari dua keyframe yang mengapit t, menerapkan easing dari keyframe keluar, dan menginterpolasi setiap field numerik:
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];
}
Trik kedalaman
Kamera tidak pernah bergerak. Dunialah yang bergerak sebaliknya. Setiap lapisan kemudian mendapat translasi pada sumbu z plus skala kompensasi:
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})`;
Dengan CSS perspective yang diatur ke 1200px, lapisan di z = -900 dirender pada skala 1200 / 2100, atau 57% dari ukurannya. Mengalikan skala dan offset x/y-nya dengan 1 + 900/1200 = 1,75 meniadakan efek itu. Setiap lapisan muncul pada ukuran dan posisi yang Anda tentukan saat kamera diam, dan tetap bergerak dengan kecepatannya sendiri saat kamera bergerak. Itulah seluruh ilusinya.

Bug yang perlu Anda ketahui
Versi pertama mengalikan skala setiap lapisan tetapi bukan offsetnya, sehingga bukit-bukit naik menutupi dua pertiga langit. Solusinya: kalikan x dan y dengan comp juga. Kami menemukannya dari satu frame hasil render, bukan dari membaca kode, jadi mintalah agen Anda merender tangkapan still di awal, tengah, dan akhir sebelum Anda memercayai gerakan kamera apa pun.
Mengekspor timeline ke MP4
Pemutaran real-time menggunakan requestAnimationFrame, yang membuang frame saat mesin sedang sibuk. Ekspor tidak boleh begitu. Karena itu, exporter menggerakkan waktu dari indeks frame dan menunggu setiap frame selesai digambar sebelum mengambil tangkapan layar 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) resolves setelah dua animation frame, sehingga setiap tangkapan layar melihat transformasi yang baru. image2pipe (lihat dokumentasi format ffmpeg) membaca PNG dari stdin, dan yuv420p menjaga agar file tetap bisa diputar di browser.
Hasil run kami: 180 frame, 1280x720, 30 fps, 6,0 detik, sebuah MP4 berukuran 420 KB, dalam 20 detik waktu nyata. Jalankan npx playwright install chromium sekali jika Playwright belum memiliki browser.

Menara-menara kota bergeser lebih sedikit daripada bukit latar depan yang gelap, yang keluar dari frame sepenuhnya. Itulah parallax.
Menambahkan fitur "describe a shot" dengan API Opus 5.5
Selanjutnya, biarkan seorang penulis mengetik deskripsi shot dan mendapatkan keyframe sebagai hasilnya. Studio memanggil Messages API secara langsung:
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." }]
}'
Dua detail dari dokumentasi effort penting di sini. output_config.effort adalah tempat level effort ditempatkan dalam request Messages API. Dan Opus 5.5 selalu menggunakan adaptive thinking (request dengan thinking: {"type": "disabled"} mengembalikan 400), sehingga token thinking ikut dihitung dalam max_tokens. Jika terlalu kecil, JSON terpotong di tengah array dengan stop_reason: "max_tokens". Daftar shot adalah tugas yang kecil dan spesifikasinya jelas, jadi effort low adalah pilihan awal yang masuk akal; lakukan perbandingan sendiri sebelum menetapkannya.
Meminta Claude Code menguji panggilan API dengan Apidog CLI
Balasan yang terpotong atau bertele-tele akan merusak sample() saat render, sehingga panggilan ini membutuhkan contract test. Sesi Claude Code yang sama bisa menulis dan menjalankannya dengan Apidog CLI.
Instal dan login sekali:
npm install -g apidog-cli@latest
apidog auth login --with-token <YOUR_TOKEN>
apidog skill install
Kemudian beri agen satu prompt:
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.
Agen membaca schema payload alih-alih menebaknya, memvalidasi filenya, lalu membuat dan menjalankan semuanya:
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
Serahkan key saat runtime dengan --env-var "api_key=$ANTHROPIC_API_KEY" agar key tidak pernah tersimpan di proyek. Jalankan ulang test case ketika Anda mengubah system prompt atau level effort, karena keduanya mengubah hasil yang dikembalikan. apidog run yang sama dengan -r junit bisa langsung dipakai di CI; lihat panduan CI/CD Apidog CLI dan Apidog CLI di Claude Code. Paket Free Apidog mencakup hingga 4 pengguna.
Langkah selanjutnya untuk studio ini
- Lebih banyak lapisan, matematika yang sama. Tambahkan hujan, judul kedua, atau logo; beri masing-masing nilai
zdan skala kompensasi mengurus sisanya. - Ganti renderernya. Jika Anda lebih suka menulis scene sebagai komponen React, Claude Code dengan Remotion membahas jalur tersebut. Pendekatan satu file di sini tidak membutuhkan build step. Ikhtisar Claude Code menjelaskan sesi dan izin.
FAQ
Apa arti 2.5D dalam motion graphics? Artinya karya seni 2D datar ditempatkan pada kedalaman berbeda dan direkam oleh kamera yang bergerak. Anda mendapatkan parallax dan isyarat kedalaman tanpa model 3D, pencahayaan, atau engine 3D.
Mengapa menggunakan Opus 5.5 alih-alih model yang lebih kecil untuk build ini? Build pertama mencampur matematika proyeksi, engine keyframe, dan pipeline tangkap dalam satu kali jalan. Anthropic memosisikan Opus 5.5 untuk coding agentic berdurasi panjang, dan sesi kami menghasilkan studio yang berfungsi dari satu prompt plus satu putaran perbaikan. Untuk editan kecil setelahnya, level effort yang lebih rendah sudah cukup.
Mengapa tidak merekam layar alih-alih menangkap frame? Rekaman layar mengikuti waktu nyata, sehingga frame yang lambat menjadi frame yang hilang atau berlipat. Dengan seek ke i / fps dan menangkap setiap frame, hasilnya identik di setiap mesin.
Bisakah saya menggunakan fitur "describe a shot" tanpa API key? Tidak. Fitur ini memanggil Messages API, yang membutuhkan API key Anthropic Anda sendiri. Bagian lain dari studio dan ekspor MP4 berjalan secara lokal tanpa key.
Level effort apa yang sebaiknya dipakai untuk panggilan daftar shot? Mulailah dengan low, karena tugasnya pendek dan schema outputnya tetap. Naikkan hanya jika pengujian Apidog Anda menunjukkan keyframe yang rusak format atau tidak sesuai deskripsi.
