mcpField
P
presentation-video

presentation-video

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

presentation-video について

presentation-video は、スライドや構成案をナレーション付き動画にするスキルです。自動再生のプレゼン動画を作るチームや制作者向けに、Remotion でスライドを再構成し、音声に合わせて表示時間や要素の出現を設定します。

主な機能

  1. 01スライドをモーショングラフィックスとして再構成
  2. 02ナレーションの長さに基づくスライド時間設定
  3. 03音声に合わせた要素の順次表示
  4. 04トランジションとセクション区切りの追加
  5. 05MP4 のレンダリングと確認

活用シーン

  1. 01ピッチ資料を自動再生動画にする
  2. 02講座の構成案をナレーション付きスライドにする
  3. 03スライドアニメーションを音声に同期させる

インストール

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

$npx skills add https://github.com/iart-ai/data-animation-skills --skill presentation-video

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

同梱ファイル

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

README.md
references/
sequencing.md
slide-components.md

詳細

プレゼンテーション動画

デッキやスライドの概要を、ナレーション付きで自動再生するアニメーション動画に変換します。各スライドはモーショングラフィックスとして再構築されます。要素を段階的に表示し、本格的なトランジションを加え、ナレーションに合わせてタイミングを調整します。PowerPointをクリックして進める様子を画面録画するのではありません。

こんなときに使います

  • 自動再生動画にしたいピッチデッキ、ウェビナー、コースのモジュール。
  • ナレーション付きのアニメーションスライドにしたい箇条書きの概要。
  • スライドの表示時間をナレーションに正確に合わせたいデッキ(自動再生、クリック不要)。

録画せず、再構築する

画面録画では、デッキの静的な見た目やマウスのぶれ、唐突なスライド切り替えまで引き継がれます。コード(Remotion)で再構築すれば、フレームごとに正確な表示、自然なイージングを伴うトランジション、どの解像度でも鮮明なテキスト、音声の長さに合わせたスライド表示時間を実現できます。デッキは台本となり、動画は新たな成果物になります。

ストーリーの流れ:デッキは N 枚のスライドではなく、ひとつの主張です

タイミングや動きを決める前に、ストーリーを整えましょう。自動で進むプレゼンテーション動画には、話の筋を見失った視聴者をフォローする発表者がいません。構成だけで論理が伝わる必要があります。まず骨子を作り、スライドはその上の要点として組み立てます。

  • ひとつの一貫した主張(骨子)。 デッキ全体を通じて伝えることを、ひとつの文で示します。すべてのスライドはその文を前に進める必要があります。そうでないスライドは削除するか、付録に移しましょう。視聴者が一度見ただけで骨子を言い直せるのが理想です。
  • スライドごとにメッセージはひとつ。 各スライドで伝えるポイントはひとつだけです。タイトルは単なるラベル(「解約率」)ではなく、主張としてそのポイントを表します(「成長を最も損なっているのは解約率です」)。アイデアがふたつあるなら、スライドもふたつにします。
  • トランジションは動きだけでなく、論理を伝えます。 スライド間の切り替えは、「それで、次は?」への答えになるべきです。つまり、しかし、だからこそ、これが証拠です。論理の展開に合わせて動きを選びます。「さらに」の場合は控えめなフェード、「新たな章」の場合は大きくセクションを押し出します。論理の段階を示さないトランジションは、単なる飾りです。
  • 冒頭は引きつけ、最後は CTA で締めます。 最初のスライドでは、鋭い問い、意外な統計、重要な課題などで約 5 秒以内に関心をつかみます。アジェンダやロゴを表示し続けるのは避けましょう。最後のスライドでは、求める行動をひとつだけ示します(「パイロットを開始する」「200 万ドルの投資で 15%の株式を取得する」)。締めくくりのフレームとして、動かさずに表示します。
緊張 → 解決(ピッチの骨子)

ピッチは機能の一覧ではなく、ドラマです。緊張を高めてから解決するように構成し、それぞれの幕で次の幕への布石を打ちます。

