Skip to content

kunalkushwaha/vsim

Repository files navigation

vsim — code → deterministic 3D video. TypeScript in, MP4 out, byte-identical everywhere, no GPU required.

"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-core
// scene.ts
import { scene } from "@vsim/authoring";

export default scene({ fps: 30, duration: 90, width: 640, height: 360 })
  .material("cube", { color: [0.95, 0.4, 0.4], roughness: 0.5 })
  .light({ type: "ambient", intensity: 0.35 })
  .light({ type: "directional", intensity: 1.2, direction: [-0.5, -1, -0.35] })
  .mesh("floor", { geometry: { kind: "plane", size: [20, 20] },
                   material: "cube", position: [0, -1, 0] })
  .mesh("cube", { geometry: { kind: "box", size: [1.4, 1.4, 1.4] },
                  material: "cube" })
  .camera({ position: [3, 2.2, 4.5], lookAt: [0, 0.3, 0], fov: 45 })
  .animate("cube", "rotation.y", [
    { frame: 0, value: 0 },
    { frame: 90, value: Math.PI * 2 },
  ])
  .build();
The exact scene.ts on the left, rendered: a red cube rotating over a floor with a moving shadow
← this file, rendered. Nothing else involved.

That'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.

Show me

Everything below is rendered by vsim itself — clone the repo and pnpm showreel rebuilds all of it from source.

A low-poly fox running through a golden-hour park — sun disc, long shadows, wind-blown grass, distance fog
examples/06-fox — golden-hour sun + glow, sky-derived ambient, fog, 700 blades of grass. Pure TypeScript, no GPU.

A fox resting by a flickering campfire at dusk — inverse-square firelight pooling around the pit, breathing cube shadows, rising sparks and smoke, dark woods in fog
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:

An AI-written 48-second explainer film: a browser, an origin server and three edge servers animate 'How a CDN makes websites fast' with packet flows and karaoke captions
The AI wrote this entire film from one promptvsim 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.

cartoon mouse waving normal-mapped brick walls under a raking light deterministic physics tower collapse
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 cel-shaded scene hand-animated soccer kick vector text titles over 3D
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

Why vsim?

  • Preview == render == every variant. Time is frame-based (never wall-clock), all randomness flows through a seeded RNG (Math.random is 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.

What's in the box

  • Code → video: declarative scene builder → MP4 via vsim render (--workers N splits 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 with loadCharacter().
  • 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 just pip install bpy) — see the Blender guide.

The AI authors, vsim renders

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.mp4
import { 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 far

Write whole 2D explainer films from a topicvsim 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>.mp4

Direct 3D films from a storyvsim 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).

vsim Studio — the visual editor

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:5173

Built 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).

Packages

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

motion/ — films from SVG + CSS

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.

how a web request works Pip the mouse, talking what is a message queue
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 sheet

Narration 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.

Docs

pnpm docs:site builds a static documentation site (landing page + the docs above) into site/ — generated by the project's own tooling, no framework.

Develop

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.

License

MIT

About

Remotion for real 3D — a deterministic, code-first 3D animation & short-video framework. Write a TypeScript scene, render a reproducible MP4.

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors