Say hi

Video pipeline: promos drawn in code

A video pipeline I built with Claude Code that draws every frame of my promo and YouTube videos in Python and Blender, with an ElevenLabs voice, and renders a four-minute video in about a minute on my PC.

Status
Done · 2026
Built with
Python 3.12, skia-python, NumPy, OpenCV, ffmpeg, Blender 5.2, ElevenLabs, Claude Code
Time
Since November 2026
Topics
AI, Video
People
All projects
The PETRACH P drawn from lattice triangles in the middle of a render: the left part filled in ink, an orange edge of cells being drawn, the rest still outlined, above a progress bar reading frame 3,976 of 7,647

I make promo and explainer videos without a camera, an editing app or a motion designer. Every frame is drawn in code that Claude Code writes, and I direct it with notes in plain words. This is the engine behind that: a folder of Python scenes that draws my brand's lattice and P frame by frame, Blender for real 3D, ElevenLabs for the voice and the music, and ffmpeg to put it all together.

If you want to make videos this way yourself, start with the workflow in my AI toolbox: the steps and every prompt, built on HyperFrames, which anyone can install. This page is the custom part underneath, the renderer I built for my own brand, and what it took to make it fast.

From the 30-second promo: graded footage and shapes drawn in Python, cut on the beat.

Why I built it

My brand is geometry: a 60° triangle lattice and a P built from its cells. An editing app treats that as a picture to move around. I wanted it to behave like the site does, where the lattice grows, lights up and rearranges, and that is easier to write than to keyframe.

I also wanted every change to be a sentence. "The logo comes in too late" should mean one number changes and the video renders again, not an evening in a timeline. And I wanted to try ideas on my own machine, without paying a video model for every attempt.

How it works

One folder holds everything, and every step saves its own files: the script, one voice file per sentence, the music, the scenes, the renders. When a sentence sounds wrong, that sentence is recorded again and nothing else changes.

  • script.json holds the voice lines and the shortest each scene may be.
  • timeline.py makes each scene as long as its voice line plus padding, then rounds it up to whole beats of the music. Every cut lands on a beat because the timeline is built from the beat grid.
  • core.py holds the brand: colours, IBM Plex, easing curves, and the lattice and P with the same geometry as the site's own code.
  • Scene files are Python functions of time. Skia draws each frame and adds the bloom, NumPy and OpenCV grade the footage, and raw frames go straight into ffmpeg.
  • mix.py places each voice line at its cue, ducks the music under it, measures the loudness and applies one gain to -14 LUFS, where YouTube plays it.
Timeline chart: voice lines as black bars, music beats as ticks, and five picture scenes whose cuts all land on a beat at 110 BPM
The timeline is built from the music: scenes stretch to their voice line, then snap to the beat.
From the 55-second promo. The P assembles from lattice cells, turns into a 3D extrusion and hands out project files, all drawn in Skia.

It isn't one script. Each film got its own scene file on top of the shared core, and the code grew with every video.

Lines of Python per file

  1. YouTube tutorial (tut.py)1,223
  2. Tutorial, guide cut (guide.py)982
  3. First Lab promo (render.py)685
  4. Drawn animation (anim.py)607
  5. 30-second cut (v2.py)436
  6. Cinematic promo (cine.py)423
  7. Brand core (core.py)377
Counted on the Mac copy of the project: 7,828 lines across 32 scripts, before the PC renderer was added.

Footage, voice and music

Stock clips come from Pixabay. The agent searches, shows me a numbered contact sheet, and I pick by number. Each clip is then graded to the brand, with the lattice cut in as windows, wipes and overlays. A credits file keeps the source of every clip.

From the cinematic promo: Pixabay footage graded to the brand, then the triangle lattice over a particle wave.

The voice is ElevenLabs, the voice Eric, fetched lossless at 48 kHz, one file per sentence. After that a light chain: EQ, gentle compression, a short room and a limiter. The score is ElevenLabs Music, asked for the mood, the exact length and a hit on the cut points, with the prompt saved next to the file so it can be made again.

Real 3D from Blender

