Skip to content
MostlyKIGuessPublic

Repository files navigation

Manimator

Manimator turns a text idea or a PDF (arxiv papers work well) into a narrated Manim video with synchronized subtitles.

How it works

  1. An LLM writes a 60-second Manim scene and a matching narration script.
  2. A TTS engine reads the narration. The default engine is OmniVoice, a zero-shot voice cloning model: drop a 3-10 second reference clip into assets/voices/ and the narration comes out in that voice.
  3. Manim renders the scene, ffmpeg merges video, audio, and an ASS subtitle track. If the generated code fails to render, the same LLM gets the error message and fixes its own code (up to 2 retries).

Video styles

Both input types offer a Manim animation style and a narrated slide-show style.

A text idea offers:

Manim animation (default): the LLM writes an animated scene about the idea, as above.

Picture walkthrough: a narrated slide show of real images. The LLM plans the video as a list of segments, each naming a subject to illustrate, and the app looks each one up and downloads it. No image bytes come from the model, so this works with every provider. Four open, key-free sources are tried in order until one returns a usable image: a Wikipedia article's lead image, an Openverse search (public domain and CC0 only), a Wikimedia Commons image search, and the NASA image library. Concrete subjects (people, places, organisms, landmarks, standard scientific diagrams) resolve well; very niche or very recent specifics may have no image and get skipped.

A PDF offers:

Manim animation (default): the LLM picks one core concept from the paper and writes an animated scene about it, as above.

Figure walkthrough: a narrated slide show of the paper's own figures, in the style of NotebookLM video overviews and the Paper2Video line of work. The pipeline extracts each figure as a whole region by clustering the page's images and vector drawings (pymupdf), so multi-part figures and vector-drawn plots come out intact rather than split into pieces; it falls back to full-page renders only for unusual layouts. It then shows the LLM the paper plus every figure and gets back a script: which figures to show, in what order, with a heading and narration for each. The slide code is generated from a fixed template, with each slide timed to its narration audio, so this mode has no LLM-written Manim code and cannot fail on generated syntax. Works with all four providers; Codex gets the extracted paper text and the figure images instead of the PDF file.

Both walkthrough styles share an explainer style and an optional focus box:

Style Shape Rough length
Summary Motivation, method, results 150-350 words
Deep explainer Problem, prior work, the idea, why it works, limitations 300-550 words
Long explainer The deep spine at maximum depth 500-900 words

Since each slide waits for its own narration audio, a longer style makes a longer video. The focus box is free text ("spend most of the time on the architecture") and takes priority over the style's default emphasis.

LLM providers

The app supports four providers. The provider dropdown in the sidebar only shows providers that are usable: key-based providers appear when their key is set, and Codex appears when the codex CLI is installed.

Provider Needs Default model Override with
Gemini GEMINI_API_KEY gemini-2.5-pro GEMINI_MODEL
Claude ANTHROPIC_API_KEY claude-opus-4-8 CLAUDE_MODEL
OpenAI OPENAI_API_KEY gpt-5.5 OPENAI_MODEL
Codex codex CLI + codex login account default CODEX_MODEL

Copy .env.example to .env and fill in your keys, or export them in your shell. The Gemini fallback path additionally uses Google Search grounding to look up Manim API errors; the other providers fix code from the error message alone.

The Codex provider runs codex exec with a JSON output schema, so it works with a ChatGPT subscription instead of an API key. The codex CLI cannot ingest a PDF file directly, so for PDFs the app feeds it the extracted text (pymupdf) and attaches the figures as images via --image; it sees content rather than layout. Generation also goes through a local CLI process, so it is slower to start than the API providers.

Voices

Voices for OmniVoice live in assets/voices/, one directory per voice:

assets/voices/
  karpathy/
    reference.wav    # 3-10 seconds of clean speech
    reference.txt    # optional transcript; auto-transcribed if absent

See assets/voices/README.md for details on adding voices. The Kokoro engine is still available in the sidebar; it has a fixed voice but produces word-level subtitle timing, while OmniVoice subtitles are sentence-level.

Subtitles are off by default. Enable "Burn subtitles into the video" in the sidebar to render the narration at the bottom of the frame.

Running with Nix

The repo ships a flake that provides every system dependency (ffmpeg, texlive, cairo, pango, espeak-ng) and a Python virtualenv:

nix develop          # first entry creates .venv
pip install -r requirements.txt
streamlit run src/app.py

The texlive closure is several gigabytes; the first nix develop takes a while. Subsequent entries are instant.

Running with Docker

docker pull mostlyk/manimator
docker run -p 8501:8501 -e GEMINI_API_KEY='your_api_key' mostlyk/manimator

Or build locally:

docker build -t manimator .
docker run -p 8501:8501 -e ANTHROPIC_API_KEY='your_api_key' manimator

Then open http://localhost:8501. Pass whichever provider keys you have as -e flags.

Running manually

System requirements: Python 3.12, ffmpeg, a texlive installation (Manim needs latex for MathTex), cairo, pango, and espeak-ng (for Kokoro).

With conda:

conda env create -f environment.yml
conda activate manimator
streamlit run src/app.py

With a plain virtualenv:

python -m venv .venv
source .venv/bin/activate    # Windows: .venv\Scripts\activate
pip install -r requirements.txt
streamlit run src/app.py

For GPU inference install the torch build matching your hardware first: https://pytorch.org/get-started/locally/. OmniVoice runs on CPU as well, just slower. On GPUs with little memory (4 GB and below), the app clears the CUDA cache between sentences and retries once on out-of-memory; if it still cannot fit, set TTS_DEVICE=cpu to force CPU synthesis. The nix dev shell also sets PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True, which reduces fragmentation on small cards.

Examples

Prompts that produce good videos:

  • "Explain the Fourier transform by decomposing a square wave into sine waves"
  • "Show how gradient descent finds the minimum of a parabola, with the learning rate visualized"
  • "Visualize matrix multiplication as a linear transformation of the plane"
  • Paste an arxiv PDF and the app summarizes one core concept from it

Prompts that work poorly: anything needing 3D scenes or images (both are excluded from the generated code on purpose), and topics that need more than 60 seconds.

Tests

pytest                  # fast unit tests only
pytest -m integration   # loads TTS models, renders video, calls live APIs

Hosted version

The app is hosted on Hugging Face Spaces: https://huggingface.co/spaces/mostlyk/Manimator

Contributing

  • The model is primed with the examples in src/api/guide.md. PRs adding good Manim examples there directly improve generation quality.
  • If a prompt fails to produce a video, open an issue with the prompt so an example covering that failure can be added.
  • New reference voices are welcome; see assets/voices/README.md.
  • Code and prose in this repo follow .rules.

Releases

Packages

Used by

Contributors

Languages