Visualize Using Manim

by pluto-atom-4

documentdata

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:

  1. 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()}")
    
  2. 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")
    
  3. 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:

  1. Check for missing Uncreate/FadeOut calls before creating new text
  2. Verify loop cleanup – ensure final cleanup happens after loops
  3. Track text references – use variable names like temp_text to remind yourself it needs cleanup
  4. Use print statements to debug how many objects you're creating
  5. 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

data

Clickhouse Io

ClickHouse database patterns, query optimization, analytics, and data engineering best practices for high-performance analytical workloads.

datacli

Clickhouse Io

ClickHouse database patterns, query optimization, analytics, and data engineering best practices for high-performance analytical workloads.

datacli

Analyzing Financial Statements

This skill calculates key financial ratios and metrics from financial statement data for investment analysis

data

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.

data

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.

designdata

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.

testingdocumenttool

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.

designdata

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

developmentdocumentcli

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.

arttooldata

Skill Information

Category:Document
License:Proprietary. LICENSE.txt has complete terms
Last Updated:1/30/2026