Claude CodeとRemotionで動画を作成する
Claude CodeにRemotionスキルを組み合わせることで、自然言語プロンプトから動画を生成するワークフローが実現します。スキルをインストールし、Remotionプロジェクトをスキャフォールドし、動画の内容を平易な英語で説明するだけで、ClaudeがフレームアキュレートなReactコードを生成してMP4にレンダリングします。これがワークフローの全体像です。障壁となるのはプロンプトの書き方ではなく、レンダリング結果が意図からずれたときに修正できるだけの理解力を、Claudeが返すコードに対して持てるかどうかです。本記事では、開発者向けのサンプルを一つ端から端まで解説し、RemotionのメンタルモデルをどのようにStudioプレビューから読み解くかを示します。これにより、再プロンプトを繰り返すことなく、壊れたレンダリングを診断できるようになります。
重要なポイント
- Remotionは、各フレームを
useCurrentFrame()で取得したフレーム番号の純粋関数として扱い、その出力をスクリーンショットしてMP4を生成するReactフレームワークです。 - 30fpsで15秒の動画は450フレームになります。すべてのアニメーションはフレーム番号からCSS値へのマッピングであり、タイムラインエディターは存在しません。
- インストールコマンドはチュートリアルによって複数の形式で紹介されています。スキルのツールチェーンは変動しているため、実行前に
remotion.dev/docs/ai/claude-codeで現在のコマンドを必ず確認してください。 - プレビューでアニメーションが同時に発火する場合、原因はほぼ必ず
<Sequence>コンポーネントのfromオフセットが指定されていないことです。デフォルトではフレーム0から開始されます。 - Remotionはヘッドレスのchromiumを起動してすべてのフレームをスクリーンショットすることでレンダリングします。30fpsで30秒のコンポジションに900枚のスクリーンショットが必要な理由、そしてRemotionがプロダクション環境向けにRemotionLambdaを提供している理由はここにあります。
このスタックとは何か
Remotionは、動画をプログラムで作成するためのオープンソースのReactフレームワークです。シーンをReactコンポーネントとして定義し、フレーム番号でタイミングを制御し、MP4・WebM・GIFとして出力します。タイムラインエディターもドラッグ&ドロップも不要です。Claude Code向けのRemotionスキルは、エージェントにRemotionのAPIサーフェス(コンポジション、シーケンス、interpolate()、spring()、レンダー設定)を教えるルールパックです。これにより、エージェントは推測ではなく正確なフレーム計算を含むコードを生成します。これはレンダリング時に動作するプラグインではなく、Claudeに注入されるコンテキストです。これにより、生成されるコードがコンパイルされ、意図したとおりにアニメーションします。
Remotionのメンタルモデルとは?
Remotionでは、動画はフレーム番号の純粋関数です。コンポジションは固定された継続フレーム数を持つReactコンポーネントです。30fpsで15秒の動画は450フレームになり、すべてのアニメーションは現在のフレームからCSS値(opacity、transform、colorなど)へのマッピングとして表現されます。タイムラインは存在せず、あるのは数式だけです。
モデル全体を支える4つのプリミティブがあります。すべてRemotionのファンダメンタルズに記載されています。
useCurrentFrame()は現在レンダリング中のフレーム番号を返します。コンポーネントはすべてのフレームで再実行されます。- fpsとduration は
<Composition>に設定します。コンポジションのドキュメントによれば、durationInFrames・fps・width・heightをここで指定します。 <Sequence>は時間をシフトします。<Sequence from={90}>でラップされた子コンポーネントは、グローバルフレームが90のときにフレーム0を参照します。これがシーンをずらす仕組みです。interpolate()はフレーム範囲を値の範囲にマッピングします。
頻繁に使うフレーム計算の早見表:
| 秒数 | 30fpsでのフレーム数 |
|---|---|
| 5秒 | 150 |
| 10秒 | 300 |
| 15秒 | 450 |
| 30秒 | 900 |
| 60秒 | 1800 |
「フレームを入力してCSS値を出力する」という考え方が身につけば、生成されたコードが魔法のように見えなくなります。
前提条件とインストール
Discover how at OpenReplay.com.
Node.js(インストール前にRemotionの入門ページでバージョン要件を確認してください)と、AnthropicのターミナルコーディングエージェントであるClaude Codeが必要です。Claude CodeはClaudeのサブスクリプションまたはAPIの課金に対して動作します。プランの提供状況は変わることがあるため、現在のアクセス要件はAnthropicのClaude Codeドキュメントで確認してください。
ここが多くのチュートリアルで駆け足になる部分です。Remotionスキルのインストールコマンドは、最近の記事だけでも少なくとも3つの形式で登場しています(npx @anthropic-ai/skills add remotion、npx skills add remotion、npx skills add remotion-dev/skills)。スキルのパッケージングは変化し続けているため、コピーしたコマンドを信用しないでください。公式のスキルページ(remotion.dev/docs/ai/skills)を開き、現在記載されているコマンドを実行してください。エコシステムが安定するまで、このページを唯一の情報源として扱ってください。
まずClaude CodeがPATHに通っているか確認します:
claude --version
次に公式ドキュメントページのコマンドを使ってスキルをインストールし、プロジェクト内でClaudeに「Do you have the Remotion skill loaded?」と質問してインストールを確認します。
実践例:15秒のフィーチャーリビールクリップ
このスタックが最も力を発揮する開発者向けユースケースは、データやスケジュールに基づいて再生成するアセットです。CHANGELOG.mdから生成するチェンジログクリップ、現在のシステムを反映したアニメーションアーキテクチャ図、リリースごとに再レンダリングするプロダクトフィーチャーリビールなどが挙げられます。ここではフィーチャーリビールを作成します。
ステップ1 — スキャフォールド
スキャフォールドコマンドはRemotionの入門ページに記載されています:
npx create-video@latest feature-reveal
cd feature-reveal
npm install
TypeScriptテンプレートまたは設定でプロジェクトを作成します。スキャフォールドにより、src/Root.tsx(コンポジションを登録する場所)とスターターコンポジションが生成されます。
ステップ2 — Claude Codeへのプロンプト
プロジェクト内でエージェントを起動し、フレーム計算を明示的に指定します。ここが不正確な出力の主な原因となります:
Create a Remotion composition called FeatureReveal.
- 15 seconds at 30fps (450 frames), 1920x1080.
- Dark background (#0d1117).
- A headline "Ship changelogs as video" fades in over frames 0-30,
holds, then fades out over frames 420-450.
- Three feature rows below the headline, each sliding up from 40px
with a spring, staggered: row 1 enters at frame 60, row 2 at 90,
row 3 at 120.
- Register FeatureReveal in Root.tsx with the correct
durationInFrames and fps.
ステップ3 — 生成されたコードを読む
正しく生成されたコードは以下のようになります。見出しにはinterpolate()を使用し、各行には<Sequence from>オフセットとspring()を使用します:
import {
AbsoluteFill,
interpolate,
spring,
Sequence,
useCurrentFrame,
useVideoConfig,
} from "remotion";
const features = ["One prompt", "Re-render per release", "Version-controlled"];
const FeatureRow: React.FC<{ label: string }> = ({ label }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
// spring()は0→1の値を返す。translateYとopacityにマッピングする。
const enter = spring({ frame, fps, config: { damping: 14 } });
const translateY = interpolate(enter, [0, 1], [40, 0]);
return (
<div style={{ opacity: enter, transform: `translateY(${translateY}px)` }}>
{label}
</div>
);
};
export const FeatureReveal: React.FC = () => {
const frame = useCurrentFrame();
// 見出しのopacity: フレーム30までにフェードイン、保持、フレーム420から450でフェードアウト。
const headlineOpacity = interpolate(
frame,
[0, 30, 420, 450],
[0, 1, 1, 0],
{ extrapolateRight: "clamp" }
);
return (
<AbsoluteFill style={{ backgroundColor: "#0d1117", color: "white" }}>
<h1 style={{ opacity: headlineOpacity, fontSize: 72 }}>
Ship changelogs as video
</h1>
{features.map((label, i) => (
// 各行は`from`によって30フレームずつずらされる。
<Sequence key={label} from={60 + i * 30}>
<FeatureRow label={label} />
</Sequence>
))}
</AbsoluteFill>
);
};
覚えておくべき行は見出しのinterpolate()です。この呼び出しはフレーム番号をopacityにマッピングします。フレーム0でopacityは0、フレーム30で1になり、フレーム420まで保持され、フレーム450で0にフェードアウトします。フレーム配列と値配列は同じ長さでなければならず、{ extrapolateRight: "clamp" }は最後の点を超えて値が継続するのを防ぎます。このオプション名と動作はinterpolate()のリファレンスに記載されています。
スタガーは各<Sequence>のfrom={60 + i * 30}によって実現されます。各行が独自のシーケンスを持つため、そのuseCurrentFrame()は開始フレームで0にリセットされ、spring()は動画の開始点ではなく各行のエントリーポイントから発火します。
ClaudeはRoot.tsxにコンポジションも登録する必要があります:
import { Composition } from "remotion";
import { FeatureReveal } from "./FeatureReveal";
export const RemotionRoot: React.FC = () => (
<Composition
id="FeatureReveal"
component={FeatureReveal}
durationInFrames={450}
fps={30}
width={1920}
height={1080}
/>
);
ここのdurationInFramesとfpsがコンポーネント内のフレーム番号と一致しない場合、タイミングが崩れます。これが「動画の長さが意図と異なる」という最も一般的な原因です。
ステップ4 — Studioでプレビュー
Remotion Studioを起動します:
npm run dev
Studioがブラウザで開きます(表示されるポート番号を確認してください。常に同じとは限りません)。FeatureRevealを選択して再生し、再生ヘッドをスクラブします。特定のフレームへのスクラブはデバッグの基本操作です。プレビューはフレームNのレンダリング結果を正確に表示するため、コード内のフレーム計算と比較できます。
Remotionのレンダリング結果がおかしい場合の診断方法
汎用的なミスのリストではなく、Studioのプレビューから診断してください。問題が発生しているフレームにスクラブし、そのフレーム範囲を制御するコードを読みます。ほとんどの問題は3つの障害パターンに分類されます。
すべてのアニメーションが同時に発火する。 プレビューでアニメーションが同時に発火する場合、原因はほぼ必ず<Sequence>コンポーネントのfromオフセットが指定されていないことです。from={startFrame}を設定しない限り、各シーンはデフォルトでフレーム0から開始されます。フレーム0にスクラブして、3つの行がすでに動いている場合はシーケンスがスタガーされていません。「各フィーチャー行をfromが60、90、120に設定された独自の<Sequence>でラップしてください」と再プロンプトして修正します。
動画の長さが意図と異なる。 Claudeは指定したfpsからフレーム番号を生成します。fpsを指定しない場合、デフォルト値を仮定して、異なる設定のコンポジションに対して計算が合わなくなることがあります。再生ヘッドの末尾にスクラブしてください。450フレームのアニメーションがフレーム300で終わっている場合、コンポジションのdurationInFramesが300に設定されています。「もっと長くして」という曖昧な指示ではなく、「15秒を30fpsで換算すると450フレームです。durationInFrames={450}に設定してください」と具体的な数値で再プロンプトします。
モーションが機械的に見える。 イージングのない線形interpolate()は機械的に見えます。エントリーアニメーションをスクラブしてください。一定速度で動いている場合はイージングがありません。有機的なモーションにはspring()に切り替えます(0→1の値を返し、それをtransformにマッピングします)。またはinterpolateのドキュメントに従ってinterpolate()にeasingオプションを渡します。「線形のinterpolateの代わりにspring()を使って行のエントリーを実装してください」と依頼します。
ランディングページに埋め込まれたデモ動画のセッションリプレイでは、別の障害パターンとして、大きなMP4が自動再生されてペイントをブロックするケースがよく見られますが、これはデリバリーの問題であり、レンダリングの問題ではありません。
MP4へのレンダリング
npx remotion renderを使ってコマンドラインからレンダリングします。コンポジションIDと出力パスを指定します:
npx remotion render FeatureReveal out/feature-reveal.mp4
ここで、多くのプロンプトガイドが省略している現実を説明します。Remotionはヘッドレスのchromiumを起動してすべてのフレームをスクリーンショットし、そのフレームをffmpegにパイプしてレンダリングします。このアーキテクチャはレンダリングのドキュメントに記載されています。30fpsで30秒のコンポジションは900枚のブラウザスクリーンショットを意味します。レンダリング時間がフレーム数とフレームごとの複雑さに比例してスケールする理由、そして長い動画や視覚的に重いコンポジションがラップトップで「1分以内」ではなくかなりの実時間を要する理由はここにあります。
プロダクションパイプライン(長い動画、多数のバリアント、ビルドエージェントを占有できないCI)では、Remotionが推奨するクラウドの選択肢はRemotion Lambdaです。これはフレームを並列Lambdaファンクションに分散させます。ローカルのレンダリング時間がボトルネックになるか、開発者のマシンなしでスケジュールに従ってレンダリングする必要が生じた時点で、Lambdaのセットアップコストは十分に回収できます。15秒の一回限りのクリップであれば、ローカルでレンダリングしてLambdaはスキップしてください。
Remotionは個人および小規模企業には無料ですが、大企業にはライセンスが必要です。商用利用前にRemotionのライセンスページで現在の条件を確認してください。
Remotionを使わない方がよい場合
Remotionが適しているのは、動画がデータドリブンで繰り返し生成するもの、またはデザインシステムに正確に合わせる必要があるもの(チェンジログクリップ、アニメーション図、リリースごとのフィーチャーリビールなど)です。コンテンツが実写・フォトリアリスティック・一回限りのクリエイティブ作品で、再レンダリングのコストがコードベースの制御の利点を上回る場合は、従来のエディターやAI動画ジェネレーターを選んでください。トーキングヘッドの録画からNGシーンをカットする、映像のカラーグレーディング、有機的でフォトリアルなモーションの制作は、Remotionでは困難な作業です。判断の基準は、出力がプログラムであることで恩恵を受けるかどうかです。一度レンダリングして二度と触れないのであれば、コードはオーバーヘッドであり、利点ではありません。
このスタックの真価は最初のレンダリングではなく、2回目以降にあります。コンポジションが一度完成すれば、新しいデータ(新しいチェンジログ、新しいメトリクス、新しいリリース)から再生成するのはコマンド一つです。公式ドキュメントページからスキルをインストールし、上記のフィーチャーリビールの例を構築し、同じコンポジションを実際に出荷するデータに向けてください。
FAQ
interpolate()はデフォルトでフレーム範囲を値の範囲に線形マッピングします。フレーム0から30の間でopacityを0から1にフェードさせるなど、正確な開始・終了フレームを制御できます。spring()は0から1の物理ベースの値を返し、自然にイーズインして落ち着きます。この値をtransformやopacityにマッピングします。正確なタイミングと保持にはinterpolate()を、機械的に見えないような有機的なモーションにはspring()を使用してください。
継続時間はコンポーネント内のアニメーション値ではなく、Root.tsxのCompositionのdurationInFramesで制御されます。durationInFramesが300でアニメーションがフレーム450まで実行される場合、レンダリングはフレーム300で停止して残りはカットされます。fpsの値もフレーム計算に使用した値と一致している必要があります。30fpsで15秒は450フレームです。コンポーネントの計算とコンポジションの設定が一致するよう、両方を明示的に設定してください。
動画を平易な英語で説明してClaude Codeにコードを生成させることはできますが、レンダリング結果が意図からずれた場合に修正するために出力を読む必要があります。スキルはClaudeにRemotionのAPIを教えて正確なフレーム計算を生成させますが、デバッグ時にコンポジション・シーケンス・フレーム番号を理解する必要性はなくなりません。「フレームを入力してCSS値を出力する」というメンタルモデルを理解していることが、Studioのプレビューから問題を診断するための鍵です。
ローカルのレンダリング時間がボトルネックになるか、開発者のマシンなしでスケジュールに従ってレンダリングする必要が生じた時点で、Remotion Lambdaのセットアップコストは十分に回収できます。Remotionはヘッドレスのchromiumを通してすべてのフレームをスクリーンショットするため、レンダリング時間はフレーム数とフレームごとの複雑さに比例してスケールします。長い動画や多数のバリアントはラップトップやビルドエージェントを占有します。Lambdaはフレームを並列ファンクションに分散させます。一回限りの短いクリップであれば、ローカルでレンダリングしてLambdaはスキップしてください。
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.