Home  /  Blog  /  Videos with agents
TUTORIAL / AGENTS + VIDEO

From chat
to video.

A week of group conversation, 1,300 messages, turned into 74 seconds of motion graphics with a voiceover, real screenshots and animated diagrams. No video editor: one agent running Claude Opus, orchestrating six tools. Here is how we did it, step by step.

BY SKOA~12 MIN READ
Six-step pipeline: chat export, script, voice, timestamps, screenshots and render with verification.
01 / ONE AGENT, SIX TOOLS, ONE VIDEO.
74 s1080p · 16:9
8 scenesone story per scene
6 screenshotsreal sources
−14 LUFSsync verified
01 /

The result: one week in 74 seconds.

The request was a single sentence: “summarise last week as a script, then make a motion-graphics animation with screenshots of the sources and Aaron’s voice from ElevenLabs”. The material: the WhatsApp export of a developer group, 5 MB of text. The agent returned two things: a Markdown script and this video (in Spanish).

02 / “LA SEMANA EN EL GRUPO”, 12–19 OCTOBER.

Every scene has a diagram that draws itself to the rhythm of the voice, a number that appears exactly when it is spoken, and a real screenshot of the source: the GitHub repository, the model ranking, the migrated website. No stock footage, no templates.

02 /

The architecture: one orchestrator, six tools.

The model does not “generate a video”. Opus acts as the director: it reads, decides, writes code and calls tools. Each tool does one thing well:

  1. Reading the chat: a shell in a sandbox (grep, sed) to cut just the last week out of a 55,000-line file.
  2. Script: the model itself picks 8 stories with concrete numbers and writes the voiceover.
  3. Voice: ElevenLabs generates four takes with the “Aaron Abad – Storyteller en Castellano” voice.
  4. Timestamps: Scribe, the ElevenLabs transcriber, returns the start and end of every word.
  5. Screenshots: the built-in browser of the Claude app opens each source on your computer.
  6. Render: a canvas animation engine, headless Chromium and ffmpeg, with automatic loudness and sync checks.
The key idea

Everything hangs off one file, score.js, which holds the time of every cut, every number and every caption. Sound and picture both read it, so they cannot drift apart.

03 /

From chat to script.

A 5 MB export does not fit in one read. The agent first counts messages per day to find the window, then extracts only those lines:

