Felo API PlatformFelo API Platform

Content

Maak een motion graphics-video met HTML en Opus 5.5

Schrijf een beat sheet, laat Opus 5.5 via de Felo API een deterministische HTML-scène schrijven, leg frames vast met Playwright en codeer een MP4 met ffmpeg. Draait in WorkBuddy, ChatGPT met code-uitvoering, Codex, Pi Agent of Claude Code.

Tijd45 minutenNiveauGevorderdBijgewerkt2026-10-30
Voor wieMakersMotion designersIndie-ontwikkelaars

Prompt kopiëren

Vervang de velden tussen blokhaken en plak in Claude Code, Codex of een AI met terminaltoegang. Die installeert de Felo-skill, vraagt voor credits en voert de workflow uit.

Maak een video van [duur] seconden, formaat [verticaal / horizontaal], over [idee].
Stijl: [stijl]. Publiek: [publiek]. Audio: [geen / een gelicenseerd nummer in ./audio.mp3 / TTS].

Je draait in een map met terminal en [WorkBuddy / ChatGPT met code-uitvoering / Codex / Pi Agent / Claude Code]. Als je Node en een headless browser niet kunt uitvoeren, stop en zeg het mij in plaats van te doen alsof je hebt gerenderd.

Volg deze volgorde:
1. Schrijf een storyboard van 3–6 beats met exacte begin- en eindseconden.
2. HTML-stap: roep Claude Opus 5.5 aan via de Felo API. Gebruik model-ID claude-opus-5-5 en mijn FELO_API_KEY. Vraag mij niet om een Claude-abonnement of een andere Anthropic-sleutel.
3. Vraag Opus om één zelfstandig HTML-bestand: stel window.DURATION in en exposeer async window.seek(t). Geen externe assets, CDN’s, Date.now(), requestAnimationFrame(), CSS-transities of timers. Seed alle willekeurige waarden zodat dezelfde t altijd hetzelfde frame tekent.
4. Bekijk een preview op t = 0, 25%, 50%, 75% en het laatste frame. Controleer overflow, afsnijding, lettertypen en safe areas voordat je alles rendert.
5. Render elk frame met Playwright, codeer naar H.264 met ffmpeg en mux de audiotrack als die er is.
6. Toon het eerste frame, het laatste en het uiteindelijke bestandspad. Publiceer niets.

Regels:
- Voordat er credits worden gebruikt, toon je de Felo API-aanroepen en de geschatte tokens.
- Gebruik alleen de tekst die ik geef, of markeer verzonnen plaatshoudertekst duidelijk.
- Gebruik geen auteursrechtelijk beschermd materiaal, muziek, logo’s of lettertypen. Alleen systeemlettertypen en gegenereerde vormen.
- Houd de uiteindelijke video onder 30 seconden, tenzij ik expliciet een lange versie vraag.
- Schrijf alles naar ./video/ en vat de uitgevoerde commando’s samen.

De kopie bevat ook de installatiestappen en veiligheidsregels.

Wat je krijgt

Voorbeelduitvoer van een run. Geen live generatie.

Voorbeeld-renderrapport (fragment)

Renderrapport

Scène: video/scene.html (18 KB) Duur: 12 s · 1080x1920 · 30 fps · 360 frames Checks: window.DURATION = 12 · seek(t) aanwezig · geen Date.now / rAF / timers

Preview: preview-0ms.png · preview-3000ms.png · preview-6000ms.png · preview-9000ms.png Encoding: video/video.mp4 (H.264, 360 frames) Audio: video/final.mp4 (AAC, 12,0 s)

De exacte aanroepen

Voor ontwikkelaars: dezelfde workflow als kopieerbare code in uitvoeringsvolgorde.

