| name | remotion-trace |
| description | 参考動画からプロ品質の Remotion 動画を再現するワークフロースキル。 「参考動画から再現したい」「動画トレース」「PVを作りたい」「Remotion動画を参考動画ベースで作りたい」で発動。 |
| triggers | ["参考動画から再現したい","動画トレース","PVを作りたい","Remotion動画を作りたい","参考動画ベースで動画作成","remotion-trace","Remotion"] |
Remotion Video Trace
参考動画からプロ品質の Remotion 動画を再現するためのワークフロースキル。
企業PV制作の実プロジェクト(16イテレーション)で確立した手法を体系化。
トリガーワード
「Remotion動画を作りたい」「PVを作りたい」「参考動画から再現」「動画トレース」
前提条件
- Node.js + Remotion (
mv-composer/ プロジェクト or 新規)
- ffmpeg (フレーム抽出・音声処理)
- yt-dlp (参考動画ダウンロード)
Part 1: Reference Research(参考動画リサーチ)
1.1 参考動画ソース
目的に応じてソースを使い分ける:
| ソース | URL | 用途 |
|---|
| Vimeo Staff Picks | vimeo.com/channels/staffpicks | トランジション・カラーグレーディング |
| Art of the Title | artofthetitle.com | タイトルシーケンス・モーショングラフィックス |
| Stash Media | stashmedia.tv | CM・プロモ映像のトレンド |
| YouTube | 同業種・競合検索 | 企業PV・紹介動画の業界水準把握 |
| ゲームトレーラー | Brikk等 | ダイナミックなカット・エフェクト |
1.2 クリップ収集
「このシーンの表現が欲しい」単位で 5-10秒のクリップ を収集する。動画全体ではなく、特定の演出・トランジションにフォーカス。
yt-dlp --download-sections "*57-59" -o "data/video_refs/{project}/{id}_%(section_start)s-%(section_end)s.%(ext)s" "https://youtube.com/watch?v={id}"
yt-dlp --download-sections "*20-30" -o "data/video_refs/{project}/vimeo_{id}_0020-0030.%(ext)s" "https://vimeo.com/{id}"
1.3 ファイル管理
data/video_refs/{project_name}/
├── {videoId}_{startSec}-{endSec}.mp4 # 参考クリップ
├── frames/ # 抽出フレーム(Part 2で生成)
└── README.md # 各クリップの出典・用途メモ
必ず README.md に出典を記録:
## {videoId}_{start}-{end}.mp4
- 出典: {会社名} 公式PV (YouTube)
- 用途: 人物紹介のワイプ演出(暗背景→矩形飛散→写真露出)
Part 2: Frame Analysis(フレーム分析)
2.1 フレーム抽出
ffmpeg -i data/video_refs/{project}/clip.mp4 \
-vf "fps=8" \
data/video_refs/{project}/frames/clip_%04d.png
ffmpeg -i clip.mp4 -ss 2.0 -t 3.0 -vf "fps=10" frames/%04d.png
2.2 視覚分析チェックリスト
抽出フレームを Read ツールで開き、以下の観点で分析する:
2.3 分析結果の記録
各クリップについて以下を記述:
## clip: {videoId}_{start}-{end}.mp4
### アニメーション分解
- 0.0s: 暗背景 + 白い矩形がランダム配置で飛散
- 0.5s: clipPath inset で左→右に写真がワイプイン
- 1.5s: 写真フル表示 + 左下に名前テロップ(白文字 shadow付き)
- 3.0s: 3ストリップに分割、各ストリップが別アングル
### Remotion実装メモ
- clipPath: `inset(0 ${100 - progress}% 0 0)` で実現可能
- 矩形飛散: position:absolute + ランダム top/left/rotation
2.4 draw.io PNG → React SVGアニメーション化ガイド
draw.io等で作成した図解PNGを、Remotion上のReact SVGアニメーションに変換する手順。
なぜ変換するか
| 観点 | PNG(そのまま) | React SVG |
|---|
| アニメーション | 不可(静止画) | 要素が spring() で順番に登場 |
| 解像度 | 拡大でぼやける | SVGはどんなサイズでもシャープ |
| 微調整 | draw.ioで再編集→再エクスポート | px値やタイミングをコードで即座に制御 |
| 2フェーズ遷移 | 不可 | 1シーンで前半/後半で異なる情報を表示可能 |
変換手順
- 構成要素の分解: draw.ioの図をノード(ボックス)、矢印(コネクタ)、テキストラベルに分解。各要素の座標・サイズ・色をメモ
- Reactコンポーネント化: 各要素をSVGプリミティブで再実装
<rect x={node.x} y={node.y} width={node.w} height={node.h}
rx={8} fill={node.color} opacity={nodeOpacity} />
<text x={node.x + node.w/2} y={node.y + node.h/2}
textAnchor="middle" dominantBaseline="central"
fill="#FFF" fontSize={16}>{node.label}</text>
<line x1={arrow.x1} y1={arrow.y1} x2={arrow.x2} y2={arrow.y2}
stroke="#666" strokeWidth={2} markerEnd="url(#arrowhead)" />
- スタッガードアニメーション付与:
spring() で要素を順番に登場させる
const nodeOpacity = spring({
frame: frame - index * STAGGER_DELAY,
fps, : { : , : , : },
});
nodeScale = ({
: frame - index * ,
fps, : { : , : , : },
});
チェック
静的PNGが <Img> で3秒以上表示されている場合 → 「アニメーション化を検討」フラグを出す。
motion-review スキルの J3 チェック項目と連動。
Part 3: BPM & Audio Analysis(音楽分析)
3.1 BPM解析
ffmpeg -i reference.mp4 -vn -acodec pcm_s16le ref_audio.wav
ffmpeg -i ref_audio.wav -af "showinfo" -f null - 2>&1 | head -50
3.2 Beat-Aligned Duration 設計
BPM = 103 の場合:
1 beat = 60/103 = 0.5825s
5 beats = 2.91s
9 beats = 5.24s
sectionDurations は必ず beat の整数倍にする。これにより BGM の拍とシーン切り替えが自然に同期する。
3.3 BGM 生成(fal.ai Stable Audio)
Stable Audio 2.5(推奨): 最大190秒を一発生成可能。ジョイント問題なし。
const result = await fal.subscribe("fal-ai/stable-audio-25/text-to-audio", {
input: { prompt: "...", seconds_total: 80 },
});
const url = result.data?.audio?.url;
ffmpeg -y -i raw.wav -af "afade=t=out:st=76:d=4" -q:a 2 bgm_final.mp3
代替モデル(fal.ai):
| モデル | API ID | 最大尺 | 用途 |
|---|
| Stable Audio 2.5 | fal-ai/stable-audio-25/text-to-audio | 190秒 | 汎用(推奨) |
| Beatoven maestro | beatoven/music-generation | 150秒 | ライセンス済み商用BGM |
| CassetteAI | cassetteai/music-generator | 180秒 | 低コスト高速 |
| Stable Audio (旧) | fal-ai/stable-audio | 47秒 | 非推奨(分割+crossfadeが必要) |
旧方式(47秒制限の場合のみ): 複数パートを crossfade で連結
ffmpeg -i part1.mp3 -i part2.mp3 \
-filter_complex "[0][1]acrossfade=d=2:c1=tri:c2=tri" \
-y bgm_full.mp3
3.4 ナレーション生成(ElevenLabs TTS)
TaxAccountantDemo v34-v40 で確立したワークフロー。
3.4.1 音声生成
VOICE_ID="StTDrGrPSyfaHGmzwXbj"
SETTINGS='{"stability":0.70,"similarity_boost":0.80,"style":0.10,"use_speaker_boost":true}'
curl -s -X POST "https://api.elevenlabs.io/v1/text-to-speech/${VOICE_ID}" \
-H "xi-api-key: ${ELEVENLABS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"text":"テキスト","model_id":"eleven_multilingual_v2","voice_settings":'"${SETTINGS}"'}' \
--output "narration.mp3"
3.4.2 中国語発音混入対策(重要)
ElevenLabs multilingual v2 は日本語の漢字を中国語読みすることがある。
対策: 問題が起きやすい漢字をひらがなに置換してから生成
税理士 → ぜいりし 記帳 → きちょう 仕訳 → しわけ
取引 → とりひき 損益 → そんえき 即時 → そくじ
即座 → そくざ 監視 → かんし 瞬時 → しゅんじ
24時間365日 → にじゅうよじかん さんびゃくろくじゅうごにち
12,800円 → いちまん にせん はっぴゃく えん
stability を 0.55→0.70 に上げると中国語混入が減る。
3.4.3 Gemini 発音評価(必須QA)
import google.generativeai as genai
model = genai.GenerativeModel("gemini-2.0-flash")
audio = genai.upload_file("narration.mp3")
resp = model.generate_content([
audio,
"この日本語音声を正確に書き起こしてください。"
"中国語の発音が混入していないか、読み間違いがあれば指摘。"
])
全クリップに対して実行し、問題があるクリップのみ再生成。
3.4.4 atempo 速度調整
ナレーション尺がシーン尺を超える場合、ffmpeg atempo で調整:
ffmpeg -y -i raw.mp3 -af "atempo=1.42" adjusted.mp3
限界: atempo 1.35倍が上限。超えると早口で聞き取りづらい → テキストを短縮するか、シーン尺を延長。
3.4.5 Remotion への組み込み
interface Props {
narrationPrefix?: string;
}
{p.narrationPrefix && starts.map((st, i) => {
const padNum = String(i + 1).padStart(2, "0");
return (
<Sequence key={`narr-${i}`} from={st} durationInFrames={frames[i]}>
<Audio src={staticFile(`${p.narrationPrefix}${padNum}.mp3`)} volume={1.2} />
</Sequence>
);
})}
音量バランス: ナレーション volume={1.0-1.2}, BGM volume={0.18-0.25}
3.5 音声ルール
- SE なし・BGM 一本で駆け抜けるのが基本スタイル
- 転調は動画が 45秒超の場合のみ検討
- ライブ映像等の直接挿入時は
muted で BGM と干渉させない
- ナレーション付き動画: BGM volume を 0.20-0.25 に下げてナレーションを際立たせる
- キャプションとナレーションの整合: キャプションテキストはナレーション内容と一致させる(数値の%表記も統一)
Part 4: Storyboard(絵コンテ作成)
4.1 シーン分割表
以下のテンプレートで全シーンを定義してから実装に入る:
| Scene | 秒数 | Beat数 | 参考クリップ | 演出概要 | コンポーネント名 |
|---|
| 01 | 2.91 | 5 | {clip_id} | ロゴ blur→focus + パーティクル | LogoFocusIn |
| 02 | 2.91 | 5 | {clip_id} | バリュー訴求パンチイン | ValuePunch |
| 03 | 5.24 | 9 | {clip_id} | メンバー紹介ワイプ | MemberShowcase |
| ... | | | | | |
4.2 Props インターフェース設計
interface CompositionProps {
bgmSrc?: string;
sectionDurations?: number[];
logoSrc?: string;
}
export const DEFAULT_PROPS: CompositionProps = {
sectionDurations: [2.91, 2.91, 5.24, 5.24, 12.23, 6.99, 8.74],
bgmSrc: '{project}/audio/bgm.mp3',
};
4.3 sectionDurations 優先
sectionDurations 配列を先に確定 → 各シーンコンポーネントはフレーム数を受け取って内部で配分。Root.tsx の durationInFrames は Math.round(sum(sectionDurations) * FPS) で自動計算。
Part 5: Implementation Patterns(Remotion テクニック集)
企業PV制作で確立した 13 のテクニック。新プロジェクトでもそのまま適用可能。
P1: Blur→Focus ロゴ
const blurPx = interpolate(frame, [0, 20], [20, 0], { extrapolateRight: 'clamp' });
ロゴ画像を最初ぼかして徐々にシャープに。テキストでロゴを再現しない(フォント不一致リスク)。
P2: パンチインテキスト
const scale = spring({ frame: f, fps, config: { damping: 12, mass: 0.5, stiffness: 200 } });
テキストが弾むように出現。damping を低くしすぎると振動が目立つので 10-15 が適正。
P3: clipPath ワイプ
const progress = interpolate(frame, [startF, endF], [0, 100], { extrapolateRight: 'clamp' });
右→左に写真がワイプイン。方向は inset() の値を変えるだけ。
P4: 矩形飛散→写真ワイプ
暗背景にランダム配置の白/色付き矩形を散らし、clipPath で写真を露出させる二段階演出。
{rects.map((r, i) => (
<div key={i} style={{
position: 'absolute', top: r.y, left: r.x,
width: r.w, height: r.h, background: r.color,
transform: `rotate(${r.rot}deg)`,
opacity: interpolate(f, [0, phaseAEnd], [1, 0])
}} />
))}
P5: 3ストリップ分割
画面を縦3分割し、各ストリップに異なるアングルの写真を配置。ストリップごとに微妙にタイミングをずらすとリッチに見える。
P6: Apple風カードグリッド
const scrollY = interpolate(frame, [0, dur], [0, -totalHeight], { extrapolateRight: 'clamp' });
カーソル位置はスクロールオフセットからカードの実画面座標を逆算して合わせる。
P7: 4パネルスタガー
const panelDelay = panelIndex * 4;
const slideIn = interpolate(frame - panelDelay, [0, 15], [100, 0], { extrapolateRight: 'clamp' });
const imgIdx = (cycleIndex + pi * Math.ceil(images.length / 4)) % images.length;
4分割パネルの更新タイミングをずらす。全パネル同じ画像にならないよう baseOffset 必須。
P8: カウントアップ
const eased = 1 - Math.pow(1 - ratio, 3);
const displayNum = Math.round(eased * targetNumber);
spring() でカウントアップすると数値が上がって下がって戻る。cubic ease-out を使うこと。
P9: CSS パーティクル
{particles.map((p, i) => (
<div key={i} style={{
position: 'absolute', borderRadius: '50%',
width: p.size, height: p.size, background: p.color,
top: `${p.y}%`, left: `${p.x}%`, opacity: p.opacity,
animation: `float ${p.duration}s ease-in-out infinite`,
}} />
))}
i2v(Kling等)は品質が微妙 → CSS パーティクルやRemotionアニメーションで代替する。
P10: SceneWrap exit
const exitProgress = interpolate(frame, [dur - exitFrames, dur], [0, 1], { extrapolateLeft: 'clamp' });
シーン終了時に scale: 0.95 + opacity: 0 で自然に次シーンへ遷移。noExit prop で無効化可能。
P11: 顔保護
<div style={{ position: 'relative', overflow: 'hidden' }}>
{}
<Img src={src} style={{ position: 'absolute', width: '120%', filter: 'blur(20px)', objectFit: 'cover' }} />
{}
<Img src={src} style={{ position: 'relative', objectFit: 'contain', width: '100%', height: '100%' }} />
</div>
P12: テキスト可読性
style: {
textShadow: '0 2px 8px rgba(0,0,0,0.8), 0 0 20px rgba(0,0,0,0.5), 0 0 40px rgba(0,0,0,0.3)',
}
P13: 動画直挿入
import { OffthreadVideo } from 'remotion';
ライブ映像等は muted で挿入し、BGM トラックと干渉させない。サムネイルは ffmpeg で事前抽出:
ffmpeg -i video.mp4 -ss 5 -frames:v 1 thumb.jpg
Part 6: Comparison Loop(比較改善ループ)
6.1 レンダリング
cd mv-composer
npx remotion render src/index.ts {CompositionId} out/{Name}_v{N}.mp4
6.2 出力動画のフレーム抽出
ffmpeg -i out/{Name}_v{N}.mp4 -vf "fps=8" data/video_refs/{project}/output_frames/v{N}_%04d.png
6.3 比較分析
参考動画フレームと出力フレームを並べて、プロの映像クリエイター視点 で以下をチェック:
6.4 修正→再レンダリング
指摘事項を修正 → v{N+1} としてレンダリング → 再度比較。
v1→v18 のような反復は正常。5-10回のイテレーションで収束するのが標準。
Part 7: Lessons Learned(禁止事項・推奨事項)
禁止
| ルール | 理由 |
|---|
| 絵文字をアイコンに使わない | レンダリング環境でフォント欠落 → SVG/画像を使う |
| i2v(Kling/fal.ai等)に頼らない | 品質が微妙、Remotion CSS で代替可能 |
| spring() でカウントアップしない | 振動して数値が上がって下がる |
| objectFit "cover" で人物写真 | 顔が切れる → "contain" + blurred bg |
| 黒帯テロップ | ダサい → text-shadow で可読性確保 |
| 7MB超の画像をそのまま使う | デコードエラー → sips --resampleWidth 1200 |
| テキストでロゴを再現 | フォント不一致 → ロゴ画像を使う |
推奨
| ルール | 理由 |
|---|
| SE なし・BGM 一本 | 駆け抜ける勢いが出る |
| sectionDurations を BPM 倍数に | 音ハメが自然になる |
| 画像は 1200px 幅以下にリサイズ | Remotion のデコード安定性 |
| シーン間を ±5フレーム オーバーラップ | 黒フレーム防止 |
| 名前テロップは1シーン1回 | 2回出ると違和感 |
| パネル画像は baseOffset で分散 | 全パネル同じ画像防止 |
| ライブ映像は muted 挿入 | BGM との干渉防止 |
| ブランドカラーは OGP/公式サイトから取得 | 目視推定は不正確 |
ワークフロー全体像
[1. Reference Research]
↓ 参考クリップ収集(5-10秒単位)
[2. Frame Analysis]
↓ フレーム抽出 → 演出分解
[3. BPM & Audio]
↓ テンポ解析 → sectionDurations 確定 → BGM 生成
[4. Storyboard]
↓ シーン分割表 → Props設計
[5. Implementation]
↓ パターン集(P1-P13)を活用して実装
[6. Comparison Loop] ←─── 5-10回反復
↓ レンダリング → フレーム比較 → 修正
[7. Final Check]
↓ Lessons Learned チェック → 完成