@agarwal29796/manim-js
v0.2.0
Published
Manim.js — a Manim-inspired, framework-agnostic animation engine for the web. Author scenes in code; render anywhere via a tiny Renderer interface.
Downloads
225
Maintainers
Readme
Manim.js
Manim-inspired, framework-agnostic animation engine for the web. Author scenes in code, compile them to a deterministic timeline, and render anywhere through a tiny
Rendererinterface.
Independent project inspired by Manim; not affiliated with or endorsed by the Manim Community.
Why
- Deterministic. A scene compiles to a
Timelinewhosesample(t)is a pure function of time. Scrubbing, seeking and frame-accurate export all just work — no live state to unwind. - Renderer-agnostic. The engine (
core/) has zero dependencies and never touches a canvas. Pixels are someone else's job, behind a 2-methodRendererinterface. The package ships a Canvas2D renderer; embed your own to draw through an existing pipeline. - Real morphing. Shapes are point sets, so
transform(circle, square)interpolates any pair of shapes by resampling to a common point count.
Install
npm i @agarwal29796/manim-jsQuick start
import { Scene, circle, square, text, create, write, transform } from "@agarwal29796/manim-js";
import { CanvasRenderer, Player } from "@agarwal29796/manim-js/canvas";
const s = new Scene({ width: 1280, height: 720 });
const title = text("hello", { y: -200, size: 80 });
const c = circle({ r: 120, stroke: "#5B5BD6" });
s.play(write(title));
s.play(create(c));
s.play(transform(c, square({ size: 220, stroke: "#FF6B5B" })));
const timeline = s.compile();
const renderer = new CanvasRenderer(canvas.getContext("2d")!);
new Player(timeline, renderer, 1280, 720).play();Authoring vocabulary
Scene.play(...anims, { run, ease }), Scene.wait(s), Scene.add(...).
- Shapes:
circle,square,rect,polygon,line,functionGraph,numberLine,text - Animations:
create,write,fadeIn,fadeOut,transform,shift,scaleBy,rotate,indicate - Easing:
linear,easeInOut,easeOut,easeIn,easeOutBack,thereAndBack
The seam (for custom renderers)
interface Renderer {
clear(width: number, height: number): void;
draw(op: DrawOp): void; // DrawOp = PolyOp | TextOp
}That is the entire public contract between engine and pixels. Implement it and the engine runs unchanged. (This is how it renders natively inside other apps.)
Layout
src/core/ engine — mobjects, animations, scene/timeline, IR (zero deps)
src/canvas/ reference Canvas2D renderer + rAF Player
studio/ browser playground (npm run studio)Develop
npm install
npm run build # tsup -> dist (ESM + CJS + .d.ts)
npm run studio # bundle + serve the live-coding playground at :8123
npm test # vitest
npm run check:exports # publint + are-the-types-wrongRoadmap
- [x] LaTeX
Write—@agarwal29796/manim-js/tex(MathJax → SVG paths).await initTex(), thentex("E=mc^2"). - [x]
axes()/ coordinate plane (withc2p/p2c/plot),brace(),vector(),dot() - [x] Easing
lag/stagger inplay(play(..., { lag })andstagger([...])) - [x] Camera moves —
moveCamera()/zoomCamera() - [x] Frame-accurate MP4 export off the timeline —
@agarwal29796/manim-js/export(exportMP4, WebCodecs) - [ ] More mobjects (matrices, tables), 3D
Subpath entry points
| Import | What | Extra dep |
|---|---|---|
| @agarwal29796/manim-js | core engine (scenes, mobjects, animations) | none |
| @agarwal29796/manim-js/canvas | reference Canvas2D renderer + Player | none |
| @agarwal29796/manim-js/tex | initTex, tex, texGroup (animated LaTeX) | mathjax-full |
| @agarwal29796/manim-js/export | exportMP4, exportFrames (MP4 off the timeline) | mp4-muxer |
The two heavy deps are optional peers — only installed if you use those subpaths; the core stays dependency-free.
License
Apache-2.0 © Archit Agarwal
