For developers
This page explains how wanim works inside and how to build it from source. If you only want to write scenes, start with the gallery.
The idea is simple. Pyodide, a full CPython runtime compiled to WebAssembly, is too heavy for demos that should be embeddable anywhere. So wanim treats manim code as a small domain-specific language: it translates that code to JavaScript and runs the JavaScript against a manim-compatible library.
The pipeline
A scene goes through four stages before anything is drawn:
- Preprocess.
py2js/src/preprocess.tsrewrites the Python source. It removes themanim,numpy,random,math, anditertoolsimports (their names are injected as globals instead), rewritesnumpycalls into numpy-ts calls, and desugars the few constructs the compiler cannot parse, such as nested multi-forcomprehensions. Any other import becomes a clear error. - Compile. The preprocessed source goes through
rapydscript-ng, built from a
pinned commit with a wanim patch. The patch does two things. It routes
arithmetic through helpers so that operator overloading (such as
UP + LEFTor matrix products) works. It also turnsself.play(...)andself.wait(...)intoawaitcalls, so the Python still reads synchronously. - Run. The compiled JavaScript executes in a hidden iframe, which gives the scene its own global scope, with the runtime shims in place. The scene builds objects from the library, and the library's classes reach rapydscript through wrappers.
- Draw.
src/drives the scene in real time. Each frame, the scene extracts geometry and the Canvas 2D renderer paints it. Between frames, the runtime yields to the browser, which is whyself.wait()is a real pause.
runPythonOnCanvas(source, canvas, options) runs all four stages. The
<wanim-scene> element is a thin wrapper around it.
The library
src/ is a TypeScript port of manim Community Edition, extended for the web.
It covers scenes, mobjects, VMobject, animations, cameras, and the
ThreeDScene camera. The interactive controls and InteractiveScene are
wanim additions built on the same model.
Typesetting and text work as follows:
MathTexandTexuse MathJax, bundled as on-demand chunks.- Text uses opentype.js with a configurable font chain. The default chain points at free CDN copies of Tinos and Noto Serif SC.
numpyis backed by numpy-ts, which holds a WebAssembly heap. wanim sizes that heap at startup, andconfigureWasmcan change it before first use.
Repository layout
wanim/
src/ manim-compatible TypeScript library and Canvas 2D renderer
src/element.ts the <wanim-scene> custom element
src/embed.ts code-in-fragment encoding and the iframe helper
py2js/ preprocessing, compiler bridge, runtime shims, rapydscript patch
demo/ editor page and the iframe player
scripts/ bundling, packaging, and the demo server
packaging/wanim/ package.json, README, and LICENSE for the npm package
tests/ Vitest suites over the library
Build and run
You need Node.js 20 or newer and pnpm.
git clone https://github.com/Acfboy/Wanim.git
cd Wanim
pnpm install
pnpm build:demo # bundles the runtime, compiles the patched compiler, builds the element
pnpm demo # editor at http://localhost:5174/demo/
pnpm build:demo does the heavy lifting:
- type-checks and bundles the library;
- builds the
py2jsbridge and the<wanim-scene>element; - clones rapydscript-ng at the pinned commit, applies
py2js/patches/rapydscript-ng.patch, and writes the patchedrapydscript.js.
The first run needs network access for that clone. The checkout is cached in
.rapydscript-ng/.
The files in dist/ are what you serve in production. See using wanim
for how to load them.
Checks
pnpm typecheck # TypeScript, without emitting
pnpm test # library test suite (Vitest)
pnpm test:py2js # preprocessing, error formatting, and runtime bridge tests
Packaging
The npm package is assembled from packaging/wanim/ plus build artifacts:
pnpm build:packages # rebuild dist/ and assemble the package in packages-out/
pnpm pack:packages # assemble from the current dist/ and run npm pack
The package is named @wanimjs/wanim. It ships the Node build with type
declarations, and a browser/ directory of prebuilt bundles. The subpath
exports are listed in using wanim.
numpy-ts, MathJax, and opentype.js are regular dependencies. In the browser bundles, numpy-ts is left as a bare import, so the consumer maps it with an import map or a bundler. The bundles do not include it.
Status
wanim is an early preview. The packaging and the embed API may still change before a stable release. Until then, the repository is the source of truth. Bug reports and scenes that fail to compile are especially useful. Open an issue with the Python source and what you expected.