remotion-video について
remotion-videoは、映像のブリーフからRemotionプロジェクトを構築するスキルです。複数の画角、アニメーション、音声のタイミング、再利用可能なビジュアル部品を組み込み、Remotion Studioで開ける形にします。
主な機能
- 013種類の画角のコンポジション
- 02springベースのシーンアニメーション
- 03シーンの時間設定とクロスフェード
- 04シーンに連動する音量制御
- 05再利用可能なシーンとビジュアル部品
- 06モバイル向けのサイズ調整
活用シーン
- 01製品デモ動画のプロジェクト構築
- 02デスクトップとモバイル向け動画の作成
- 03時間に沿ったシーン構成の作成
- 04動くテキストやターミナル演出の追加
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ tushaarmehtaa/ tushar-skills --skill remotion-videoインストール後、Claude Code で/remotion-videoを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/audio.mdcheatsheet.mdprimitives.md詳細
Remotion の動画プロジェクトを、複数フォーマットのコンポジション、スプリングアニメーション、ビートに同期した音声、再利用可能なシーンプリミティブとともにスキャフォールドします。すぐに npx remotion studio を実行できるプロジェクトを出力します。
フェーズ 1:概要を確認する
コードを書く前に、不足している情報を質問します。
I'll scaffold a Remotion video project. Quick decisions:
1. Product name? (used for composition IDs and file names)
2. One-line pitch? (the core message of the video)
3. Scene ideas? (3-8 scenes — what story does each beat tell?)
4. Color vibe?
a) Dark + amber accent (#d97706) — warm, editorial
b) Dark + monochrome — clean, minimal
c) Custom — give me bg, text, and accent hex values
5. BPM of your music track? (default: 115)
6. Font? (default: system stack — or provide .woff2 files for Geist/custom)
ユーザーが会話の中ですでに動画について説明している場合は、質問する前に回答を読み取ります。すでにわかっていることは質問しません。
ユーザーが回答を省略した場合のデフォルト: 25 秒、30fps、6 シーン、ダーク+アンバー、115 BPM、システムフォントスタック。
フェーズ 2:プロジェクトをスキャフォールドする
次のファイルツリーを正確に作成します。
[name]-video/
├── package.json
├── tsconfig.json
├── src/
│ ├── index.ts
│ ├── Root.tsx
│ ├── [Name]Demo.tsx # orchestrator
│ └── [Name]Scenes.tsx # scenes + tokens + primitives
├── public/
│ ├── audio/ # user drops .mp3 here
│ └── fonts/ # .woff2 files if custom font
└── out/
package.json
{
"name": "[name]-video",
"type": "commonjs",
"dependencies": {
"@remotion/cli": "^4.0.421",
"@types/react": "^19.2.13",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"remotion": "^4.0.421",
"typescript": "^5.9.3"
}
}
ローカルのフォントファイルを読み込む場合に限り、@remotion/fonts: "^4.0.434" を追加します。 それ以外の場合は含めません。
"type": "commonjs" が必要です。 ESM は Remotion のバンドラーで問題を引き起こします。
tsconfig.json
{
"compilerOptions": {
"target": "ES2018",
"module": "commonjs",
"jsx": "react-jsx",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"]
}
src/index.ts
常に同じ内容にします。
import { registerRoot } from "remotion";
import { RemotionRoot } from "./Root";
registerRoot(RemotionRoot);
フェーズ 3:コンポジション(Root.tsx)
3 つのコンポジションを登録します。同じコンポーネントですべてをレンダリングし、アスペクト比の検出はコンポーネント内で行います。
import { Composition } from "remotion";
import { Demo } from "./[Name]Demo";
export const RemotionRoot = () => (
<>
<Composition id="[Name]Demo" component={Demo} durationInFrames={750} fps={30} width={1920} height={1080} />
<Composition id="[Name]DemoMobile" component={Demo} durationInFrames={750} fps={30} width={1080} height={1920} />
<Composition id="[Name]DemoMobileLandscape" component={Demo} durationInFrames={750} fps={30} width={1334} height={750} />
</>
);
30fps で 750 フレーム = 25 秒です。 ユーザーが異なる長さを指定した場合は調整します。
モバイル用のコンポーネントを個別に作成してはいけません。 コンポーネント内でフォーマットを検出します。
const { width, height, fps } = useVideoConfig();
const isPhone = height > width;
const isMobileLandscape = width > height && width <= 1400;
const globalScale = isPhone ? 1.14 : isMobileLandscape ? 1.08 : 1;
シーン領域全体を、globalScale を使ってスケーリングした <AbsoluteFill> で囲みます。
カスタムフォントを読み込む場合は、コンポジションより前の Root.tsx のモジュールレベルでフォントローダーを呼び出します。
フェーズ 4:シーンアーキテクチャ(オーケストレーター)
オーケストレーターファイル([Name]Demo.tsx)では、タイミングの定義、音声の接続、シーケンスの配置の 3 つを行います。
シーンのタイミングオブジェクト
const scenes = {
hook: { from: 0, dur: 90 },
xfade1: { from: 86, dur: 10 },
pain: { from: 90, dur: 120 },
xfade2: { from: 206, dur: 10 },
// ... continue for each scene
} as const;
ルール:
- クロスフェードは直前のシーンと 4 フレーム重ねます(
from = prev scene end - 4) - クロスフェードの長さ:10~16 フレーム
- シーンの長さ:60~195 フレーム(2~6.5 秒)。短めにします。どのシーンも長すぎてはいけません。
- TypeScript が値をリテラルとして扱うには
as constが必要です
シーケンスの配置
すべてのシーン+クロスフェードのペアで繰り返すパターン:
const frame = useCurrentFrame();
<Sequence from={scenes.hook.from} durationInFrames={scenes.hook.dur}>
<SceneHook frame={frame - scenes.hook.from} duration={scenes.hook.dur} />
</Sequence>
<Sequence from={scenes.xfade1.from} durationInFrames={scenes.xfade1.dur}>
<CrossfadeTransition frame={frame - scenes.xfade1.from} dur={scenes.xfade1.dur} />
</Sequence>
各シーンにローカルフレーム 0 から始まるフレームを渡すため、必ず frame - scenes.X.from を渡します。 シーンでグローバルフレームを参照してはいけません。
音声
<Audio src={staticFile("audio/music.mp3")} volume={(f) => getMusicVolume(f)} />
references/audio.md にある 5 層システムを使って getMusicVolume を構築します。scenes オブジェクトとトランジションのフレーム番号を渡します。
フェーズ 5:シーンを作成する
すべてのシーンコンポーネント、デザイントークン、プリミティブを [Name]Scenes.tsx に配置します。
デザイントークン
ユーザーが選んだ色に基づいて設定します。
ダーク+アンバーアクセント:
export const colors = {
bg: "#000000", surface: "#080808", surfaceRaised: "#111111",
border: "#191919", borderHover: "#333333", muted: "#555555",
text: "#999999", heading: "#ffffff",
accent: "#d97706", accentDim: "rgba(217,119,6,0.19)",
accentGlow: "rgba(217,119,6,0.07)", accentBright: "rgba(217,119,6,0.35)",
};
ダーク+モノクロ:
export const colors = {
bg: "#09090b", surface: "#18181b", border: "#27272a",
textPrimary: "#e4e4e7", textSecondary: "#c2c2cb",
textMuted: "#ababb5", textDim: "#9595a0", accent: "#e4e4e7",
};
次のプリミティブを含める
references/primitives.md からコピーします。
- SceneWrap — すべてのシーンで使用します。非単調な inputRange ガードを備えたフェードイン/フェードアウトを処理します。
- SlamText — 単語ごとのスプリング表示。見出しに使用します。
- CrossfadeTransition — シーン間で不透明度を高めます。
zIndex: 100が必須です。 - TypingEffect — 点滅するカーソルを備えたターミナル/入力シミュレーションです。
- GrainOverlay — SVG ノイズテクスチャ(アンバーの雰囲気の場合のみ)。
opacity: 0.035、zIndex: 50。 - StageLight + AmbientGlow — ゆっくり動く放射状グラデーション(アンバーの雰囲気の場合のみ)。
シーンのプレイブック
ユーザーの要望に応じて、このメニューからシーンを選びます。
| シーンの種類 | 長さ | 目的 |
|---|---|---|
| フック | 2-4.5s | SlamText の見出し+スプリングアニメーションのサブテキスト。注目を集めます。 |
| ペイン/問題 | 4-5s | 古いやり方をターミナルのタイピングで表示します。「新しいセッション」のフラッシュでリセットします。 |
| スナップ/解決策 | 2-3s | 1 つのコマンドを入力し、結果を表示して、光のパルスとともに「完了。」と表示します。 |
| 読み込み中 | 1.5-2s | 絵文字+揺れ+プログレスバー。期待感を高めます。 |
| 結果/カード | 4-6.5s | アイテムがさまざまな方向から飛び込んできます。出力の品質を示します。 |
| 機能グリッド | 2-3s | 2x3 または 3x2 のグリッドで、スプリングアニメーションを使って順番に登場します。 |
| フィードバック/チャット | 3-4s | チャットバブル:ユーザーの質問は右にスライドし、AI の応答は左にスライドします。 |
| スクロール/カルーセル | 5-6.5s | アイテムが入場・退出のトランジションとともに中央を順番に通過します。 |
| インストール | 3s | 見出し+インストールコマンドのタイピングアニメーション+「完了。」 |
| CTA | 3s | SlamText +脈打つボタン+URL。fadeOut: 0(フェードなしで動画を終了)。 |
シーン内でよく使うパターン:
- すべてのテキスト要素の登場には
spring()を使い、動きにinterpolate()を直接使わないでください。 - 順番に登場させる場合:各アイテムに 3-6 フレームの遅延を設定します。
- 完了時の光のパルス:
interpolate(Math.sin(frame * 0.1), [-1, 1], [0.03, 0.12]) - 控えめなズームドリフト:
interpolate(frame, [0, duration], [1, 1.03])
レスポンシブなサイズ設定
すべてのサイズ定数で isPhone に応じた分岐を設定します。
const fontSize = isPhone ? 148 : 88; // headlines
const subSize = isPhone ? 72 : 46; // subtext
const cmdSize = isPhone ? 72 : 48; // terminal commands
const panelW = isPhone ? 920 : 800; // UI panels
const borderRadius = isPhone ? 20 : 14; // card corners
スマートフォン向けのサイズは、デスクトップ向けの約 1.4-2 倍です。線形スケールではありません。
スプリング設定の一覧、レスポンシブ値、レンダーコマンドについては、references/cheatsheet.md を参照してください。
フェーズ 6:注意点
次の点を見落とすと、動画に気づきにくい不具合が発生します。
- 外挿は必ずクランプしてください。 すべての
interpolate()呼び出しに{ extrapolateLeft: "clamp", extrapolateRight: "clamp" }が必要です。指定しないと、値が 0/1 を超えて大きくなります。 - 入力範囲が単調でない場合のクラッシュ。 SceneWrap で
fadeOut=0にすると、inputRange に重複した値が設定されます。プリミティブにあるsafeEnd = Math.max(duration - fadeOut, fadeIn + 1)のガードで修正されます。 - レンダリング時のフォントのタイムアウト。 フォントの読み込みに使う
delayRender()は、700 を超えるフレームでタイムアウトする場合があります。--timeout=60000を使用してください。 - クロスフェードでの zIndex。 クロスフェードのオーバーレイがシーンのコンテンツより上に表示されるよう、
zIndex: 100を設定する必要があります。 - ローカルフレームとグローバルフレーム。 シーンコンポーネントには、必ず
frame - scenes.X.fromを渡してください。 - シーンオブジェクトに
as constを指定する。 TypeScript が値をリテラルとして扱うために必要です。 - remotion.config.ts は不要です。 CLI のデフォルト設定で問題なく動作します。
- package.json の
type: "commonjs"。 必須です。ESM では Remotion のバンドラーが動作しません。 - SceneWrap の背景色。 AbsoluteFill に
backgroundColor: colors.bgを設定してください。背景が透明だと、クロスフェード中に前のシーンが見えてしまいます。 - カーソルの点滅速度。 非対称のしきい値
[-1, -0.2, 0.2, 1] → [0, 0, 1, 1]とともにMath.sin(frame * 0.25)を使うと、60% の表示率でリアルな点滅になります。
検証
[ ] npm install completes without errors
[ ] npx remotion studio opens and shows all 3 compositions
[ ] Desktop composition (1920x1080) renders without crashes
[ ] Mobile portrait composition (1080x1920) renders — text is readable
[ ] Mobile landscape composition (1334x750) renders
[ ] No interpolate() calls without extrapolation clamping
[ ] SceneWrap has the safeEnd/safeDur guard for fadeOut=0
[ ] Every scene component receives local frame (frame - scenes.X.from)
[ ] Crossfade overlays have zIndex: 100
[ ] Audio plays with volume changes across transitions
[ ] Full render completes: npx remotion render [Id] out/video.mp4 --timeout=60000