Exacte API-aanroepen tonen
  1. Schrijf eerst de beat sheet

    Leg duur en 3–6 beats vast, elk met start, einde, tekst op het scherm en belangrijkste visuele verandering.

  2. Genereer de HTML-scène met Opus 5.5

    Richt de Anthropic SDK op de Felo base-URL, gebruik claude-opus-5-5 en vraag alleen HTML met window.DURATION en 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`);

    Voorbeelduitvoer

    scene.html: 18432 bytes
  3. Controleer determinisme en preview

    Maak screenshots op t=0, 25%, 50%, 75% en het laatste frame en controleer tekstoverloop, lettertypen en veilige zones.

    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`);

    Voorbeelduitvoer

    duration 12s
    video/preview-0ms.png
    video/preview-3000ms.png
    video/preview-6000ms.png
    video/preview-9000ms.png
  4. Render alle frames en codeer

    Roep seek(t) aan met t = frame / fps, leg frames vast en codeer H.264 met ffmpeg.

    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`);

    Voorbeelduitvoer

    360 frames -> video/video.mp4
  5. Mux audio en bekijk volledig

    Voeg de gelicenseerde track toe met -c:v copy, controleer duur en volume en bekijk de video helemaal.

    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

    Voorbeelduitvoer

    12.000000
  6. Itereer met hetzelfde model

    Beschrijf alleen het foutieve frame en de gewenste wijziging, geef de huidige HTML aan Opus en start eerst de preview opnieuw.

Wat inbegrepen is

  • Eerst geschreven storyboard en tijdlijn
  • Deterministische HTML-scène van Opus 5.5
  • Playwright-frame-renderscript en ffmpeg-codering
  • Eind-MP4 met optionele audiotrack
  • Frame-voor-frame review en correctielus

Wat het niet doet

  • Opus 5.5 levert code, geen videoframes; de browser-renderer legt frames vast
  • Geen Claude-abonnement nodig, maar Felo-modelgebruik wordt op gebruik gefactureerd
  • Node, Playwright Chromium en ffmpeg blijven nodig
  • Geen externe assets of CDN’s; alleen HTML/CSS/SVG/Canvas of lokale bestanden
  • Lange video’s vragen scènedeling; dit is geen realtime schermopname
  • Narratie en muziek zijn aparte stappen; de workflow muxet alleen aangeleverde audio

Vragen

Genereert Opus 5.5 direct video?

Nee. Het levert tekst en code en schrijft de HTML-scène; browser en ffmpeg maken daar frames en een MP4 van.

Heb ik een Claude-abonnement nodig?

Nee. Gebruik de Felo-sleutel om `claude-opus-5-5` via de Felo API aan te roepen, op gebruik gefactureerd; een apart Claude-abonnement is niet nodig.

In welke agents draait dit?

WorkBuddy, ChatGPT met code-uitvoering, Codex, Pi Agent, Claude Code of elke agent die Node en een headless browser kan draaien.

Waarom seek(t) in de scène?

Frame-capture is niet realtime: frame 400 kan veel later worden vastgelegd dan 399, dus elk frame moet een pure functie van t zijn.

Kan ik HyperFrames of Remotion gebruiken?

Ja, voor lange composities en gestructureerde projecten. Dit recept houdt één HTML-bestand zodat modelaanroep en renderlus controleerbaar blijven.

Wat kost een video van 12 seconden?

Dat hangt af van prompt, uitvoertokens en iteraties. Het Felo-model factureert per token; controleer actuele prijzen voor een lange render.

Kan ik mijn eigen muziek en narratie gebruiken?

Ja, als je de rechten hebt. Render eerst de video en mux daarna de gelicenseerde track of je TTS met ffmpeg. De workflow genereert geen narratie.

Referenties

Tutorials en projecten waarop deze workflow is gebaseerd.

Gerelateerde workflows

Maak het van jou

Hetzelfde recept, aangepast aan hoe je echt oplevert.

HyperFrames- of Remotion-project

Voor lange composities deze frameworks gebruiken; de modelaanroep blijft gelijk en alleen het rendercommando verandert.

Vierkante socialversie

Behoud de beat sheet, ga naar 1080x1080 en pas typografie en veilige zones aan.

Drie varianten uit één brief

Genereer drie scènes met andere kleuren en ritmes, bekijk previews en render alleen de beste.

De referentiepagina’s achter dit recept en de volgende stappen.