TakumiTakumi

Video via ffmpeg

Render time-stepped raw RGBA frames into an MP4.

Preview

For real video — high frame rate, MP4, audio later — renderAnimation() doesn't fit. Instead, loop over time with render(..., { format: "raw", timeMs }) and pipe each frame's raw RGBA bytes straight to ffmpeg's stdin. Render and write one frame at a time so memory stays bounded no matter how long the clip is.

import { render } from "takumi-js";
import { spawn } from "bun";

const fps = 30;
const durationSeconds = 4;
const totalFrames = fps * durationSeconds;
const width = 1200;
const height = 630;

const ffmpeg = spawn(
  [
    "ffmpeg",
    "-y",
    "-f",
    "rawvideo",
    "-pixel_format",
    "rgba",
    "-video_size",
    `${width}x${height}`,
    "-framerate",
    `${fps}`,
    "-i",
    "pipe:0",
    "-vf",
    "format=yuv420p",
    "-c:v",
    "libx264",
    "-crf",
    "18",
    "output.mp4",
  ],
  { stdin: "pipe" },
);

const scene = <Scene />;

for (let i = 0; i < totalFrames; i++) {
  const frame = await render(scene, {
    width,
    height,
    format: "raw",
    timeMs: (i / fps) * 1000,
  });
  ffmpeg.stdin.write(frame);
}

ffmpeg.stdin.end();
await ffmpeg.exited;

Source: example/ffmpeg-keyframe-animation — full Scene with shiki tokens, keyframe definitions, and ffplay variant.

Edit on GitHub

Last updated on