"Remotion for real 3D." Write a 3D scene in TypeScript — meshes, characters, physics, audio — run one command, and get a deterministic MP4. The same scene plays live in the browser, byte-for-byte identical to the export.
npm i -D @vsim/cli @vsim/authoring
npx vsim render scene.ts -o out.mp4 # add --workers 4 for multi-coreThat's the whole loop: a .ts file in, a reproducible .mp4 out — no GPU, no native
renderer, no cloud account. The default engine is a pure-TypeScript rasterizer with
per-pixel PBR lighting, shadow maps, and supersampling, so it runs identically on your
laptop, in CI, and in the browser.
Everything below is rendered by vsim itself — clone the repo and pnpm showreel rebuilds
all of it from source.
examples/06-fox — golden-hour sun + glow, sky-derived ambient, fog, 700 blades of grass. Pure TypeScript, no GPU.
examples/24-campfire — one flickering point light with inverse-square falloff (decay: 2) IS the scene: breathing cube shadows, spark & smoke particles, ACES rolling the flames off filmically.
And this one wasn't written by a person at all:
The AI wrote this entire film from one prompt — vsim film -p "how a CDN makes websites fast". Claude authors a schema-validated screenplay (filmdoc.json, committed right here); vsim renders it deterministically. The screenplay re-renders byte-identically forever — no AI needed after the first take. See how.
![]() |
![]() |
![]() |
Character from primitives — no rig, just pivoted node groups (21-mouse) |
Normal mapping — both walls are the same flat quad; the right one carries a tangent-space map (22-normalmap) |
Deterministic physics — Rapier rigid bodies, the same collapse every run (02-physics) |
![]() |
![]() |
![]() |
Manga mode — cel-shading + ink outlines with one flag: style: "manga" (09-manga) |
Keyframe animation — a hand-animated kick and a launched ball (08-soccer) |
Text & titles — true vector type composited over the 3D (20-titles) |
There are 25 examples in examples/ — rigged glTF characters,
MakeHuman humans with real skin textures, a VRM avatar, beat-synced audio, morph-target
lip-sync, procedural parks, a trotting quadruped, and more:
pnpm install
pnpm example:fox # → out/fox.mp4 (any example works: cube, physics, crossfade, …)
pnpm showreel # renders all 24 reel scenes in parallel → out/showreel.mp4- Preview == render == every variant. Time is frame-based (never wall-clock), all
randomness flows through a seeded RNG (
Math.randomis lint-banned in runtime code), and CI enforces byte-identical renders with golden-frame hashes. Render one video or fan out 100 personalized variants — every pixel is reproducible. - Runs anywhere. The reference renderer is pure TypeScript: no GPU, no node-gyp, no headless-Chrome. CI boxes, serverless, browsers — same bytes everywhere. A Three.js engine (GPU) is a drop-in swap for high-fidelity preview.
- Web-first. The player, the rig loader, and even MP4 export (WebCodecs) run in the browser on the exact same runtime as the headless renderer.
- Scriptable like code, editable like a doc. Every scene is a plain, zod-validated JSON document — authored fluently from TypeScript, diffable in git, and safely editable by the AI copilot.
- Code → video: declarative scene builder → MP4 via
vsim render(--workers Nsplits frames across cores, byte-identical to single-threaded). - Realistic software rendering: per-pixel PBR (roughness/metalness + full texture-map set with normal mapping), 2× supersampling in linear light, PCF-filtered directional shadows and point-light cube shadows, mip-mapped trilinear texture sampling, perspective-correct interpolation, sorted transparency, optional ACES tone mapping.
- Atmosphere: gradient sky with a visible sun disc + glow, sky-derived hemisphere ambient, linear distance fog, and closed-form deterministic particles.
- Animation: keyframe tracks, glTF skeletal clips with layered crossfades
(
idle → walk → run), ground-contact IK with stance locking (in-place walk cycles drive real locomotion), spring bones for secondary motion, and morph-target lip-sync. - Physics: deterministic Rapier rigid bodies, fixed-step, reproducible.
- Assets: glTF/GLB load + export — including browser-safe rig parsing (
loadRigFromUrl), plus a bundled character library (fox, humans, clothing) loaded by name withloadCharacter(). - Audio: mux a track into the MP4 and drive properties from beat frames.
- Live preview: a browser player that shares the exact runtime with the renderer,
plus in-browser MP4 export via WebCodecs (
renderToSink). - Photoreal finals: hand the same scene document to Blender Cycles for a path-traced
master (
apps/studio/cycles-render.mjs; works with justpip install bpy) — see the Blender guide.
Every AI feature follows one rule: the model writes a validated document at authoring
time, and the deterministic pipeline renders it. The AI can propose a bad scene or film —
it cannot emit an invalid one, and it never touches the render loop. It runs through the
Anthropic SDK (ANTHROPIC_API_KEY) or, if no key is set, the claude CLI (a Claude
Code login) — whichever you have.
Edit scenes in natural language — prompts become schema-constrained edit operations, grounded in the scene's existing objects:
vsim edit scene.ts --prompt "make the cube blue and add a point light" -o edited.scene.json
vsim edit scene.ts --prompt "spin it twice as fast" --render out.mp4import { editScene, CopilotSession } from "@vsim/ai";
const { doc, operations, summary } = await editScene({ doc: scene, prompt: "add a red floor" });
// Multi-turn refine — follow-ups resolve against the running transcript:
const session = new CopilotSession(scene);
await session.refine("make the cube blue");
await session.refine("now spin it twice as fast"); // "it" = the cube
session.document; // the edited scene so farWrite whole 2D explainer films from a topic — vsim film -p "<topic>" asks Claude for
a FilmDoc: the screenplay as a zod-validated document (stage entities, beats, actions,
camera), interpreted by the motion/ studio. The committed
filmdoc.json re-renders byte-identically forever, no AI required. The CDN film
featured above was made exactly this way, as was
films/load-balancer — both screenplays landed
valid on Claude's first attempt.
pnpm film:gen "how a CDN makes websites fast" # → screenplay + out/<slug>.mp4Direct 3D films from a story — vsim film -p "<story>" --template 3d asks for a
Film3DDoc instead: set preset, props, actors from the character library, beats of
move/play/face actions, and a shot list (wide/close/follow/orbit cuts). The
@vsim/film3d compiler lowers it to a plain scene document — gait clips crossfade by
travel speed, actors turn into their headings, cameras track head-height aim nodes — and
the 3D engine renders it. The schema validates per-character clips, contiguous beats, and
full camera coverage, so the model can't hand back a film that doesn't run.
films/snowy-park.film3d.json came out of exactly this
command; the hand-written films/fox-day.film3d.json shows
the whole vocabulary in 50 lines (pnpm film3d:fox renders it — vsim render accepts
*.film3d.json directly).
The first slice of the visual editor (surface 2). A browser app on top of the same engine: load a scene, play/scrub the timeline, select an object, edit its transform/colour live, keyframe those properties (value at one frame, another later → it animates, with clickable markers on the timeline), ask the AI copilot to change the scene in natural language, and render an MP4 — all in the browser. The preview updates instantly because the runtime reads the scene document every frame, so preview == render.
pnpm studio:server # backend (AI copilot + MP4 render) → http://localhost:8787
pnpm studio # editor (Vite dev server) → http://localhost:5173Built with Vite + vanilla TS (no UI framework), reusing @vsim/player + @vsim/engine-three in the
browser and @vsim/ai + @vsim/render in a tiny Node backend (the AI uses
ANTHROPIC_API_KEY or the claude CLI).
| Package | Role |
|---|---|
@vsim/core |
Scene document schema, fixed-timestep clock, seeded RNG, animation eval, math, engine interface — zero engine deps |
@vsim/engine-software |
Pure-TS reference rasterizer. Runs anywhere (no GPU), bit-identical — the determinism oracle & default renderer |
@vsim/engine-three |
Three.js production renderer (GPU, high fidelity) + experimental path-tracer / WebGPU backends |
@vsim/text |
Deterministic vector text rasterizer (bundled font → glyph fill) for screen-space titles/captions |
@vsim/physics-rapier |
Deterministic Rapier physics adapter |
@vsim/render |
Headless frame capture → ffmpeg → MP4 (+ audio mux, multi-core workers) |
@vsim/authoring |
Declarative builder API: code → scene document |
@vsim/player |
Browser real-time preview component |
@vsim/assets |
glTF/GLB asset pipeline (load + export) + character library |
@vsim/ai |
AI copilot: natural-language prompt → schema-constrained scene-document edits (Claude tool-use) |
@vsim/cli |
vsim render scene.ts -o out.mp4 · vsim film -p "…" · vsim edit scene.ts --prompt "…" |
@vsim/motion |
The 2D animation studio: design tokens, frame-pure timeline, explainer kit, deterministic HTML→MP4 recorder |
@vsim/film3d |
AI-directed 3D films: Film3DDoc (a validated high-level screenplay — sets, actors, beats, shots) compiled to a scene document |
A second way to make video, built on the same determinism contract: author a film as an
HTML/SVG page on a frame-pure seekable timeline (no wall clock — seek(f) is a pure
function of the frame index), and the recorder frame-steps it in headless Chromium into a
byte-reproducible MP4. Design tokens keep every asset on one palette; a 12-primitive
explainer kit (servers, packets-along-a-path, draw-on arrows, typing code blocks,
kinetic titles…) covers technical storytelling; the film is driven by an editable
screenplay.json.
![]() |
![]() |
![]() |
Technical explainer — DNS→TLS→request→render with karaoke captions (web-request) |
Voiced character short — Pip's mouth is driven by the narration's audio envelope (pip-hello) |
The template proof — a second explainer from the same kit, one screenplay later (message-queue) |
pnpm film:webreq # → out/web-request.mp4 — 60s "How a web request works"
pnpm film:queue # → out/message-queue.mp4 — 42s "What is a message queue?"
pnpm film:pip # → out/pip-hello.mp4 — Pip the mouse, with TTS narration + lip-sync
pnpm film:kit # → out/kit-sheet.mp4 — the animated primitive contact sheetNarration is built by tools/narrate.mjs: timed lines → TTS → one WAV + a per-frame mouth
envelope the puppet lip-syncs to. The in-repo engine is espeak-ng (offline, deterministic);
an ElevenLabs backend is included — set ELEVENLABS_API_KEY and switch
"engine": "elevenlabs" for a production voice, no film changes.
Prefer the AI to write the screenplay? See The AI authors, vsim renders.
- Quickstart
- Scene document reference
- Determinism guide
- ADR 0001 — render backend & determinism
- Guide: creating characters with Blender / MakeHuman — generate rigged, animated, textured glTF headlessly (incl. a realistic MakeHuman human with real skin) →
loadGltfRig
pnpm docs:site builds a static documentation site (landing page + the docs above) into
site/ — generated by the project's own tooling, no framework.
Requires Node ≥ 20, pnpm, and ffmpeg on PATH.
pnpm install
pnpm test # unit + determinism (golden-frame) tests
pnpm typecheck
pnpm lint # determinism lint (bans Math.random in runtime)
pnpm build # compile all packages to dist/Releasing is documented in RELEASING.md.
MIT