幕要点役割
課題「何がうまくいっていないのか/何が懸かっているのか」視聴者が感じる緊張感を生み出す
洞察「誰もが見落としていること」意外性のある転換を示し、解決策への期待を高める
解決策「これが私たちの答えです」課題によって生まれた緊張を解消する
実証「これが機能する理由です」実績、デモ、数値を示す
依頼「私たちが求めるもの」具体的な CTA をひとつ提示する

課題が伝わる前に解決策を明かしてはいけません。十分な流れを経ていない解決策は、救いではなく単なる機能に感じられます。各スライドをこれらの幕に当てはめましょう。ひとつでも欠けていれば、論理に穴があります。

ペース配分:展開をナレーションに同期させます

一貫した主張は時間の中で展開します。そのため、画面上の表示とナレーション(VO)は一緒に進める必要があります。時間はナレーションが決めます(次のセクションを参照)。要素の表示はナレーションに合わせて設定し、視聴者が先読みすることなく、言葉が話されるのと同時に読めるようにします。スライドごとのメッセージをひとつにすれば、これは自然に実現できます。スライドは、ナレーションがそのポイントを説明する時間だけ表示され、その後のトランジションで論理の次の段階へ進みます。ひとつのポイントについてナレーションが長くなる場合は、表示のペースを速めるのではなく、スライドを分割します。

唯一のルール:時間はナレーションが決めます

各スライドの長さは、ナレーション音声の長さに少し余裕を加えた時間と正確に一致させます。 ナレーションのクリップをすべて実際に計測してフレーム数に換算し、その数をスライドの長さに設定します。「このスライドは約 5 秒だろう」と目測してはいけません。必ずずれが生じ、音声が途中で切れるか、スライドの表示中に音声が終わってしまいます。

import { getAudioDurationInSeconds } from "@remotion/media-utils";
// In a Remotion calculateMetadata() / build step, per slide:
const sec = await getAudioDurationInSeconds(slide.audioUrl);
const HANDLE = 0.4; // breathing room after the voice ends
slide.durationInFrames = Math.ceil((sec + HANDLE) * fps);

その後、スライド内の要素の表示をこの時間に合わせて設定します。箇条書きは固定間隔ではなく、ナレーションの言葉に合わせて表示します。

ビルドのリビール:装飾ではなく、見せるためにアニメーションさせる

ビルドの目的は、テンポを作ることです。ナレーションで触れたタイミングで各要素を表示し、視聴者が読み上げられている行を読むようにします。3行先まで読ませてはいけません。要素は順番に表示し、タイトル、ロゴ、背景の図形は静止させておきます。

選択肢推奨理由
表示時の動きフェード+12~24pxの上昇洗練されていて落ち着いた印象で、「現れる」ように見えます
要素ごとの表示時間0.25~0.5s流れを保てるほど速く、認識できるほどゆっくりです
箇条書き間の時間差0.15~0.4sテキストの壁ではなく、順番に表示されます
避ける動き飛び込み、回転、バウンス、タイプライター装飾的に見え、音声の邪魔になります

useCurrentFrame() を使って表示タイミングを制御し、レンダリングされた音声と常に同期させます(コンポーネント全体については references/slide-components.md を参照してください)。

import { useCurrentFrame, interpolate, spring, useVideoConfig } from "remotion";

const Bullet: React.FC<{ index: number; children: React.ReactNode }> = ({ index, children }) => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();
  const STAGGER = 8;                         // frames between bullets (~0.27s @ 30fps)
  const enter = frame - index * STAGGER;     // each bullet starts later
  const p = spring({ frame: enter, fps, config: { damping: 200 }, durationInFrames: 12 });
  return (
    <li style={{ opacity: p, transform: `translateY(${interpolate(p, [0, 1], [18, 0])}px)` }}>
      {children}
    </li>
  );
};

スライド間のトランジション

@remotion/transitions の <TransitionSeries> を使ってスライドを連ねます。各スライドはナレーションから算出した durationInFrames を持つ Sequence で、その間に Transition を配置します。トランジションは控えめにします(短いフェードや穏やかなスライドなど)。主役はコンテンツです。切り替わるたびに派手なワイプを使うと、すぐに見飽きてしまいます。

import { TransitionSeries, linearTiming } from "@remotion/transitions";
import { fade } from "@remotion/transitions/fade";

