Felo API PlatformFelo API Platform

コンテンツ

HTML と Opus 5.5 でモーショングラフィックス動画を作る

ビートシートを書き、Opus 5.5 に Felo API 経由で決定論的な HTML シーンを書かせ、Playwright でフレームを取得して ffmpeg で MP4 にエンコードします。WorkBuddy、コード実行付き ChatGPT、Codex、Pi Agent、Claude Code で実行できます。

所要時間45 分レベル中級更新日2026-10-30
対象クリエイターモーションデザイナー個人開発者

プロンプトをコピー

角括弧の項目を置き換えて、Claude Code、Codex、ターミナルにアクセスできる AI に貼り付けてください。Felo スキルをインストールし、クレジットを使う前に確認し、ワークフローを実行します。

[アイデア] について [duration] 秒の [縦 / 横] 動画を作成してください。
スタイル: [スタイル]。読者: [読者]。音声: [なし / ./audio.mp3 のライセンス済みトラック / TTS]。

あなたは [WorkBuddy / コード実行付き ChatGPT / Codex / Pi Agent / Claude Code] のターミナルがあるフォルダで実行しています。Node とヘッドレスブラウザを実行できない場合は、レンダリングしたふりをせず、停止して私に伝えてください。

次の順序で進めてください。
1. 正確な開始秒と終了秒を持つ3〜6ビートのストーリーボードを書く。
2. HTML ステップでは、Felo API 経由で Claude Opus 5.5 を呼び出す。モデル ID claude-opus-5-5 と私の FELO_API_KEY を使う。Claude のサブスクリプションや別の Anthropic キーを私に求めない。
3. Opus に、単一の自己完結型 HTML ファイルを依頼する。window.DURATION を設定し、async window.seek(t) を公開すること。外部アセット、CDN、Date.now()、requestAnimationFrame()、CSS トランジション、タイマーは禁止。すべての乱数値をシードし、同じ t が常に同じフレームを描くようにする。
4. t = 0、25%、50%、75%、最終フレームでプレビューする。すべてをレンダリングする前に、はみ出し、クリッピング、フォント、セーフエリアを確認する。
5. Playwright で全フレームをレンダリングし、ffmpeg で H.264 にエンコードし、音声トラックがあれば mux する。
6. 最初と最後のフレーム、最終ファイルパスを見せる。公開はしない。

ルール:
- クレジットを消費する前に、Felo API 呼び出しと推定トークン量を示す。
- 私が渡したコピーだけを使うか、捏造したプレースホルダーコピーを明確に示す。
- 著作権のある映像、音楽、ロゴ、フォントを使わない。システムフォントと生成図形のみ。
- 私が長尺を求めない限り、最終動画は30秒未満にする。
- すべてを ./video/ に書き、実行したコマンドを要約する。

コピー内容にはインストール手順と安全ルールも含まれます。

完成するもの

実行時の出力例です。リアルタイム生成ではありません。

レンダーレポートの例(抜粋)

レンダーレポート

シーン: video/scene.html (18 KB) 長さ: 12秒 · 1080x1920 · 30fps · 360フレーム チェック: window.DURATION = 12 · seek(t) あり · Date.now / rAF / タイマーなし

プレビュー: preview-0ms.png · preview-3000ms.png · preview-6000ms.png · preview-9000ms.png エンコード: video/video.mp4 (H.264, 360フレーム) 音声: video/final.mp4 (AAC, 12.0秒)

実際の呼び出し

開発者向け:同じワークフローを実行順に並べたコピー可能なコード。

