Turning research papers into Manim animations
Fine-tuning a small model to generate ManimThe library 3blue1brown uses to generate its videos. See manim.community. animations straight from a research paper.
The idea
Input a paper and get back a set of clear animations explaining it. Larger models were able to generate some good looking animations, but every video could end up costing $2-3 given how expensive tokens were. So the plan was to distill that ability into a small model I could run cheaply.
Where the instructions for the animations come from
To fine-tune a model to make animations, I first needed prompt-and-result pairs: an instruction describing an animation, and the ManimThe library 3blue1brown uses to generate its videos. See manim.community. code that actually produces it. I built a whole system to generate those pairs.
It starts from a database of papers I crawled from AI/ML conferences. For each paper, I asked Gemini 3 Flash (the teacher) to break it into about four animation “scripts”: a voice-over plus instructions for what to draw. Those scripts are the prompts.

To get the results, Gemini then turned each script into actual ManimThe library 3blue1brown uses to generate its videos. See manim.community. code and I compiled it. Roughly 75% of Gemini's code compiled into a video, and those (script → compiling code) pairs became my SFT dataset for Qwen2.5-Coder-3B.

The small model was terrible on its own
I tried feeding those same scripts to Qwen2.5-Coder-3B directly, and it produced almost nothing but ManimThe library 3blue1brown uses to generate its videos. See manim.community. compilation errors. So I was forced to first do SFT on the Gemini pairs, and then a round of DPO on top.

Building preference pairs for DPO
For the DPO stage I sampled the SFT model eight times per prompt. Some attempts compiled, most didn't, and I paired each failed attempt with a compiling one to build preference pairs. Prompting, SFT, and DPO together moved pass@1 from 2.5% to 25%.

Reward hacking
Pass@1 went up, but the animations themselves weren't good. A compilation-based reward is useful, but over-optimizing it just makes Manim code that compiles into a video, not something that looks good. You can see it below: next to a Gemini training example, my fine-tuned model's output is far simpler and emptier.
What the course actually taught me
CS234 taught me the math behind reinforcement learning: policy optimization, DPO/GRPO, reward design. In this project I therefore struggled with the "crafty" part of doing SFT/DPO well: building a good and diverse dataset and a high quality set of evals.
Teacher vs. student
What I learned
- My first time doing SFT / DPO on an LLM. I learned the shape of the data necessary for doing this, and got first experience with using cloud compute (in this case Google Collab).
- The real bottleneck in finetuning small models is high quality data and the evals, not the adapter hyperparameters. I spent a bunch of time changing hyperparams with practically no results.
What I'd do differently
- Build a real benchmark first: a fixed set of ~50 animation scripts with reference videos from a large model, and score every model against that same set. My only signal was compile rate, measured on inconsistent sets, so my evaluation was a mess.
- Choose models from that benchmark: I'd compare small candidates (Liquid, Qwen, Gemma) to pick the base model, and also measure the teacher (Gemini 3 Flash) as the target to beat.
- Start from Manim datasets that already exist on Hugging Face, like bespoke-manim or manim_python. My hand-generated Gemini data was lower quality, partly because Gemini itself only compiled ~75% of the time and the subset of papers used to generate the animation scripts was limited.
- Try GRPO, not just DPO: GRPO does online learning over the same kind of samples I generated for this project and might fit this task better.
- Don't over-sample the same prompt: Building 7+ pairs from a single prompt overweighted a few papers and likely pushed the model to overfit them.