<TransitionSeries>
  {slides.map((s, i) => (
    <React.Fragment key={s.id}>
      <TransitionSeries.Sequence durationInFrames={s.durationInFrames}>
        <Slide {...s} />
      </TransitionSeries.Sequence>
      {i < slides.length - 1 && (
        <TransitionSeries.Transition
          timing={linearTiming({ durationInFrames: 15 })}
          presentation={fade()}
        />
      )}
    </React.Fragment>
  ))}
</TransitionSeries>

大きなトランジション(スライドやプッシュ)はセクション区切りのみに使います。「新しいトピック」を示し、追加の動きを加える価値があります。トランジションは両隣のスライドからフレームを借りるため、スライドのナレーションを最後まで再生する必要がある場合は、その長さをスライドの尺に加えてください。references/sequencing.md を参照してください。

タイトルとセクション区切り

タイトルカードとセクション区切りは、動画にリズムを与え、情報量の多いスライドの合間に視聴者がひと息つけるようにします。区切りは、それ自体が短い「スライド」であるかのように扱います。大きなセクション番号または単語、セクションタイトル、約1.5~2sの表示時間、そして両側に少し強めのトランジションを配置します。区切りのビジュアル表現(色、書体)は一貫させ、別の動画ではなく句読点として認識されるようにします。

テキストを読みやすく、ブランドに沿ったものにする

スライドはテキストが多いため、読みやすさが最も重要です。

  • デッキのサイズではなく、文字サイズを基準にします。 ノートパソコンで見るデッキと、フィードで見る動画は異なります。1080pでは本文を28~32px以上に、タイトルを56~80pxにします。16pxの脚注を入れないと収まらないスライドは、2枚に分ける必要があります。
  • 1枚につき1つのアイデアにします。 スライドのナレーションが長くなる場合は分割します。文字を小さくして収めてはいけません。
  • コントラストとセーフマージンを確保します。 コントラスト比は4.5:1以上にし、プラットフォームのトリミングに耐えられるよう、すべてのコンテンツを中央90%以内に収めます。
  • テーマオブジェクトでブランドを統一します。 色、フォント、ロゴ、間隔を1つの theme にまとめ、すべてのスライドで一貫させます。そうすれば、ブランドの変更も1行で済みます。
export const theme = {
  bg: "#0B0B12", fg: "#F5F5F7", accent: "#6C5CE7",
  font: "Inter, system-ui, sans-serif",
  titlePx: 72, bodyPx: 30, maxWidthPct: 90,
} as const;

ワークフロー

  1. アウトライン → スクリプト。 1枚のスライドにつき1つのアイデアと、正確なナレーションの文をまとめます。これを唯一の基準とします。
  2. スライドごとにナレーションを生成または録音します。 スライド1枚につき音声ファイルを1つ用意します(TTSまたは録音)。
  3. 音声を計測 → スライドの尺を設定します。 getAudioDurationInSeconds でフレーム数に変換します(上記のルールを参照)。
  4. useCurrentFrame() に連動した段階的なリビールを使って、スライドコンポーネントを作成します。
  5. <TransitionSeries> で連ね、タイトルとセクション区切りを追加します。
  6. 各スライドの音声を、その Sequence 内に配置します。これにより、音声と映像がずれることはありません。
  7. 読みやすさとブランドを確認してから、MP4 にレンダリングします。

出力チェックリスト

  • 各スライドの durationInFrames は推測ではなく、ナレーション音声から算出されています。
  • 各スライドの音声は、それぞれの Sequence 内に配置されています(映像と音声が同期しています)。
  • 箇条書きは useCurrentFrame() で順番に表示され、タイトルとロゴは静止しています。
  • リビールはフェード+上昇で、0.25~0.5sです。装飾的な効果は使いません。
  • スライド間のトランジションは控えめにし、大きなトランジションはセクション区切りにのみ使います。
  • 本文の文字サイズは1080pで28px以上、コントラスト比は4.5:1以上で、コンテンツは中央90%以内に収まっています。
  • 1枚につき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 を参照してください。