実際の API 呼び出しを表示
  1. 先にビートシートを書く

    長さと3〜6個のビートを先に固定し、各ビートの開始・終了時刻、画面コピー、主な視覚変化を書きます。

  2. Opus 5.5 で HTML シーンを生成する

    Anthropic SDK の base URL を Felo に向け、モデル claude-opus-5-5 を使い、HTML のみを返すよう要求します。window.DURATION と async seek(t) を含めます。

    generate-scene.ts
    import { mkdir, writeFile } from "node:fs/promises";
    import Anthropic from "@anthropic-ai/sdk";
    
    const SYSTEM = `You write a single, self-contained HTML video scene.
    Requirements:
    - 1080x1920 (or 1920x1080), no external assets, system fonts only.
    - set window.DURATION = <seconds>.
    - expose async window.seek(t) that draws the frame for time t.
    - no Date.now(), no requestAnimationFrame(), no CSS transitions, no timers.
    - seed every random value; the same t must always produce the same frame.
    - return only the HTML file, no markdown fences.`;
    
    const brief = `12-second vertical motion graphic about [idea].
    Beats: [0-3s hook], [3-7s point], [7-10s proof], [10-12s CTA].
    Palette: [colors]. Type: system sans. Keep text inside a 64px safe area.`;
    
    const anthropic = new Anthropic({
      apiKey: process.env.FELO_API_KEY,
      baseURL: "https://openapi.felo.ai/api",
    });
    
    const message = await anthropic.messages.create({
      model: "claude-opus-5-5",
      max_tokens: 32000,
      system: SYSTEM,
      messages: [{ role: "user", content: brief }],
    });
    
    const raw = message.content
      .filter((block) => block.type === "text")
      .map((block) => block.text)
      .join("");
    
    const html = raw.replace(/^```(?:html)?\s*/i, "").replace(/```\s*$/, "").trim();
    await mkdir("video", { recursive: true });
    await writeFile("video/scene.html", html);
    console.log(`scene.html: ${html.length} bytes`);

    出力例

    scene.html: 18432 bytes
  3. 決定論を確認しキーフレームをプレビューする

    t=0、25%、50%、75%、最終フレームでスクリーンショットを取り、文字のはみ出し、フォント、セーフエリアを確認します。

    preview.ts
    import { chromium } from "playwright";
    import { pathToFileURL } from "node:url";
    
    const browser = await chromium.launch();
    const page = await browser.newPage({
      viewport: { width: 1080, height: 1920 },
      deviceScaleFactor: 1,
    });
    
    await page.goto(pathToFileURL("video/scene.html").href);
    await page.waitForFunction(() => typeof (window as any).seek === "function");
    
    const duration = await page.evaluate(() => (window as any).DURATION as number);
    for (const t of [0, duration * 0.25, duration * 0.5, duration * 0.75]) {
      await page.evaluate(async (time) => {
        await (window as any).seek(time);
      }, t);
      await page.screenshot({ path: `video/preview-${Math.round(t * 1000)}ms.png` });
    }
    
    await browser.close();
    console.log(`duration ${duration}s`);

    出力例

    duration 12s
    video/preview-0ms.png
    video/preview-3000ms.png
    video/preview-6000ms.png
    video/preview-9000ms.png
  4. 全フレームをレンダリングしてエンコードする

    t = frame / fps で seek(t) を呼び、スクリーンショットを撮り、ffmpeg で H.264 にエンコードします。

    render.ts
    import { spawn } from "node:child_process";
    import { mkdir, rm } from "node:fs/promises";
    import { pathToFileURL } from "node:url";
    import { chromium } from "playwright";
    
    const FPS = 30;
    
    const browser = await chromium.launch();
    const page = await browser.newPage({
      viewport: { width: 1080, height: 1920 },
      deviceScaleFactor: 1,
    });
    await page.goto(pathToFileURL("video/scene.html").href);
    await page.waitForFunction(() => typeof (window as any).seek === "function");
    
    const duration = await page.evaluate(() => (window as any).DURATION as number);
    const frames = Math.ceil(duration * FPS);
    
    await rm("video/frames", { recursive: true, force: true });
    await mkdir("video/frames", { recursive: true });
    
    for (let frame = 0; frame < frames; frame++) {
      await page.evaluate(async (time) => {
        await (window as any).seek(time);
      }, frame / FPS);
      await page.screenshot({
        path: `video/frames/${String(frame).padStart(5, "0")}.png`,
      });
    }
    await browser.close();
    
    const ffmpeg = spawn("ffmpeg", [
      "-y",
      "-framerate", String(FPS),
      "-i", "video/frames/%05d.png",
      "-c:v", "libx264",
      "-pix_fmt", "yuv420p",
      "-movflags", "+faststart",
      "video/video.mp4",
    ], { stdio: "inherit" });
    
    await new Promise<void>((resolve, reject) => {
      ffmpeg.on("exit", (code) => {
        if (code === 0) resolve();
        else reject(new Error(`ffmpeg exited with ${code}`));
      });
    });
    
    console.log(`${frames} frames -> video/video.mp4`);

    出力例

    360 frames -> video/video.mp4
  5. 音声を mux して最後まで見る

    -c:v copy でライセンス済み音声を加え、長さと音量を確認し、最後まで通して見ます。

    mux.sh
    ffmpeg -i video/video.mp4 -i video/audio.wav \
      -c:v copy -c:a aac -b:a 192k -shortest \
      video/final.mp4
    
    ffprobe -v error -show_entries format=duration \
      -of default=nw=1:nk=1 video/final.mp4

    出力例

    12.000000
  6. 同じモデルで反復する

    失敗したフレームと希望する変更だけを伝え、現在の HTML と一緒に Opus に渡します。プレビューを再実行してから本番レンダリングします。

