Visualize Using Manim
by pluto-atom-4
Create comprehensive algorithm visualizations using Manim animations. Documents step-by-step transformations with formulas, color-coding, arrows, and multiple explanation formats. Use when building visual explanations for data structure operations, algorithmic transformations, or educational demonstrations.
Skill Details
Repository Files
2 files in this skill directory
name: visualize-using-manim description: "Create comprehensive algorithm visualizations using Manim animations. Documents step-by-step transformations with formulas, color-coding, arrows, and multiple explanation formats. Use when building visual explanations for data structure operations, algorithmic transformations, or educational demonstrations." license: Proprietary. LICENSE.txt has complete terms
Visualize Using Manim Skill
Overview
This skill provides a structured approach to creating algorithm visualizations using Manim (Mathematical Animation Engine). It guides you through designing clear, step-by-step animations that explain complex operations through visual transformation, coordinate mapping, and progressive reveal.
Manim visualizations are particularly effective for:
- Showing state transformations (before/after comparisons)
- Demonstrating coordinate/index mapping with arrows
- Color-coding elements to track movement through algorithms
- Animating step-by-step processes with intermediate states
- Including formulas and mathematical explanations inline
When to Use
Use this skill when you need to:
- Create visual explanations for algorithm operations
- Demonstrate data structure transformations
- Animate coordinate transformation formulas
- Build educational content showing step-by-step processes
- Create interview preparation materials with visual aids
- Illustrate complex algorithmic logic that benefits from animation
Visualization Architecture
A complete Manim visualization consists of three components:
1. Visualization Scene (operation_viz.py)
The main Manim Scene subclass that defines the animation logic.
2. Runner Script (operation_viz_example.py)
A standalone script that executes the visualization and saves output.
3. Supporting Documentation
Header notes and comments explaining the visualization strategy.
Header Structure for Visualization Files
A visualization file header should include:
"""
## Operation Overview
[1-2 sentence description of what operation is being visualized]
Shows the step-by-step transformation using [algorithm/formula]:
[Key formula or mathematical concept]
Example with concrete values:
[Before state example]
[After state example]
Visualizes:
1. [Element 1 of visualization]
2. [Element 2 of visualization]
3. [Element 3 of visualization]
"""
Step-by-Step Implementation Guide
1. Operation Overview Section
What to include:
- Clear description of the operation being visualized (1-2 sentences)
- The algorithm or mathematical formula involved
- Concrete example with input and output
- Bulleted list of what the visualization shows (3-5 key elements)
Example:
Visualization of the 90-degree clockwise matrix rotation algorithm.
Shows the step-by-step transformation using the coordinate transformation formula:
rotated[c][rows - 1 - r] = matrix[r][c]
Uses a 3×3 matrix example:
Original: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Rotated: [[7, 4, 1], [8, 5, 2], [9, 6, 3]]
Visualizes:
1. Original matrix on the left
2. Empty rotated matrix on the right
3. Element-by-element transformation with arrows
4. Color-coded elements to track movement
5. Transformation formula displayed during animation
2. Visualization Strategy Section
What to include:
- How data is represented visually (shapes, colors, layout)
- The animation flow (what happens in sequence)
- Color scheme and what each color represents
- Positioning strategy (left/right/center layout)
Template:
# Scene Layout Strategy
# - [Where element A goes] → [Why this placement]
# - [Where element B goes] → [Why this placement]
# - [Where element C goes] → [Why this placement]
# Color Scheme
# - [Color 1]: [Meaning]
# - [Color 2]: [Meaning]
# - [Color 3]: [Meaning]
# Animation Flow
# 1. [Initial state display] (timing: X seconds)
# 2. [Transformation step] (timing: X seconds)
# 3. [Result display] (timing: X seconds)
3. Key Visualization Concepts Section
What to include:
- 2-3 critical design decisions in the visualization
- For each: WHY was this choice made and HOW does it help understanding
- Trade-offs (e.g., complexity vs. clarity)
Template for each concept:
- [Visualization technique/choice]
[Why is this important? What understanding does it enable?]
[How is it implemented? Any constraints?]
Example:
- Why show both matrices side-by-side?
Parallel layout allows viewers to simultaneously see source and destination,
making the coordinate transformation mapping immediately intuitive.
Implemented with LEFT positioning for original, RIGHT for rotated.
- Why color-code each element?
Color persistence through the animation tracks individual element movement,
helping viewers understand which original position maps to which new position.
Implemented with a color palette that cycles through distinct colors.
- Why show arrows between matrices?
Arrows make the coordinate mapping explicit, showing the exact transformation
path from source to destination. This reinforces the mathematical formula.
Implemented with Create animation for emphasis, FadeOut to avoid clutter.
4. Animation Flow Section
What to include:
- Sequence of animation steps
- Timing for each major phase
- What user sees at each stage
- State transitions (e.g., when formulas appear/disappear)
Template:
Animation Sequence:
1. Title and formula display (0.5s write + 0.5s wait)
2. Original matrix creation (parallel Create for boxes and text)
3. Rotated matrix skeleton (empty boxes, indicates dimensions)
4. Element-by-element transformation loop:
- Highlight source element (Indicate with color)
- Display current formula (Write with current indices)
- Draw arrow to destination (Create Arrow)
- Update destination element (FadeIn text)
- Clear temporary elements (FadeOut formula and arrow)
5. Final summary (completion message + complexity info)
5. Implementation Notes Section
What to include:
- Key technical decisions in code
- How to calculate positions and indices
- Handling of flat vs. multi-dimensional data
- Color palette and animation timing choices
Example:
Technical Implementation Notes:
Position Calculation:
- Matrix elements positioned using: ORIGIN + direction * base + RIGHT * (c * spacing) + DOWN * (r * spacing)
- Spacing of 0.8 units ensures clear visual separation
- Base offsets (LEFT*3, RIGHT*3) center matrices on screen
Index Management:
- Flattened index = r * cols + c (for accessing VGroup items)
- Original position: [r][c], Transformed position: [c][rows - 1 - r]
- Indices displayed in real-time formula updates
Color Palette:
- 9-color cycle to distinguish all elements in 3x3 matrix
- Same color maintained throughout transformation to track movement
- Provides visual feedback when elements arrive at destination
Animation Timing:
- Source highlight: Indicate() with 1.2 scale factor
- Arrow creation: 0.3s default
- Element arrival: FadeIn with simultaneous Indicate highlight
6. Multiple Summary Formats
30-Second Pitch: Natural, verbal explanation suitable for quick communication:
"This visualization shows how a matrix is rotated 90 degrees clockwise
using a coordinate transformation formula. Each element from the original
matrix is mapped to its new position through the formula rotated[c][rows-1-r] = matrix[r][c].
The animation displays each mapping step-by-step with arrows and color-coding,
making the pattern clear and the formula intuitive."
Rapid-Fire Version: Bullet points capturing key technical points:
- Shows 90-degree clockwise matrix rotation step-by-step
- Uses coordinate transformation: rotated[c][rows-1-r] = matrix[r][c]
- Color-codes elements to track movement through algorithm
- Displays transformation formulas in real-time
- Shows both original (3×3) and rotated (3×3) matrices side-by-side
- Time: O(n×m), Space: O(n×m), Non-mutating algorithm
Ultra-Minimal One-Liner: Single sentence capturing the essence:
"Step-by-step animation of matrix rotation showing coordinate mapping
from original to rotated position for each element."
Manim Fundamentals for Visualization
Core Imports
from manim import (
# Colors
BLUE, RED, GREEN, YELLOW, WHITE,
# Directions
UP, DOWN, LEFT, RIGHT, ORIGIN,
# Shapes
Rectangle, VGroup,
# Animations
Create, FadeIn, FadeOut, Write, Indicate,
# Special
Arrow, Text, Scene,
)
Common Patterns
Creating a grid of elements:
elements = VGroup()
for r in range(rows):
for c in range(cols):
element = Rectangle(width=0.6, height=0.6)
element.move_to(ORIGIN + RIGHT*(c*0.8) + DOWN*(r*0.8))
elements.add(element)
Animating transformations:
# Highlight source
self.play(Indicate(source, color=color, scale_factor=1.2))
# Show arrow with transformation
arrow = Arrow(source.get_center(), dest.get_center())
self.play(Create(arrow))
# Update destination and highlight
self.play(FadeIn(dest_text), Indicate(dest, color=color))
# Clean up temporary elements
self.play(FadeOut(arrow), FadeOut(temp_text))
Positioning elements:
# Position relative to screen edges
element.to_edge(LEFT) # Far left
element.to_edge(RIGHT) # Far right
element.to_edge(UP) # Top
element.to_edge(DOWN) # Bottom
# Position relative to other elements
element.next_to(other, DOWN, buff=0.3) # Below, with 0.3 unit gap
# Absolute positioning
element.move_to(ORIGIN + RIGHT*3 + DOWN*2)
Animating Elements from Left to Right
Core Pattern: Computing Text Positions
When displaying a sequence of elements (characters, tokens, array elements), position them clearly from left to right using a linear positioning formula:
# Define positioning parameters
start_x = -2.0 # Starting x position (left side)
element_width = 0.5 # Spacing between elements
element_height = 0.4 # Height of each element box
# Position each element from left to right
for i, element in enumerate(elements):
# Compute position: start_x + index * spacing
pos_x = start_x + i * element_width
pos_y = 0.5 # Fixed y position (or adjust per row)
# Apply position using RIGHT (positive x-offset for rightward movement)
element.move_to(ORIGIN + RIGHT * pos_x + UP * pos_y)
Why This Pattern?
| Aspect | Benefit |
|---|---|
| Linear formula | Easy to understand progression; predictable spacing |
| Named variables | start_x, element_width are self-documenting |
| RIGHT direction | Positive offsets intuitive for rightward movement |
| Index-based | O(1) computation per element; scales efficiently |
Complete Example: Character Sequence Display
class CharacterSequenceVisualization(Scene):
def construct(self):
input_string = "abacabad"
# === Position setup ===
char_width = 0.5
start_x = -2.0
char_box_y = 0.5
# === Create and position character boxes ===
char_boxes = {}
string_display = VGroup()
for i, ch in enumerate(input_string):
# Create box and text
box = Rectangle(width=0.4, height=0.4, color=BLUE)
char_text = Text(ch, font_size=12)
# Compute position: linear progression from left to right
pos_x = start_x + i * char_width
box.move_to(ORIGIN + RIGHT * pos_x + UP * char_box_y)
char_text.move_to(box.get_center())
# Store and group
string_display.add(box, char_text)
char_boxes[i] = (box, char_text)
# Animate: Display all at once or sequence from left to right
self.play(Create(string_display))
self.wait(0.5)
Animation Direction: Left-to-Right Sequencing
For interactive algorithms that process elements sequentially, animate from left to right:
# === Option 1: Animate all elements immediately ===
self.play(Create(string_display)) # All visible at once
# === Option 2: Sequence animations left to right ===
for i in range(len(input_string)):
self.play(Indicate(char_boxes[i][0], color=RED, scale_factor=1.3))
self.wait(0.2)
# === Option 3: Create with staggered animation ===
animations = []
for i, (box, text) in char_boxes.items():
# Create animation objects (not played yet)
animations.append(Create(box))
animations.append(Write(text))
# Play all animations with slight stagger
self.play(*animations, lag_ratio=0.1) # 0.1 = 10% stagger between elements
Positioning Grid: Rows and Columns
For 2D displays (like frequency tables below character sequences):
# Character boxes: single row, multiple columns
char_width = 0.5
char_start_x = -2.0
char_y = 0.5
for i, ch in enumerate(input_string):
pos_x = char_start_x + i * char_width
# All characters in same row
box.move_to(ORIGIN + RIGHT * pos_x + UP * char_y)
# Frequency table: left-aligned, stacked vertically below characters
freq_start_x = -2.0 # Align with char start
freq_start_y = -1.0 # Below characters
freq_row_height = 0.3
for idx, (ch, freq) in enumerate(freq_counter.items()):
pos_x = freq_start_x
pos_y = freq_start_y - idx * freq_row_height
freq_text.move_to(ORIGIN + RIGHT * pos_x + DOWN * pos_y)
Computing Offset for Centered Alignment
If you need to center a group of elements (e.g., center character sequence on screen):
# Given: N elements with total width
num_elements = 8
element_width = 0.5
total_width = (num_elements - 1) * element_width
# Compute start_x to center the sequence
center_x = 0 # Center of screen
start_x = center_x - (total_width / 2) # Shifts to center
for i in range(num_elements):
pos_x = start_x + i * element_width
element.move_to(ORIGIN + RIGHT * pos_x + UP * 0.5)
Best Practices: Left-to-Right Animation
| ✅ DO | ❌ DON'T |
|---|---|
Use linear formula: pos_x = start + i * width |
Hardcode individual positions |
Store start_x, element_width as variables |
Embed magic numbers in loops |
Use RIGHT * pos_x for positive offsets |
Use LEFT * negative_value (confusing) |
| Validate positions stay within bounds | Position elements off-canvas |
| Center sequences using symmetrical offsets | Assume elements fit without checking |
Animate sequentially with lag_ratio |
Create all animations without timing |
| Document positioning strategy in comments | Leave positioning logic unexplained |
| Test with different string lengths | Hardcode for single test case |
Common Pitfalls and Fixes
| ❌ Problem | ✅ Solution |
|---|---|
| Elements overlap | Increase element_width or decrease font size |
| Elements extend off-screen | Reduce element_width or use centered formula |
| Inconsistent spacing | Use pos_x = start_x + i * element_width consistently |
| Animation feels jerky | Use lag_ratio=0.1-0.2 for smooth stagger |
| Hard to adjust layout | Store positioning in variables; change once at top |
| Text not centered in box | Use text.move_to(box.get_center()) after box positioning |
| Multiple rows misaligned | Use same element_width for all rows |
Computing Summary Position: Vertical Stacking
Pattern: Positioning Multiple Sequential Messages
When displaying multiple text messages vertically (e.g., search result → summary → complexity info), precise positioning prevents overlapping and creates visual hierarchy.
Core Formula: Consistent Vertical Spacing
# Define vertical positions for stacked messages
base_y = 2.0 # Starting y position (top)
message_spacing = 0.4 # Gap between messages
# Position messages with consistent spacing
result_y = base_y
summary_y = base_y + message_spacing
complexity_y = base_y + (message_spacing * 2)
# Apply positions
result_text.move_to(ORIGIN + DOWN * result_y)
summary_text.move_to(ORIGIN + DOWN * summary_y)
complexity_text.move_to(ORIGIN + DOWN * complexity_y)
Real-World Example: Rotated Binary Search
# Scenario: Three text elements to display at end of animation
# 1. Match result (shown when target found)
# 2. Summary (iterations count)
# 3. Complexity (time/space analysis)
# === Position Calculation ===
match_text_y = 2.0 # First message (top)
summary_y = 2.2 # Second message (0.2 units below match)
complexity_y = 2.6 # Third message (0.4 units below summary)
# Vertical spacing hierarchy:
# match_text: y = -2.0 (top)
# ↓ (0.2 gap)
# summary: y = -2.2 (middle)
# ↓ (0.4 gap, larger for emphasis)
# complexity: y = -2.6 (bottom)
# === Implementation ===
if nums[mid] == target:
match_text = Text(f"Found! nums[{mid}] = {target}", font_size=14, color=RED)
match_text.move_to(ORIGIN + DOWN * 2.0) # Top position
self.play(Write(match_text))
# [animation continues...]
break
# After search loop completes:
summary = Text(f"Search completed in {iteration} iterations", font_size=14, color=GREEN)
summary.move_to(ORIGIN + DOWN * 2.2) # Middle position (0.2 below match)
self.play(Write(summary))
complexity = Text("Time: O(log n) Space: O(1)", font_size=12, color=GRAY)
complexity.move_to(ORIGIN + DOWN * 2.6) # Bottom position (0.4 below summary)
self.play(Write(complexity))
Position Computation Algorithm
Step 1: Identify reference points
# If you have a known reference element:
reference_element_y = 2.0 # e.g., match_text position
# If stacking from screen bottom:
canvas_bottom = -2.2
available_height = 0.6 # Space for 2-3 messages
Step 2: Calculate spacing
# Define number of messages and available space
num_messages = 3
total_gap_needed = 0.2 + 0.4 # e.g., 0.2 + 0.4 = 0.6 units
# Option A: Fixed gaps (most common)
gap_small = 0.2 # Between related messages (match → summary)
gap_large = 0.4 # For emphasis/separation (summary → complexity)
# Option B: Proportional gaps (auto-calculate)
total_available_height = 0.8 # Vertical space available
auto_gap = total_available_height / (num_messages - 1) # ~0.4 per gap
Step 3: Assign positions
message_y_values = {
"match": 2.0, # Base position
"summary": 2.0 + 0.2, # Base + small gap
"complexity": 2.0 + 0.2 + 0.4, # Base + small gap + large gap
}
# Or use loop for N messages:
positions = []
current_y = 2.0
gaps = [0.2, 0.4] # Gap before each subsequent message
for i, gap in enumerate(gaps):
current_y += gap
positions.append(current_y)
# Result: [2.2, 2.6]
Step 4: Validate within canvas bounds
canvas_bottom = -2.2
canvas_top = 2.2
for message_y in [2.0, 2.2, 2.6]:
if canvas_bottom <= message_y <= canvas_top:
print(f"✓ Position {message_y} is within bounds")
else:
print(f"✗ Position {message_y} is OUT OF BOUNDS - ADJUST!")
# Recalculate with different spacing or base_y
Best Practices: Summary Position Computation
| ✅ DO | ❌ DON'T |
|---|---|
| Use consistent gaps for visual hierarchy | Hardcode arbitrary y values |
| Define gaps as variables (gap_small, gap_large) | Mix different gap strategies |
| Validate positions within canvas bounds | Assume elements fit without checking |
| Account for different font sizes (larger text needs more space) | Ignore text height when spacing |
| Document positioning strategy in comments | Leave calculations unexplained |
| Test with boundary cases (3 vs 1 message) | Hardcode for single scenario |
| Use DOWN for positive gaps (intuitive) | Use confusing UP/LEFT/RIGHT combinations |
| Group related messages with smaller gaps | Treat all messages equally |
| Separate different categories with larger gaps | Use uniform spacing everywhere |
Common Positioning Scenarios
Scenario 1: Result + Summary + Complexity (3 messages)
# Most common: Show result, then summary, then technical info
base_y = 2.0
positions = {
"result": base_y + 0.0, # y = 2.0
"summary": base_y + 0.2, # y = 2.2 (0.2 gap)
"complexity": base_y + 0.6, # y = 2.6 (0.4 gap)
}
Scenario 2: Multiple Results (2 messages, no complexity)
# Simplified: Just result and summary
base_y = 2.0
positions = {
"result": base_y + 0.0, # y = 2.0
"summary": base_y + 0.3, # y = 2.3 (0.3 gap)
}
Scenario 3: Centered Stack (3 messages, centered vertically)
# Center the entire stack on screen
message_height_total = 0.2 + 0.4 # Total vertical extent
canvas_center_y = 0.0
base_y = canvas_center_y + (message_height_total / 2)
positions = {
"result": base_y + 0.0,
"summary": base_y + 0.2,
"complexity": base_y + 0.6,
}
Debugging Overlapping Text
If text appears to overlap:
-
Check current positions:
print(f"match_y: {match_text.get_y()}") # Actual position print(f"summary_y: {summary_text.get_y()}") print(f"complexity_y: {complexity_text.get_y()}") -
Verify gaps are sufficient:
gap = abs(summary_text.get_y() - match_text.get_y()) print(f"Gap between match and summary: {gap}") if gap < 0.2: print("⚠ Gap too small - increase gap or reduce font size") -
Adjust and re-test:
# Increase gaps if overlapping summary.move_to(ORIGIN + DOWN * 2.3) # Was 2.2, now 2.3 complexity.move_to(ORIGIN + DOWN * 2.7) # Was 2.6, now 2.7
Quality Checklist
Before finalizing a visualization, verify:
- Operation Overview clearly describes what's being visualized
- Visualization Strategy explains layout, colors, and flow
- Key Concepts explain WHY each design choice improves understanding
- Animation Flow sequences steps with appropriate timing
- Implementation Notes document technical decisions
- 30-Second Pitch is natural and conversational
- Rapid-Fire Version uses clear bullet points
- Ultra-Minimal One-Liner captures essence in one sentence
- Animations are paced appropriately (not too fast, not too slow)
- Color scheme provides good contrast and distinguishes elements
- Formulas/text are readable (appropriate font sizes)
- Scene layout utilizes screen space effectively
- Runner script properly executes the visualization
- All necessary Manim imports are included
- Position calculations avoid overlapping elements
File Structure Template
drills/visualizations/
├── operation_viz.py # Main Scene class
└── operation_viz_example.py (optional) # Runner script
operation_viz.py Template
"""
## Operation Overview
[Description of the operation]
[Concrete example]
Visualizes:
1. [Element 1]
2. [Element 2]
"""
from manim import (
# ... imports
)
class OperationVisualization(Scene):
"""
Visualization of [operation name].
[Detailed docstring describing what happens]
"""
def construct(self):
# Title and setup
# Element creation
# Animation loop
# Summary
operation_viz_example.py Template
#!/usr/bin/env python
"""
Runner script for operation_viz.py visualization.
This script renders the [operation] visualization using [formula/concept].
"""
import subprocess
import sys
from pathlib import Path
def run_visualization():
"""Run the visualization using manim."""
script_dir = Path(__file__).parent
viz_script = script_dir / "operation_viz.py"
media_dir = script_dir.parent.parent / "generated" / "media"
media_dir.mkdir(parents=True, exist_ok=True)
cmd = [
"manim",
"-qh", # high quality
"-p", # preview
"--media_dir", str(media_dir),
str(viz_script),
"OperationVisualization",
]
try:
result = subprocess.run(cmd, check=True, capture_output=False)
print(f"SUCCESS: Visualization rendered to {media_dir}")
return result.returncode
except subprocess.CalledProcessError as e:
print(f"ERROR: Failed rendering visualization: {e}")
return 1
except FileNotFoundError:
print("ERROR: Manim not found. Install with: pip install manim")
return 1
if __name__ == "__main__":
sys.exit(run_visualization())
Text Rendering & Cleanup Guide
Critical: Managing Dynamic Text
When displaying text that changes frequently (like state variables, counters, or labels), improper cleanup can cause:
- Text overlapping on screen (multiple versions visible simultaneously)
- Performance degradation (too many objects accumulating)
- Visual clutter obscuring the actual animation
Proper Text Lifecycle
❌ WRONG: Text accumulates
for step in range(5):
status_text = Text(f"Step {step}")
self.play(Write(status_text))
self.wait(1)
# Text is still on screen!
✅ CORRECT: Text is replaced
status_text = None
for step in range(5):
# Remove previous text before creating new one
if status_text is not None:
self.play(Uncreate(status_text))
status_text = Text(f"Step {step}")
self.play(Write(status_text))
self.wait(1)
Text Cleanup Patterns
Pattern 1: Replace with New Text
# Store reference to updatable text
status = Text("Initial", font_size=14)
status.move_to(ORIGIN + DOWN * 2.0)
self.play(Write(status))
for i in range(3):
# Remove old, add new
self.play(Uncreate(status))
status = Text(f"Step {i}", font_size=14)
status.move_to(ORIGIN + DOWN * 2.0)
self.play(Write(status))
self.wait(0.5)
Pattern 2: Fade Out Before New Content
status_text = Text("Loading...", font_size=14)
status_text.move_to(ORIGIN)
self.play(Write(status_text))
self.wait(1)
# Fade out instead of Uncreate for smooth transition
self.play(FadeOut(status_text))
new_text = Text("Complete!", font_size=14)
new_text.move_to(ORIGIN)
self.play(Write(new_text))
Pattern 3: Keep Persistent vs. Temporary Separation
# Persistent elements (keep on screen)
persistent = VGroup()
title = Text("Algorithm Demo", font_size=20)
persistent.add(title)
self.play(Create(persistent))
# Temporary elements (clean up after use)
temp_text = None
for step in range(3):
# Clean up previous temp text
if temp_text is not None:
self.play(Uncreate(temp_text))
# Create new temp text
temp_text = Text(f"Step {step}: Processing", font_size=12)
temp_text.next_to(title, DOWN, buff=0.5)
self.play(Write(temp_text))
self.wait(1)
# Final cleanup
self.play(Uncreate(temp_text))
State Display Management
Problem: Displaying multiple state variables that update each iteration
Solution: Update all state text in one atomic operation
# Setup initial state display (left panel)
state_labels = VGroup()
prev_label = Text("prev:", font_size=12, color=BLUE)
curr_label = Text("curr:", font_size=12, color=RED)
nxt_label = Text("nxt:", font_size=12, color=GREEN)
# Position and group
prev_label.move_to(LEFT * 3.5 + UP * 0.5)
curr_label.move_to(LEFT * 3.5)
nxt_label.move_to(LEFT * 3.5 + DOWN * 0.5)
state_labels.add(prev_label, curr_label, nxt_label)
self.play(Create(state_labels))
# For each iteration: remove old values, show new ones
state_values = None # Track the value text group
for i in range(3):
# Remove previous state values
if state_values is not None:
self.play(Uncreate(state_values))
# Create new state values
state_values = VGroup()
prev_val = Text(f"Node{i-1}" if i > 0 else "None", font_size=10, color=BLUE)
curr_val = Text(f"Node{i}", font_size=10, color=RED)
nxt_val = Text(f"Node{i+1}" if i < 2 else "None", font_size=10, color=GREEN)
prev_val.next_to(prev_label, RIGHT, buff=0.3)
curr_val.next_to(curr_label, RIGHT, buff=0.3)
nxt_val.next_to(nxt_label, RIGHT, buff=0.3)
state_values.add(prev_val, curr_val, nxt_val)
self.play(Write(state_values))
self.wait(1)
# Final cleanup
if state_values is not None:
self.play(Uncreate(state_values))
Best Practices for Text
| ✅ DO | ❌ DON'T |
|---|---|
| Create/Uncreate text in pairs | Leave temporary text on screen |
| Group related text for batch operations | Update individual pieces of text separately |
| Store references for cleanup | Lose track of what text is displayed |
| Use FadeOut for smooth transitions | Instantly remove text without animation |
| Clear state between iterations | Accumulate text from multiple loops |
| Position text before displaying | Move text after it's on screen |
| Keep text within canvas bounds | Let text extend beyond visible area |
Performance Optimization
# BEFORE: Inefficient (accumulates 100 text objects)
for i in range(100):
text = Text(f"Value: {i}")
text.move_to(ORIGIN)
self.play(Write(text))
self.wait(0.1)
# Text not removed = 100 objects in memory!
# AFTER: Efficient (keeps only 1 text object)
current_text = None
for i in range(100):
if current_text is not None:
self.play(Uncreate(current_text), run_time=0.05)
current_text = Text(f"Value: {i}")
current_text.move_to(ORIGIN)
self.play(Write(current_text), run_time=0.05)
self.wait(0.05)
# Clean up
if current_text is not None:
self.play(Uncreate(current_text))
Debugging Text Issues
If text appears to overlap or accumulate:
- Check for missing Uncreate/FadeOut calls before creating new text
- Verify loop cleanup – ensure final cleanup happens after loops
- Track text references – use variable names like
temp_textto remind yourself it needs cleanup - Use print statements to debug how many objects you're creating
- Test with small iterations first before running full animation
Critical: Understanding the Manim Canvas
Manim renders to a 2D canvas with default dimensions of 8 units wide × 4.5 units tall (in standard quality). Elements positioned outside these bounds will not render or will be clipped.
Default Canvas Bounds
- Horizontal: -4 to +4 (LEFT to RIGHT)
- Vertical: -2.25 to +2.25 (DOWN to UP)
- Total safe area: 8 units wide × 4.5 units tall
Safe Positioning Strategy
1. Divide the Canvas into Logical Regions
┌─────────────────────────────────────┐
│ TITLE/HEADER (UP) │ y ≈ +2.0
├──────┬───────────────┬──────────────┤
│LEFT │ CENTER │ RIGHT │ y ≈ +0.5 to -0.5
│PANEL │ CONTENT │ PANEL │
├──────┼───────────────┼──────────────┤
│ │ WORKING AREA │ │
│ │ (animations) │ │ y ≈ -1.0 to -2.0
├──────┴───────────────┴──────────────┤
│ FOOTER/STATUS (DOWN) │ y ≈ -2.2
└─────────────────────────────────────┘
2. Absolute Position Calculation
Never hardcode positions without accounting for canvas bounds:
# ❌ WRONG: Can go out of bounds
element.move_to(RIGHT * 5 + DOWN * 3) # Renders outside canvas
# ✅ CORRECT: Constrained to safe area
element.move_to(RIGHT * 3.5 + DOWN * 2) # Within bounds
3. Safe Offset System
Define clear offsets and validate they stay within bounds:
# Define boundaries
CANVAS_LEFT = -3.8
CANVAS_RIGHT = 3.8
CANVAS_TOP = 2.2
CANVAS_BOTTOM = -2.2
CENTER_X = 0
CENTER_Y = 0
# Position title (top center)
title = Text("Title")
title.move_to(ORIGIN + UP * 2.0) # y = 2.0 ✓ Within +2.2
# Position left panel
left_label = Text("Left")
left_label.move_to(ORIGIN + RIGHT * CANVAS_LEFT + UP * 1.0) # x = -3.8 ✓
# Position content area (centered horizontally, mid-screen vertically)
content = VGroup()
content.move_to(ORIGIN + DOWN * 0.5) # y = -0.5 ✓
Layout Patterns
Pattern 1: Single Center Content
# Single element in center
element.move_to(ORIGIN) # Perfectly centered
# Multiple stacked elements
title.move_to(ORIGIN + UP * 1.5)
content.move_to(ORIGIN)
footer.move_to(ORIGIN + DOWN * 1.8)
Pattern 2: Three-Column Layout
# Left, Center, Right columns
LEFT_X = -3.5
CENTER_X = 0
RIGHT_X = 3.5
left_content.move_to(RIGHT * LEFT_X)
center_content.move_to(RIGHT * CENTER_X)
right_content.move_to(RIGHT * RIGHT_X)
Pattern 3: Grid Layout
# NxM grid with proper spacing
spacing_x = 2.0 # Horizontal gap
spacing_y = 1.5 # Vertical gap
for row in range(rows):
for col in range(cols):
# Calculate position ensuring bounds: -3.8 to +3.8 horizontally
x = CENTER_X + (col - cols/2) * spacing_x
y = CENTER_Y - (row - rows/2) * spacing_y
# Validate bounds before positioning
if -3.8 <= x <= 3.8 and -2.2 <= y <= 2.2:
element.move_to(RIGHT * x + DOWN * y)
Common Position Pitfalls
| ❌ Problem | ✅ Solution |
|---|---|
| Large negative LEFT offset | Use to_edge(LEFT) instead; or keep offset > -3.8 |
| Large positive RIGHT offset | Use to_edge(RIGHT) instead; or keep offset < 3.8 |
| Elements stacked at same y | Use next_to(element, DOWN, buff=0.3) for automatic spacing |
| Overlapping elements | Calculate precise spacing; validate positions don't overlap |
| Text extends beyond edges | Use max_width parameter: Text("...", max_width=3.0) |
| Arrows point off-canvas | Ensure both start/end positions are within bounds |
Validation Checklist
Before rendering, verify:
- Title position: y ≤ 2.2 (not clipped by top)
- Content position: -2.2 ≤ y ≤ 2.0 (centered vertically)
- Footer position: y ≥ -2.2 (not clipped by bottom)
- Left elements: x ≥ -3.8 (not clipped by left edge)
- Right elements: x ≤ 3.8 (not clipped by right edge)
- All arrows: Both start AND end points within bounds
- All text: Doesn't exceed max_width or extend beyond positioned container
- VGroups: All sub-elements positioned before grouping
- Animated elements: Path stays within bounds during animation
- Temporary elements: Removed with FadeOut before accumulating too many
Safe Sizing Recommendations
Font sizes (relative to content):
- Title: 24-28pt (takes ~1.5 units width for 20-char text)
- Content: 14-18pt (takes ~0.8 units width for 20-char text)
- Labels: 11-14pt (takes ~0.6 units width for 20-char text)
Shape sizing:
- Circles/boxes: radius/width ≤ 0.4 (prevents overlap in grids)
- Spacing between elements: ≥ 0.3 units (visual clarity)
- Arrow buffers: ≥ 0.05 units (prevents ugly overlaps)
Testing Your Layout
Before finalizing:
# Visualize safe area boundaries (development only)
from manim import Line
def add_boundary_lines(self):
"""Add guides to visualize canvas bounds."""
h_line = Line(LEFT * 3.8 + ORIGIN, RIGHT * 3.8 + ORIGIN, color=GRAY)
v_line = Line(UP * 2.2 + ORIGIN, DOWN * 2.2 + ORIGIN, color=GRAY)
self.play(Create(h_line), Create(v_line))
# Use in construct():
# self.add_boundary_lines()
❌ Too fast animations: Viewers can't follow the logic
→ ✅ Add self.wait() between major steps; use slower animations for important transitions
❌ Poor color contrast: Elements blend together → ✅ Use distinct colors; test with colorblind-friendly palettes
❌ Cluttered layout: Too much happening simultaneously → ✅ Use FadeOut to clean up temporary elements; space items clearly
❌ Missing context: Formula shown without explanation → ✅ Display current step information; update formulas to show actual indices
❌ Unreadable text: Font size too small or positioned poorly → ✅ Use font_size >= 18 for text; position with next_to() or move_to()
❌ Arbitrary positioning: Elements scattered randomly → ✅ Use consistent spacing; align elements in grids or rows
Tags
#visualization #manim #animation #algorithm-explanation #educational #interview-prep #step-by-step #mathematical-animation #skill
Related Skills
Xlsx
Comprehensive spreadsheet creation, editing, and analysis with support for formulas, formatting, data analysis, and visualization. When Claude needs to work with spreadsheets (.xlsx, .xlsm, .csv, .tsv, etc) for: (1) Creating new spreadsheets with formulas and formatting, (2) Reading or analyzing data, (3) Modify existing spreadsheets while preserving formulas, (4) Data analysis and visualization in spreadsheets, or (5) Recalculating formulas
Clickhouse Io
ClickHouse database patterns, query optimization, analytics, and data engineering best practices for high-performance analytical workloads.
Clickhouse Io
ClickHouse database patterns, query optimization, analytics, and data engineering best practices for high-performance analytical workloads.
Analyzing Financial Statements
This skill calculates key financial ratios and metrics from financial statement data for investment analysis
Data Storytelling
Transform data into compelling narratives using visualization, context, and persuasive structure. Use when presenting analytics to stakeholders, creating data reports, or building executive presentations.
Kpi Dashboard Design
Design effective KPI dashboards with metrics selection, visualization best practices, and real-time monitoring patterns. Use when building business dashboards, selecting metrics, or designing data visualization layouts.
Dbt Transformation Patterns
Master dbt (data build tool) for analytics engineering with model organization, testing, documentation, and incremental strategies. Use when building data transformations, creating data models, or implementing analytics engineering best practices.
Sql Optimization Patterns
Master SQL query optimization, indexing strategies, and EXPLAIN analysis to dramatically improve database performance and eliminate slow queries. Use when debugging slow queries, designing database schemas, or optimizing application performance.
Clinical Decision Support
Generate professional clinical decision support (CDS) documents for pharmaceutical and clinical research settings, including patient cohort analyses (biomarker-stratified with outcomes) and treatment recommendation reports (evidence-based guidelines with decision algorithms). Supports GRADE evidence grading, statistical analysis (hazard ratios, survival curves, waterfall plots), biomarker integration, and regulatory compliance. Outputs publication-ready LaTeX/PDF format optimized for drug develo
Anndata
This skill should be used when working with annotated data matrices in Python, particularly for single-cell genomics analysis, managing experimental measurements with metadata, or handling large-scale biological datasets. Use when tasks involve AnnData objects, h5ad files, single-cell RNA-seq data, or integration with scanpy/scverse tools.