Remotion はフレーム単位で決定的です。箇条書きの各ビルド、トランジション、スライドの位置はすべて useCurrentFrame() のみから決まるため、シーク用の仕組みがなくても、任意の正確なフレームをヘッドレスでレンダリングできます。納品物はナレーション付き MP4 です。静止画を確認すれば、エンコードに時間をかける前に、タイトルの見切れ、ブランドに合わない色、領域からはみ出す箇条書きを見つけられます。

出力要件:

  • デッキが登録された Remotion プロジェクト(<Composition> + zod schema + defaultProps)を用意し、すべての動きをフレーム駆動にします(タイマー/Date.now()/Math.random() は使用しません)。各スライドの音声は、それぞれの Sequence 内にマウントします。
  • 納品物 = レンダリング済みの out/*.mp4(およびプロジェクト。ユーザーが新しいスライド/ナレーションで再レンダリングできるようにします)。
  • スライドの長さはナレーションから算出します。calculateMetadata で各音声クリップを計測し、合計とスライドごとの durationInFrames を計算します。手作業で設定しないでください。

検証ループ — 静止画をレンダリング → 確認 → エンコード。 まず個別のフレームをレンダリングします(低コストで、動画エンコードは不要です)。各スライドの仕上がりと読みやすさを確認してから、エンコードを一度だけ行います。

# 1. Frame-exact stills — with the SHIPPED props. Land one inside each slide's settled state:
npx remotion still Deck out/f-title.png --frame=20  --props='{"slides":[...]}'   # title card settled
npx remotion still Deck out/f-mid.png   --frame=240 --props='{"slides":[...]}'   # a content slide, bullets built
npx remotion still Deck out/f-end.png   --frame=899 --props='{"slides":[...]}'   # last frame = durationInFrames - 1

# 2. Inspect each PNG — FIDELITY (slide text exact, numbers/labels correct, brand colors/fonts applied)
#    AND artifacts (text overflow, body type too small, content outside center 90% safe-crop, missing
#    font, low contrast, a bullet caught mid-build where the slide should be settled).

# 3. Only after the stills check out, encode:
npx remotion render Deck out/deck.mp4 --props='{"slides":[...]}'
  • npx remotion compositions でデッキの durationInFrames/fps を確認し、最後のフレームを選びます。仕上がりを判断するには、トランジション中ではなく各スライドの表示時間内の静止画をサンプルしてください。
  • データ駆動/一括処理:すべてのバリエーションをレンダリングする前に、静止画を使って代表的なデッキを1つ(実際のスライド+ナレーション)検証します。デッキごとに確認するのではなく、文字サイズやはみ出しの問題を一度で見つけます。
  • README のデモ GIF を手軽に作成:npx remotion render Deck out/demo.gif --codec=gif。

完了前に確認すること:

  1. npx remotion still でタイトル、コンテンツスライド、最後のフレームをエラーなくレンダリングできること。フォントが欠落していないこと。
  2. スライドのテキストが正確であること。本文の文字サイズが 1080p で 28px 以上、コントラスト比が 4.5:1 以上であり、各フレームの中央 90% 以内にすべてのコンテンツが収まっていること。
  3. フレーム駆動のみであること。Date.now()/Math.random()/ライブラリのタイマーを使用しないこと(CI でも決定性を保ちます)。
  4. 出荷する props で正しくレンダリングされること(defaultProps だけで確認しないこと)。適切なスライドが使われ、ナレーションがマウントされ、音声に基づく長さが設定されていること。
  5. MP4 全体がエンコードされ、再生できること(音声と映像が同期していること)。README 用の GIF のレンダリングは任意です。

参考ファイル

  • references/slide-components.md — 共通テーマを使った、実行可能な Remotion の Slide、段階表示する BulletList、アニメーション付きの TitleCard、SectionDivider コンポーネント一式。各スライドのナレーションは <Audio> でマウントされます。
  • references/sequencing.md — デッキ全体のシーケンサー。すべてのナレーションクリップを計測し、合計とスライドごとの長さを設定する calculateMetadata、<TransitionSeries> による組み立て、トランジションのフレーム予算の計算、JSON デッキスキーマから動画までのデータフローを説明しています。

コメント