mcpField
W
wrapped-video

wrapped-video

github.com/iart-aiクリエイティブ・メディア601.4k 回インストール18 週間前に更新
リポジトリ

wrapped-video について

Remotion を使い、個人やアカウントのデータからパーソナライズされた縦型の振り返り動画を作るスキルです。同じテンプレートで多数のレコードを動画化する制作者やチーム向けです。

主な機能

  1. 01振り返りデータの型付きスキーマ
  2. 02再利用可能なシーン構成
  3. 03データ連動のテキスト、統計値、配色
  4. 04縦型 9:16 動画のレイアウト指針
  5. 05レコード単位の MP4 一括レンダリング
  6. 06フレーム単位のプレビューと検証

活用シーン

  1. 01年間のユーザー利用状況の振り返り作成
  2. 02個別のフィットネスや読書動画の生成
  3. 03営業や学生の進捗まとめの作成
  4. 04データ行から共有用動画をレンダリング

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/iart-ai/explainer-video-skills --skill wrapped-video

インストール後、Claude Code で/wrapped-videoを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

README.md
references/
batch-pipeline.md
remotion-recipes.md
scene-grammar.md

詳細

Wrapped Video

「Spotify Wrapped」風の振り返り動画を作成します。1人(またはアカウント、チーム、年)のデータ行を、目を引いて共有しやすい縦型動画に仕上げます。基本となるのは、1つのテンプレート × データテーブル → 多数のパーソナライズ動画です。テンプレートを一度作成すれば、各行に合わせた独自の動画をレンダリングできます。

使用する場面

  • ユーザーごとの統計情報がある製品の、年次振り返りや「あなたの2026年 Wrapped」。
  • パーソナライズされたデータ動画:フィットネスの1年、読書の1年、支出の振り返り、ゲームの統計、営業担当者の振り返り、学生の進捗。
  • 「同じ動画を、各人の数値に合わせて作る」という成果物が求められる場合。1本でも10万本でも対応できます。

これはデータ → 共有したくなるコンテンツのパターンであり、手作業で編集する動画ではありません。データテーブル(またはレコードごとのデータ)がない場合は、別のスキルを使用してください。

絶対に譲れない2つの条件

  1. **すべてをデータに基づかせる。**見出し、数値、名前、色はすべて、ハードコードではなく props から取得します。データ行の値を埋め込めないシーンは、Wrapped に含めません。
  2. **スクリーンショットに適した設計にする。**各シーンは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> + zod schema + 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='{...}'。

完了前に確認すること:

  1. npx remotion still で、代表的な行のフレーム0、中央、最終フレームがエラーやアセット / フォントの欠落なくレンダリングされること。
  2. 大きな数値が、表示が落ち着くフレームで正確に props の値になること。上位Xの順位 / ラベル / 値がその行と一致すること。
  3. 9:16:重要な数値 / 文字を中央80%内に配置し、上部12% / 下部18%を避けること。各フレームが静止した状態でも読み取れることを確認します。
  4. フレーム駆動のみとし、Date.now() / Math.random() / タイマーを使わないこと。バッチ処理の前に、すべての行をスキーマで検証します。
  5. 代表的な MP4 をエンコードして再生できることを確認してから、すべての行をバッチ処理します(レコードごとにファイルを作成)。必要に応じて README 用の GIF も作成します。

参考ファイル

  • references/scene-grammar.md — 全7シーンのストーリーボード。フレームのタイミング、ビルド / クレッシェンドのロジック、最上級表現とパーセンタイルのコピーライティングパターン、9:16のセーフエリアマップを掲載しています。
  • references/remotion-recipes.md — 実行可能なシーンコンポーネント:段階的に表示する上位Xリスト、パーセンタイルバー、<Series> によるシーンの連続表示、トランジション、フォント、ちらつきを防ぐアニメーションのルールを掲載しています。
  • references/batch-pipeline.md — CSV / JSON から複数の MP4 を生成する方法:スキーマ検証、レンダリングループ、並行処理、Lambda によるファンアウト、ファイル命名を掲載しています。

コメント