含まれるもの

  • 先に作る分鏡とタイムライン
  • Opus 5.5 が生成する決定論的 HTML シーン
  • Playwright のフレームレンダリングスクリプトと ffmpeg エンコード
  • 任意の音声トラック付き最終 MP4
  • フレーム単位のレビューと修正ループ

やらないこと

  • Opus 5.5 が出すのはコードであり、動画フレームではない。実際にフレームを取得するのはブラウザレンダラー
  • Claude サブスクリプションは不要だが、Felo のモデル利用は従量制
  • 実行環境には Node、Playwright Chromium、ffmpeg が必要
  • 外部素材と CDN は不可。視覚は HTML/CSS/SVG/Canvas またはローカルファイルのみ
  • 長い動画はシーン分割が必要。リアルタイム画面録画ではない
  • ナレーションと音楽は別ステップ。ワークフローは提供された音声を mux するだけ

よくある質問

Opus 5.5 は直接動画を生成しますか?

いいえ。テキストとコードを出力し、HTML シーンを書きます。ブラウザと ffmpeg がシーンをフレームと MP4 に変えます。

Claude サブスクリプションは必要ですか?

不要です。Felo キーで Felo API 経由で `claude-opus-5-5` を呼び、従量制で利用します。別途 Claude サブスクリプションは要りません。

どの agent で実行できますか?

WorkBuddy、コード実行付き ChatGPT、Codex、Pi Agent、Claude Code、または Node と headless ブラウザを実行できる任意の agent です。

なぜシーンに seek(t) が必要ですか?

フレーム取得はリアルタイム再生ではありません。399 フレーム目を撮ってから 400 フレーム目を撮るまで時間が空くことがあるため、各フレームは t の純粋関数でなければなりません。

HyperFrames や Remotion を使えますか?

はい。長い合成や構造化されたプロジェクトに向いています。このレシピは単一 HTML ファイルに保ち、モデル呼び出しとレンダリングループを確認しやすくします。

12秒の動画の費用は?

プロンプト、出力トークン、反復回数によります。Felo モデルはトークン課金です。長いレンダリングの前に最新の料金を確認してください。

自分の音楽やナレーションを使えますか?

権利がある場合のみ使えます。先に動画をレンダリングし、ffmpeg でライセンス済み音声か自分の TTS を mux してください。ワークフローはナレーションを生成しません。

参考資料

このワークフローの参考にしたチュートリアルとプロジェクト。

関連ワークフロー

自分の形に変える

同じレシピを、実際の進め方に合わせて調整します。

HyperFrames や Remotion プロジェクトに変える

長い合成や構造化された作品にはこれらのフレームワークを使います。モデル呼び出しは同じで、レンダリングコマンドだけ変わります。

正方形のSNS版を作る

ビートシートを保ち、1080x1080 にして文字サイズとセーフエリアを調整します。

1つのブリーフから3案を作る

同じブリーフで色やリズムの異なる3シーンを生成し、プレビュー後に最良の1つだけをレンダリングします。

このレシピの背景にあるリファレンスと、次に見るべきページ。