Cut the last week
for d in 12 13 14 15 16 17 18 19; do
  echo "$d/10: $(grep -c "^\[$d/10/2026" chat.txt)"
done
grep -n "^\[12/10/2026" chat.txt | head -1   # → line 54021
sed -n '54021,$p' chat.txt > week.txt          # 1,304 lines

With the week in context, editorial judgement makes the difference: stories with a number you can animate (24 hours, 80 %, 150 customers), credited by name, and nothing political or personal. The script is a table with four columns: scene, visual, voiceover and note. It is delivered before spending voice credits, so the direction can still change.

Check before you narrate

Someone in the chat said Meta had “the second-best model”. The agent opened the source and the page said #4 of 696. The video says what the page says.

04 /

The voice: four takes, pick the cleanest.

The agent finds the voice by name with creative_list_voices (never an id from memory) and generates the full text with eleven_v4. ElevenLabs returns four variations: 71, 76, 76 and 78 seconds. We picked the shortest, because in a recap pace matters more than dramatic pauses.

A practical detail: separating scenes with a paragraph break in the text gives natural pauses of half a second or more. Those pauses are exactly where the scene cuts will land.

05 /

Word timestamps: the voice is in charge.

Instead of guessing when each thing is said, the agent transcribes the chosen take with Scribe and gets a list like this:

Scribe response (excerpt)
{ "text": "Meta",   "start": 18.80, "end": 18.98 },
{ "text": "arriba", "start": 21.70, "end": 22.00 },
{ "text": "80",     "start": 39.42, "end": 39.60 },
{ "text": "%",      "start": 39.92, "end": 39.96 }

With that, the “#4” stamp lands at 21.70 s (when the voice says “arriba”, “on top”) and the 80 % ring starts filling at 39.42 s. Each scene cut is placed in the silence just before the first word of the next topic.

06 /

Real screenshots, routed through your computer.

The sandbox the agent works in has no open internet access: neither to news sites nor to the storage where ElevenLabs leaves the MP3. The fix was to use the user’s computer as a bridge, through the built-in browser of the Claude app.

The sandbox cannot reach the internet; the browser on the Mac opens pages and audio and hands them back to the sandbox.
03 / THE MAC BRIDGES THE INTERNET AND THE SANDBOX.

Screenshots: the agent opens each source, sets the viewport to 1440×900, dismisses modal notices and takes a screenshot. Each screenshot lands in the sandbox as a JPG.

Screenshot of the Muse Wonder page on Artificial Analysis: Intelligence #4 of 696.
04 / A SCREENSHOT EXACTLY AS IT REACHES THE SANDBOX.

Audio: for the 1.1 MB MP3, the agent downloads it inside the page, base64-encodes it and reads it back in chunks of 200,000 characters, eight calls in parallel. Then it rebuilds the file in the sandbox:

In the page (javascript_tool)
const buf = new Uint8Array(await (await fetch(location.href)).arrayBuffer());
let s = ''; for (let i = 0; i < buf.length; i += 32768)
  s += String.fromCharCode.apply(null, buf.subarray(i, i + 32768));
window.__b64 = btoa(s);           // 1,546,492 characters
// then, 8 times:  'C3<' + window.__b64.slice(600000, 800000) + '>'
07 /

One score for sound and picture.

The engine comes from the animation-studio skill: every frame is drawn in code on a canvas and the soundtrack is synthesized in JavaScript. Its golden rule is that every event lives in score.js:

score.js (excerpt)
const VOFF = 0.5;                         // the voice starts at 0.5 s
const S = {                               // cuts inside the voice's pauses
  intro: [0, 6.55], claudio: [6.55, 19.1], meta: [19.1, 24.85],
  rea: [24.85, 35.05], german: [35.05, 46.65], debate: [46.65, 56.45],
  homedock: [56.45, 64.95], outro: [64.95, 74.5],
};
const w = (x) => x + VOFF;                 // a word's time from Scribe
const ev = { rank: w(21.7), wallet80: w(39.42), rip: w(57.72) };

song.js reads those times to put a hit on every cut and a pop on every number; film.js reads them to bring every element in. Move a number in the score and the sound and picture move together.

08 /

Drawing scenes as functions of time.

Each scene is a pure function draw(ctx, t): given an instant, it draws exactly that frame. That way several Chromium processes can render in parallel and get identical images. Five reusable pieces build every scene:

  1. browser(): a browser window with the real URL and the screenshot inside, slowly zooming toward the part that matters.
  2. box() and arrow(): nodes and animated dashed arrows for the diagrams.
  3. Data.counter and UI.ring: counters that climb to the number and percentage rings.
  4. stampAt(): bouncing stamps for verdicts (“#4”, “RIP CLOUD”, “VALIDADOR NEGACIONISTA”).
  5. Subs.draw: phrase-level captions timed by Scribe.
09 /

Mix and measure, don’t listen.

The agent cannot hear, so it measures. Under the voice runs a synthesized lo-fi bed that ducks automatically whenever someone speaks. The first measurement showed the bass 4 dB above the voice; after adjusting gains the voice sat about 7 dB above everything else:

node engine/tools/levels.js (RMS dBFS per scene)
               intro   claudio   meta    rea
stem-voice     -23.6   -22.8    -22.0   -22.2
stem-bass      -31.6   -29.4    -28.9   -29.1
stem-keys      -30.0   -31.8    -32.0   -30.6
integrated loudness: -14.0 LUFS

−14 LUFS is the level YouTube and Spotify normalise to; the limiter keeps the true peak under −1.5 dBTP so AAC encoding does not clip it.

10 /

Render, review and verify.

Before the full render, the agent renders one frame per scene at half resolution, tiles them into a sheet and reviews it like a director: overlapping text, a character hidden behind a stamp, labels on top of the title. The first review found and fixed those overlaps.

Contact sheet with one frame every two seconds of the final video.
05 / ONE FRAME EVERY TWO SECONDS OF THE FINAL VIDEO.

Rendering 2,235 frames took about five minutes. Then verify measures in the final MP4 whether every cut lands on the right frame and whether its sound hit is within 20 ms. On four cuts, a syllable of the voice started 0.1 s from the hit and masked it; the tool flagged them as unclear. The honest fix was to mark those cuts as picture-only, not to move times by hand.

node engine/render.js verify
marker     score     sound            picture
claudio    6.550s    6.555s +5ms ✓    6.600s +1f ✓
meta       19.100s   19.105s +5ms ✓   19.133s +1f ✓
outro      64.950s   64.955s +5ms ✓   65.000s +1f ✓
✓ in sync
Two environment snags

Chromium running as root needs --no-sandbox: solved with a wrapper script in CHROME_PATH, without touching the engine. And the built-in browser does not save downloads to your folder, which is why the audio travels as base64.

11 /

Turn the process into a skill.

The most valuable output of the session is not the video but the procedure. We saved it as a skill, chat-recap-motion-video: a SKILL.md with the steps, the defaults (voice, length, style), the networking tricks and the checks. Any Claude instance on the account loads it when someone asks for “last week as a video”, and its last section asks for lessons to be added after every run.

  1. Input: a chat export or notes, and the period.
  2. Early delivery: the script, before any credits are spent.
  3. Final delivery: MP4, script, credits spent, sources captured and what was left out.

Rough cost of this piece: about 5,300 ElevenLabs credits for the four voice takes and about 390 for the transcription. Rendering is local and costs no credits.

12 /

The prompt to do it yourself.

With Claude Opus, the desktop app linked to your computer, ElevenLabs as a connector and the animation-studio skill installed:

Prompt
Attached is our group chat export. Summarise the last week as a script
(scene · visual · voiceover) and send it to me first.
Then make a ~75 s 16:9 motion-graphics video:
- voiceover with ElevenLabs (pick a voice that fits the language)
- word timestamps to sync cuts, numbers and captions
- one real screenshot of each story's source (use the browser)
- one animated diagram per scene, plus captions
Leave out politics and personal matters. Verify loudness (−14 LUFS)
and sync, and tell me how many credits you spent.

The natural next step is a vertical 9:16 cut for Reels and Shorts. Cropping won’t do it: every scene has to be recomposed for a phone screen. If you build it, tell us how it went.