When a shot needs real depth, the agent builds it in Blender. I don't open Blender: a portable Blender 5.2.3 sits in the project, the agent writes the scene as Python, and Cycles renders it on the graphics card with OptiX. The Earth, Moon and satellite models come from NASA. Each shot comes out as an image sequence that the Python renderer composites with the HUD, the voice and the music.

Preview render of a 3D opener at 720p. Previews stay small so a note costs minutes, not an hour.

Making it fast

The renderer started on my Mac, where the four-minute YouTube video took about 15 minutes. Then it moved to my Windows desktop for the heavy work: an Intel Core Ultra 9 285K with 24 cores, 64 GB of RAM and an RTX 4070 SUPER. The first render there showed 7% CPU in Task Manager, and the estimate for the same video was almost three hours.

Two things were wrong. The renderer was one Python process, so it kept one core busy out of 24. And every frame took 1.3 seconds, almost all of it Skia compositing on the CPU: the bloom, the vignette and the film grain, about 20 full-frame passes per frame. The Windows build of Skia does this far slower than the Mac one.

Rendering the 4:15 YouTube video at 1080p

  1. CPU, one process2 h 46 min
  2. CPU, 22 processesabout 15 min
  3. GPU, one process1 min 32 s
  4. GPU, 12 processes43 s
Measured on 7 November 2026: 7,647 frames at 1920x1080. The first two are worked out from timed samples, the last two are full renders.

Fix 1: every core at once

A parallel renderer splits the timeline into chunks on whole frames, renders each chunk in its own process and joins them with ffmpeg without encoding again. I checked it frame by frame against a single-process render: the same frame count, no gaps or doubles at the seams. That alone was about 11 times faster.

Fix 2: drawing on the GPU

An invisible window gives Skia an OpenGL context, so it draws on the graphics card instead of in memory. The drawing code didn't change at all, only the 12 places that create a drawing surface. A frame went from 1,300 ms to about 5 ms.

Drawing one 1080p frame

  1. Skia on the CPU1,300 ms
  2. Skia on the GPU5 ms
Measured on 7 November 2026 on the same frame. GPU stills match the CPU render at 46 to 52 dB PSNR, which looks identical.

Now the whole build, with the render, the voice effects and the mix, takes about a minute. A 4K render takes about three and a half minutes. Final encodes run on the card's NVENC encoder, and a live progress bar in the terminal shows every chunk. The full measurements and the code changes are in the toolbox entry.

Notes, one sentence at a time

I watch a render and write what bugs me, one sentence per note. Previews are 720p and only the scene that changed renders again. My first brand promo went through six versions this way.

From my YouTube video about the promo: each note in a sentence, each one a new version.

What broke

  • Glitchy transitions. The triangle wipe broke the old scene into hard-edged triangles that popped off in about 13 frames, so bits of text flickered. The new one has soft edges, a crossfade underneath and runs 0.7 s instead of 0.45 s.
Three transitions at half speed: the old wipe on the left, the soft one on the right.
  • A smeared voice. Pitch-shifting the voice to make it deeper smeared it. Now it is EQ and compression only.
  • Pumping loudness. A single-pass normaliser kept riding the level. Measuring first and applying one gain fixed it.
  • Dying encoders. 16 processes at 4K made some encoders die, most likely out of memory. The default is 12, and past 8 jobs the chunks encode on the CPU, because a GeForce card allows 8 NVENC sessions at once.
  • Different text. Windows and macOS draw glyph edges slightly differently, so a PC render and a Mac render of the same frame differ at about 40 dB. Only the text antialiasing, not the effects.

Use it yourself

The renderer is tied to my brand, so I don't hand it out as one file. What you can take is the method: the workflow and its prompts in my AI toolbox, built on HyperFrames, an open-source framework from HeyGen, installed as a Claude Code plugin, that turns HTML scenes into MP4. Start there, and write a Python scene only when HTML gets slow.

The scripts for my YouTube videos come from my own writing skills; the Shorts version is in the toolbox too.

Who built it

  • Vedran PetračDirection, code, videoI build apps and websites, and the odd maths model.About me

Next in the Lab

The whole Lab