wrapped-video について
Remotion を使い、個人やアカウントのデータからパーソナライズされた縦型の振り返り動画を作るスキルです。同じテンプレートで多数のレコードを動画化する制作者やチーム向けです。
主な機能
- 01振り返りデータの型付きスキーマ
- 02再利用可能なシーン構成
- 03データ連動のテキスト、統計値、配色
- 04縦型 9:16 動画のレイアウト指針
- 05レコード単位の MP4 一括レンダリング
- 06フレーム単位のプレビューと検証
活用シーン
- 01年間のユーザー利用状況の振り返り作成
- 02個別のフィットネスや読書動画の生成
- 03営業や学生の進捗まとめの作成
- 04データ行から共有用動画をレンダリング
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ iart-ai/ explainer-video-skills --skill wrapped-videoインストール後、Claude Code で/wrapped-videoを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
README.mdreferences/batch-pipeline.mdremotion-recipes.mdscene-grammar.md詳細
Wrapped Video
「Spotify Wrapped」風の振り返り動画を作成します。1人(またはアカウント、チーム、年)のデータ行を、目を引いて共有しやすい縦型動画に仕上げます。基本となるのは、1つのテンプレート × データテーブル → 多数のパーソナライズ動画です。テンプレートを一度作成すれば、各行に合わせた独自の動画をレンダリングできます。
使用する場面
- ユーザーごとの統計情報がある製品の、年次振り返りや「あなたの2026年 Wrapped」。
- パーソナライズされたデータ動画:フィットネスの1年、読書の1年、支出の振り返り、ゲームの統計、営業担当者の振り返り、学生の進捗。
- 「同じ動画を、各人の数値に合わせて作る」という成果物が求められる場合。1本でも10万本でも対応できます。
これはデータ → 共有したくなるコンテンツのパターンであり、手作業で編集する動画ではありません。データテーブル(またはレコードごとのデータ)がない場合は、別のスキルを使用してください。
絶対に譲れない2つの条件
- **すべてをデータに基づかせる。**見出し、数値、名前、色はすべて、ハードコードではなく props から取得します。データ行の値を埋め込めないシーンは、Wrapped に含めません。
- **スクリーンショットに適した設計にする。**各シーンは2秒以内に読み取れ、静止画でも見栄えがよくなければなりません。その静止画がストーリーに共有されます。再生中ではなく、一時停止した状態を想定してデザインしてください。
Wrapped のシーン構成
Wrapped は、短いシーンのタイプを固定の順番に並べたもので、各シーンは約2.5~4秒です。5~7個を選び、盛り上がる順に並べます。毎年、構成は同じです。変わるのはデータと配色だけです。
| シーンのタイプ | 役割 | データの形式 |
|---|---|---|
| イントロ / 「あなたの2026年 Wrapped」 | ブランドを示し、配色を設定する | 名前、年 |
| 大きな数値の発表 | 主役となる統計を1つ、大きくカウントアップする | 数値 + 単位 + ラベル |
| 上位X項目のリスト | 1→5の順位を付け、順番に表示する | {rank, label, value} の配列 |
| 最上級 / ペルソナ | 「あなたは上位1%です」のようなタイプを示す | 算出されたティア / ラベル |
| 比較 | 「リスナーの92%より多く」 | パーセンタイルまたは比率 |
| 時系列 / ヒートマップ | 「最も忙しかった月は3月でした」 | 系列またはピーク値 |
| アウトロ / 共有カード | ロゴ + ハンドル + CTAを表示し、静止状態を保つ | 名前、ハンドル |
盛り上がる流れになるよう、小さな背景情報から始め、最も印象的でパーソナルな統計をクライマックスにし、その後に静止した共有カードを表示します。タイミングを含む7シーン構成のストーリーボード全体は、references/scene-grammar.md を参照してください。
まとめストーリーの流れ
シーンの文法は骨格です。ストーリーの流れがあることで、Wrapped は単なるダッシュボードではなく、贈り物のように感じられます。まとめ動画は、視聴者一人にまつわる5拍子の小さなドラマです。それぞれの発見が前よりも大きく、よりパーソナルに感じられるように構成しましょう。
| 拍 | 役割 | 対応する要素 |
|---|---|---|
| 盛り上げ | この瞬間をブランドらしく演出し、本人のためのものだと伝える | イントロ:「{name}さん、あなたの{year}」 |
| 段階的な発見 | 重要度が高まっていく統計を重ねる | Top-X、ジャンル、時間帯の傾向 |
| 「大きな数字」 | 主役となる統計を1つ選び、最大規模で、最も長く見せる | 大きな数字/カウントアップのクライマックス |
| パーソナライズされた最高評価 | 何をしたかだけでなく、どんな人かを表す | ペルソナ/パーセンタイルのランク |
| シェアしたくなる締めくくり | 投稿したくなる静止ポスター | エンディングのシェアカード |
**インパクトが高まるよう統計を並べましょう。最も小さなものから始め、最大のものを最後にします。**すべての統計を感情に訴える力(絶対値、珍しさ、どれだけうれしい内容か)で順位付けし、低いものから高いものへ順に見せます。主役となる数字から始めてはいけません。その後に盛り上がる余地がなくなり、残りの動画が尻すぼみになってしまいます。クライマックスの統計は、最も大きく、最も長く見せましょう。それ以前のすべては、そのための導入です。
**フックは「これはあなたのことです」です。**Wrapped が成功するのは、視聴者が主人公だからです。最初の2秒でそれを伝えましょう。名前から始め、ユーザーごとの accent を使い、すべての文を二人称で書きます(「あなたは412組のアーティストを聴きました」)。一般的な文言(「トップジャンル:インディー」)では魔法が解けてしまいます。「あなたはインディー派です」のように言い換えましょう。異なる2人のユーザーにまったく同じように見えるフレームは、役割を果たせていません。
**最後のシェア用フレームをゴールとして設計しましょう。**全体のストーリーは、投稿する価値のあるポスターを届けるためにあります。エンディングはスタッフロールではなく、締めくくりです。最高評価や主役の数字を見出しとしてもう一度示し、名前と@ハンドル、ロゴ、短い CTA を配置します。スクリーンショットや自動ループがきれいに決まるよう、完全に静止した状態を2秒以上保ちましょう。最初にこのフレームを決め、そこへ向けて盛り上げていきます。コピーライティングのランクとストーリーボード全体は references/scene-grammar.md を参照してください。
データ → 動画の形
1レコード分の型付きスキーマを定義します。動画全体はそのスキーマに対する純粋関数です。
// src/schema.ts
import { z } from "zod";
export const wrappedSchema = z.object({
name: z.string(),
year: z.number(),
minutesListened: z.number(),
topArtists: z.array(z.object({ rank: z.number(), label: z.string(), value: z.number() })),
topGenre: z.string(),
percentile: z.number(), // 0–100, "top X%"
accent: z.string(), // per-user palette, e.g. "#1DB954"
});
export type Wrapped = z.infer<typeof wrappedSchema>;
コンポジションに defaultProps と schema として登録すれば、レンダリングごとに props を差し替えるだけで済みます。
// src/Root.tsx
import { Composition } from "remotion";
import { Wrapped as Recap } from "./Wrapped";
import { wrappedSchema } from "./schema";
export const Root = () => (
<Composition
id="Wrapped"
component={Recap}
schema={wrappedSchema}
durationInFrames={30 * 22} // 22s @ 30fps
fps={30}
width={1080} height={1920} // 9:16 vertical — the share format
defaultProps={{ name: "Sam", year: 2026, minutesListened: 41203,
topArtists: [{ rank: 1, label: "Phoebe Bridgers", value: 312 }],
topGenre: "Indie", percentile: 3, accent: "#1DB954" }}
/>
);
目玉の演出:大きな数字のカウンター
カウントアップは、あらゆるWrappedの鼓動です。useCurrentFrame() を使って動かし(レンダリング時にちらつく setState/setInterval は使わないでください)、スプリングでイージングし、toLocaleString() で書式設定します。
import { useCurrentFrame, useVideoConfig, spring, interpolate } from "remotion";
export const BigNumber: React.FC<{ value: number; label: string; accent: string }> =
({ value, label, accent }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const progress = spring({ frame, fps, config: { damping: 200 } }); // 0→1, settles
const shown = Math.round(interpolate(progress, [0, 1], [0, value]));
const pop = interpolate(progress, [0, 1], [0.6, 1]); // overshoot-free scale-in
return (
<div style={{ display: "flex", flexDirection: "column", alignItems: "center",
justifyContent: "center", height: "100%", transform: `scale(${pop})` }}>
<span style={{ fontSize: 220, fontWeight: 900, color: accent, lineHeight: 1,
fontVariantNumeric: "tabular-nums" }}>{shown.toLocaleString()}</span>
<span style={{ fontSize: 48, color: "#fff", marginTop: 24 }}>{label}</span>
</div>
);
};
数字が増減する際に幅が揺れないよう、tabular-nums を使います。Top-X リストでは、項目ごとにスプリングを1つ再利用し、delay を段階的にずらします(順位1から開始)。詳しくは references/remotion-recipes.md を参照してください。
縦型9:16、視聴を一時停止してもらうデザイン
1080×1920 でレンダリングします。Stories/Reels/TikTok ではトリミングや UI の重ね合わせがあるため、文字や重要な数字はすべて中央80%の高さに収め、**上部12%と下部18%**を避けてください。各フレームのアイデアは1つに絞り、大きな文字を使い、単色またはグラデーションの背景に高コントラストのアクセントを置きます。ユーザーごとの accent カラーを使うと、シェアする動画がそれぞれパーソナルになります。セーフエリアの全体マップは references/scene-grammar.md を参照してください。
バッチ処理:1つのテンプレートから多数の動画を生成
これが成果につながります。renderMedia を使い、データの各行を inputProps として渡して、それぞれを個別の MP4 にレンダリングします。ジョブの CSV を生成し、ループ処理して、レコードごとにファイル名を付けます。
// render-all.ts — run with: npx tsx render-all.ts
import { bundle } from "@remotion/bundler";
import { renderMedia, selectComposition } from "@remotion/renderer";
import { wrappedSchema } from "./src/schema";
import users from "./users.json"; // array of rows matching the schema
const serveUrl = await bundle({ entryPoint: "./src/index.ts" });
for (const user of users) {
const props = wrappedSchema.parse(user); // validate the row
const comp = await selectComposition({ serveUrl, id: "Wrapped", inputProps: props });
await renderMedia({
composition: comp, serveUrl, codec: "h264",
inputProps: props,
outputLocation: `out/wrapped-${user.id}.mp4`,
});
console.log("rendered", user.id);
}
数千行を扱う場合は、マシンやLambdaに処理を分散し、同一の props セットは重複を排除します。パイプライン、スケーリング、Node + CLI のバッチ処理の別案については references/batch-pipeline.md を参照してください。
構築チェックリスト
- 表示される文字列や数値はすべて props から取得し、ハードコードしません。
- シーンは5~7個とし、段階的に盛り上げ、最も大きくパーソナルな統計をクライマックスにします。
- 数字は
useCurrentFrame+ spring でカウントアップし、tabular-numsとロケールに応じた書式を使います。 - 9:16、1080×1920。重要な内容は中央80%に配置し、上部12%/下部18%を避けます。
- 各シーンは2秒未満で読み取れ、静止画でも見栄えよくします(スクリーンショットに撮られるためです)。
- すべての行をレンダリング前に検証するスキーマを1つ用意し、バッチスクリプトでレコードごとにファイル名を付けます。
配信と検証(レンダリング済みの静止画 → MP4)
同梱ヘルパー(
scripts/):scripts/contact-sheet.sh sheet.png f-hook.png f-mid.png f-end.pngで静止画をタイル状に並べ、scripts/probe-mp4.sh out.mp4 [WxH] [fps]でエンコード結果を検証します。詳しくはscripts/README.mdを参照してください。
Wrapped はデータ行ごとにレンダリングされる Remotion コンポジションです。フレームが決定的で、シーク用のハーネスなしに、任意の正確なフレームをヘッドレスでレンダリングできます。成果物は各人の正確な数値を含む MP4(多くの場合、複数本)です。バッチ処理の前に、代表的な1行を静止画で検証してください。
出力要件:
- コンポジションを登録した Remotion プロジェクト(
<Composition>+ zodschema+defaultProps)。すべてのモーションはフレーム駆動にします(タイマー /Date.now()/Math.random()は使わず、カウントアップにはuseCurrentFrame+ spring を使用します)。 - 成果物 = 各行に対してレンダリングされた
out/wrapped-*.mp4(および、任意の行を再レンダリングできるプロジェクト)。 - ユーザーごとの統計値を props に含め、レンダリング前にスキーマで検証します。9:16 1080×1920。
- 長さがデータに依存する場合は、手作業で設定せず
calculateMetadataで算出します。
検証ループ — 1行分の静止画 → 確認 → バッチ処理。 まず代表的なユーザーのフレームをレンダリングします(エンコード不要で低コスト)。レイアウトやデータの不具合をN回ではなく1回で見つけられます。
# Frame-exact stills at start / mid / end for ONE representative row — pass that row as props
npx remotion still Wrapped out/f-start.png --frame=0 --props='{...one user...}'
npx remotion still Wrapped out/f-mid.png --frame=N --props='{...one user...}'
npx remotion still Wrapped out/f-end.png --frame=L --props='{...one user...}' # L = durationInFrames - 1
# Inspect: every visible string/number comes from that row and is EXACT (big-number reveal lands on the
# real value, top-X ranks/labels correct); key content inside center 80%, clear of top 12% / bottom 18%.
# Only after the representative stills check out, batch-render every row:
npx tsx render-all.ts
npx remotion compositionsはdurationInFrames/fpsを読み取り、終了フレームと大きな数値の表示が落ち着くフレームを決定します。- README デモ GIF を手軽に作成:
npx remotion render Wrapped out/demo.gif --codec=gif --props='{...}'。
完了前に確認すること:
npx remotion stillで、代表的な行のフレーム0、中央、最終フレームがエラーやアセット / フォントの欠落なくレンダリングされること。- 大きな数値が、表示が落ち着くフレームで正確に props の値になること。上位Xの順位 / ラベル / 値がその行と一致すること。
- 9:16:重要な数値 / 文字を中央80%内に配置し、上部12% / 下部18%を避けること。各フレームが静止した状態でも読み取れることを確認します。
- フレーム駆動のみとし、
Date.now()/Math.random()/ タイマーを使わないこと。バッチ処理の前に、すべての行をスキーマで検証します。 - 代表的な MP4 をエンコードして再生できることを確認してから、すべての行をバッチ処理します(レコードごとにファイルを作成)。必要に応じて README 用の GIF も作成します。
参考ファイル
references/scene-grammar.md— 全7シーンのストーリーボード。フレームのタイミング、ビルド / クレッシェンドのロジック、最上級表現とパーセンタイルのコピーライティングパターン、9:16のセーフエリアマップを掲載しています。references/remotion-recipes.md— 実行可能なシーンコンポーネント:段階的に表示する上位Xリスト、パーセンタイルバー、<Series>によるシーンの連続表示、トランジション、フォント、ちらつきを防ぐアニメーションのルールを掲載しています。references/batch-pipeline.md— CSV / JSON から複数の MP4 を生成する方法:スキーマ検証、レンダリングループ、並行処理、Lambda によるファンアウト、ファイル命名を掲載しています。