Skip to main content

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:

  1. Preprocess. py2js/src/preprocess.ts rewrites the Python source. It removes the manim, numpy, random, math, and itertools imports (their names are injected as globals instead), rewrites numpy calls into numpy-ts calls, and desugars the few constructs the compiler cannot parse, such as nested multi-for comprehensions. Any other import becomes a clear error.
  2. 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 + LEFT or matrix products) works. It also turns self.play(...) and self.wait(...) into await calls, so the Python still reads synchronously.
  3. 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.
  4. 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 why self.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:

  • MathTex and Tex use 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.
  • numpy is backed by numpy-ts, which holds a WebAssembly heap. wanim sizes that heap at startup, and configureWasm can 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 py2js bridge and the <wanim-scene> element;
  • clones rapydscript-ng at the pinned commit, applies py2js/patches/rapydscript-ng.patch, and writes the patched rapydscript.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.