Awesome-omni-skill manim-scroll
Build and integrate scroll-driven Manim animations with pre-rendered assets, manifest generation, and the web runtime. Use when users ask about Manim scroll playback, render pipelines, native text animation, or integrating the runtime.
git clone https://github.com/diegosouzapw/awesome-omni-skill
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/frontend/manim-scroll" ~/.claude/skills/diegosouzapw-awesome-omni-skill-manim-scroll && rm -rf "$T"
skills/frontend/manim-scroll/SKILL.mdManim Scroll
Scroll-driven Manim animations for the web. Pre-render mathematical animations with Manim and play them back smoothly as users scroll.
Quick Start (Next.js)
The recommended approach uses the Next.js plugin for automatic build-time rendering.
- Install the unified package:
npm install @mihirsarya/manim-scroll
- Configure
:next.config.js
const { withManimScroll } = require("@mihirsarya/manim-scroll/next"); module.exports = withManimScroll({ manimScroll: { pythonPath: "python3", quality: "h", fps: 30, resolution: "1920x1080", }, });
- Use the component:
import { ManimScroll } from "@mihirsarya/manim-scroll"; export default function Page() { return ( <ManimScroll scene="TextScene" fontSize={72} color="#ffffff" scrollRange="viewport" style={{ height: "100vh", background: "#111" }} > Welcome to my site </ManimScroll> ); }
The plugin automatically extracts props, renders animations, and caches them.
Native Mode (No Pre-rendered Assets)
For text animations without pre-rendered video/frames, use native mode. This renders text directly in the browser using SVG, replicating Manim's Write/DrawBorderThenFill animation.
<ManimScroll mode="native" fontSize={48} color="#ffffff" scrollRange="viewport" style={{ height: "100vh", background: "#111" }} > Currently building </ManimScroll>
Native Mode Benefits
- No build step required - works immediately without Python/Manim
- Perfect sizing - text renders at exact pixel size (no scaling artifacts)
- Smaller bundle - no video/frame assets to download
- Authentic Manim animation - replicates Write/DrawBorderThenFill exactly:
- Uses Manim's exact
formulalag_ratio = min(4.0 / length, 0.2) - Two-phase animation: stroke drawing (0-50%) and fill transition (50-100%)
- Progressive contour drawing across all characters
- Matches Manim's
rate function for Write animationlinear
- Uses Manim's exact
- Scroll-driven - same scroll progress behavior as pre-rendered mode
Custom Fonts in Native Mode
For authentic Manim typography, provide a font URL (woff, woff2, ttf, otf):
<ManimScroll mode="native" fontSize={48} color="#ffffff" fontUrl="/fonts/CMUSerif-Roman.woff2" > Mathematical text </ManimScroll>
Progress-Based Animation (No Scroll)
Animate text programmatically via progress value or duration instead of scroll.
Controlled Progress Mode
Pass
progress prop (0-1) to render at exact animation state:
const [progress, setProgress] = useState(0); <ManimScroll mode="native" progress={progress}> Hello World </ManimScroll> <input type="range" value={progress} onChange={(e) => setProgress(+e.target.value)} min={0} max={1} step={0.01} />
Imperative Playback with Hook
Use
useNativeAnimation for programmatic control:
import { useRef, useEffect } from "react"; import { useNativeAnimation } from "@mihirsarya/manim-scroll"; function AutoPlayDemo() { const containerRef = useRef<HTMLDivElement>(null); const { isReady, play, seek, setProgress, isPlaying } = useNativeAnimation({ ref: containerRef, text: "Hello World", fontSize: 72, color: "#ffffff", }); // Auto-play on mount useEffect(() => { if (isReady) { play(2000); // Play over 2 seconds } }, [isReady, play]); return ( <div ref={containerRef}> <button onClick={() => play(1000)}>Play</button> <button onClick={() => seek(0.5)}>Jump to 50%</button> <button onClick={() => setProgress(0)}>Reset</button> </div> ); }
Playback Options
The
play() method accepts options for fine-grained control:
play({ duration: 2000, // Animation duration in ms delay: 500, // Delay before starting easing: "ease-in-out", // Easing: "linear" | "ease-in" | "ease-out" | "ease-in-out" | "smooth" loop: true, // Loop animation direction: -1, // Reverse playback onComplete: () => {}, // Callback when done });
useNativeAnimation Hook
Full programmatic control:
import { useRef } from "react"; import { useNativeAnimation } from "@mihirsarya/manim-scroll"; function NativeDemo() { const containerRef = useRef<HTMLDivElement>(null); const { progress, isReady, pause, resume, play, seek, setProgress, isPlaying } = useNativeAnimation({ ref: containerRef, text: "Hello World", fontSize: 72, color: "#ffffff", scrollRange: "viewport", // Ignored when using play()/setProgress() }); return ( <div ref={containerRef} style={{ height: "100vh" }}> {!isReady && <div>Loading...</div>} </div> ); }
Inline Mode
For animations that flow with surrounding text (like within a paragraph):
<p> I'm building{" "} <ManimScroll scene="TextScene" fontSize={24} color="#667eea" inline style={{ width: "150px", height: "30px" }} > the future </ManimScroll>{" "} today. </p>
Inline mode:
- Renders with a transparent background
- Uses
for flow with textdisplay: inline-block - Adjusts the Manim camera to fit text tightly with minimal padding
- Outputs WebM with alpha channel (for video mode) or transparent PNGs (for frames mode)
Scroll Range Configuration
Control when the animation plays relative to scroll position.
Presets (Recommended)
<ManimScroll scrollRange="viewport">...</ManimScroll> // Default: plays as element crosses viewport <ManimScroll scrollRange="element">...</ManimScroll> // Tied to element's own scroll position <ManimScroll scrollRange="full">...</ManimScroll> // Spans entire document scroll
Relative Units
<ManimScroll scrollRange={["100vh", "-50%"]}>...</ManimScroll> <ManimScroll scrollRange={["80vh", "-100%"]}>...</ManimScroll>
Supported units:
- viewport height percentagevh
- element height percentage%
- pixelspx- Plain numbers - treated as pixels
Pixel Values (Legacy)
<ManimScroll scrollRange={{ start: 800, end: -400 }}>...</ManimScroll> <ManimScroll scrollRange={[800, -400]}>...</ManimScroll>
useManimScroll Hook
For advanced use cases requiring custom control:
import { useRef } from "react"; import { useManimScroll } from "@mihirsarya/manim-scroll"; function CustomAnimation() { const containerRef = useRef<HTMLDivElement>(null); const { progress, isReady, error, pause, resume, seek, isPaused } = useManimScroll({ ref: containerRef, manifestUrl: "/assets/scene/manifest.json", scrollRange: "viewport", }); return ( <div ref={containerRef} style={{ height: "100vh" }}> {!isReady && <div>Loading...</div>} <div>Progress: {Math.round(progress * 100)}%</div> <button onClick={pause}>Pause</button> <button onClick={resume}>Resume</button> </div> ); }
Auto-Resolution Mode
When using with the Next.js plugin, you can let the hook resolve the manifest automatically:
const { progress, isReady } = useManimScroll({ ref: containerRef, scene: "TextScene", animationProps: { text: "Hello", fontSize: 72, color: "#fff" }, });
Vanilla JS Usage
Pre-rendered Animations
import { registerScrollAnimation } from "@mihirsarya/manim-scroll-runtime"; const container = document.querySelector("#hero") as HTMLElement; registerScrollAnimation({ container, manifestUrl: "/dist/scene/manifest.json", mode: "auto", scrollRange: "viewport", onReady: () => console.log("ready"), onProgress: (progress) => console.log(progress), });
Native Text Animations
import { registerNativeAnimation } from "@mihirsarya/manim-scroll-runtime"; const container = document.querySelector("#hero") as HTMLElement; registerNativeAnimation({ container, text: "Animate this", fontSize: 72, color: "#ffffff", scrollRange: "viewport", onReady: () => console.log("ready"), });
Manual Rendering (Non-Next.js)
For custom workflows, use the Python CLI directly:
python render/cli.py \ --scene-file path/to/scene.py \ --scene-name MyScene \ --output-dir ./dist/scene \ --format both \ --fps 30 \ --resolution 1920x1080 \ --quality k
Render Text with Props
echo '{"text": "Hello World", "fontSize": 72, "color": "#ffffff"}' > props.json python render/cli.py \ --scene-file render/templates/text_scene.py \ --scene-name TextScene \ --props props.json \ --output-dir ./dist/scene \ --format both
CLI Options
| Option | Default | Description |
|---|---|---|
| (required) | Path to the Manim scene file |
| (required) | Scene class name to render |
| (required) | Directory for render outputs |
| | Output format: , , or |
| | Frames per second |
| | Resolution as WxH |
| | Manim quality: , , , |
| - | Path to JSON props file |
| | Render with transparent background |
Package Structure
| Package | npm | Description |
|---|---|---|
| | Unified package (recommended) |
| | React component and hooks |
| | Next.js build plugin |
| | Core scroll runtime |
| - | Python CLI for Manim rendering |
Next.js Plugin Configuration
| Option | Default | Description |
|---|---|---|
| | Path to Python executable |
| | Manim quality preset (l, m, h, k) |
| | Frames per second |
| | Output resolution |
| | Output format (frames, video, both) |
| CPU count - 1 | Max parallel renders |
| | Enable verbose logging |
| | Remove unused cached assets |
Component Props Reference
| Prop | Type | Description |
|---|---|---|
| | Scene name (default: ) |
| | Font size for text animations |
| | Color as hex string (e.g., ) |
| | Font family for text |
| | Enable inline mode with transparent background |
| | Padding around text in inline mode (Manim units, default: ) |
| | Explicit manifest URL (overrides auto-resolution) |
| | Playback mode |
| | URL to font file for native mode |
| | Stroke width for native mode (default: ) |
| | Scroll range: preset, tuple, or object |
| | Called when animation is loaded |
| | Called on scroll progress |
| | CSS class for the container |
| | Inline styles for the container |
| | Text content (becomes prop) |
Requirements
- Python 3.8+ with Manim installed (for pre-rendered mode)
- Node.js 18+
- Next.js 13+ (for the plugin)
Additional Resources
- See
for package internals and diagramsreferences/ARCHITECTURE.md - See
for complete type definitionsreferences/API.md - See
for creating custom Manim scenesreferences/CUSTOM_SCENES.md - See
for common issues and solutionsreferences/TROUBLESHOOTING.md