CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-manimgl

Animation engine for explanatory math videos with programmatic mathematical visualization capabilities

Pending
Overview
Eval results
Files

value-tracking.mddocs/

Value Tracking

ManimGL's value tracking system provides powerful tools for managing numeric parameters that change over time during animations. Value trackers are invisible mobjects that store numeric data and integrate seamlessly with the animation system, enabling complex parameter-dependent animations and interactive controls.

Capabilities

Basic Value Tracking

Store and animate numeric values that can drive parameter-dependent animations and link multiple objects to shared data sources.

class ValueTracker(Mobject):
    value_type: type = np.float64
    
    def __init__(self, value: float | complex | np.ndarray = 0, **kwargs):
        """
        Initialize a value tracker with a numeric value.
        
        Parameters:
        - value: Initial numeric value (scalar, complex, or array)
        - kwargs: Additional Mobject keyword arguments
        """
    
    def get_value(self) -> float | complex | np.ndarray:
        """
        Retrieve the current tracked value.
        
        Returns:
        Current value (scalar if single value, array if multiple values)
        """
    
    def set_value(self, value: float | complex | np.ndarray) -> Self:
        """
        Update the tracked value.
        
        Parameters:
        - value: New numeric value to store
        
        Returns:
        Self for method chaining
        """
    
    def increment_value(self, d_value: float | complex) -> None:
        """
        Add the specified amount to the current value.
        
        Parameters:
        - d_value: Amount to add to current value
        """
    
    def init_uniforms(self) -> None:
        """Initialize internal uniform data structure for GPU integration."""

Exponential Value Tracking

Specialized tracker for exponential interpolation, storing values in logarithmic space for natural exponential animation behavior.

class ExponentialValueTracker(ValueTracker):
    def __init__(self, value: float | complex = 1, **kwargs):
        """
        Initialize tracker for exponential value changes.
        
        Parameters:
        - value: Initial value (stored as log internally)
        - kwargs: Additional ValueTracker arguments
        """
    
    def get_value(self) -> float | complex:
        """
        Get the exponential of the stored value.
        
        Returns:
        np.exp(internal_value) - the actual exponential value
        """
    
    def set_value(self, value: float | complex):
        """
        Set value by storing its logarithm internally.
        
        Parameters:
        - value: Actual value to represent (stored as log)
        """

Complex Number Tracking

Optimized tracker for complex number values with high-precision complex number operations.

class ComplexValueTracker(ValueTracker):
    value_type: type = np.complex128
    
    def __init__(self, value: complex = 0+0j, **kwargs):
        """
        Initialize tracker for complex number values.
        
        Parameters:
        - value: Initial complex number value
        - kwargs: Additional ValueTracker arguments
        """

Usage Examples

Basic Parameter Animation

from manimlib import *

class BasicValueTracking(Scene):
    def construct(self):
        # Create a value tracker for controlling opacity
        opacity_tracker = ValueTracker(0)
        
        # Create objects that respond to the tracker
        circle = Circle(radius=2, color=BLUE, fill_opacity=1)
        square = Square(side_length=3, color=RED, fill_opacity=1)
        
        # Link objects to the tracker value
        circle.add_updater(
            lambda c: c.set_fill_opacity(opacity_tracker.get_value())
        )
        square.add_updater(
            lambda s: s.set_fill_opacity(1 - opacity_tracker.get_value())
        )
        
        self.add(circle, square)
        
        # Animate the tracked value
        self.play(opacity_tracker.animate.set_value(1), run_time=3)
        self.play(opacity_tracker.animate.set_value(0.5), run_time=2)
        self.play(opacity_tracker.animate.set_value(0), run_time=3)

Function Plotting with Value Tracking

class DynamicFunctionPlot(Scene):
    def construct(self):
        # Create coordinate system
        axes = Axes(x_range=[-3, 3], y_range=[-2, 2])
        self.add(axes)
        
        # Create value trackers for function parameters
        amplitude = ValueTracker(1)
        frequency = ValueTracker(1)
        phase = ValueTracker(0)
        
        # Create function that depends on trackers
        def get_sine_graph():
            return axes.plot(
                lambda x: (
                    amplitude.get_value() * 
                    np.sin(frequency.get_value() * x + phase.get_value())
                ),
                color=YELLOW
            )
        
        # Create initial graph
        graph = get_sine_graph()
        
        # Add updater to redraw graph when parameters change
        graph.add_updater(lambda g: g.become(get_sine_graph()))
        
        self.add(graph)
        
        # Animate parameters
        self.play(amplitude.animate.set_value(1.5), run_time=2)
        self.play(frequency.animate.set_value(2), run_time=2)
        self.play(phase.animate.set_value(PI/2), run_time=2)
        
        # Multiple parameters simultaneously
        self.play(
            amplitude.animate.set_value(0.5),
            frequency.animate.set_value(3),
            phase.animate.set_value(0),
            run_time=3
        )

Exponential Growth Animation

class ExponentialAnimation(Scene):
    def construct(self):
        # Create exponential value tracker for smooth exponential growth
        scale_tracker = ExponentialValueTracker(1)
        
        # Create object that will grow exponentially
        circle = Circle(radius=0.5, color=BLUE, fill_opacity=0.7)
        
        # Link circle size to exponential tracker
        def update_circle(c):
            scale_factor = scale_tracker.get_value()
            c.set_width(scale_factor)
            c.set_height(scale_factor)
            # Color intensity based on size
            opacity = min(1, scale_factor / 5)
            c.set_fill_opacity(opacity)
        
        circle.add_updater(update_circle)
        self.add(circle)
        
        # Animate exponential growth (linear interpolation in log space)
        self.play(scale_tracker.animate.set_value(10), run_time=4)
        self.wait()
        
        # Exponential decay
        self.play(scale_tracker.animate.set_value(0.1), run_time=3)

Complex Parameter Control

class ComplexValueDemo(Scene):
    def construct(self):
        # Use complex tracker for rotation and scaling combined
        complex_tracker = ComplexValueTracker(1+0j)
        
        # Create shape to transform
        arrow = Arrow(ORIGIN, RIGHT, color=RED, buff=0)
        
        # Transform arrow based on complex number
        def update_arrow(a):
            complex_val = complex_tracker.get_value()
            # Complex multiplication = rotation + scaling
            magnitude = abs(complex_val)
            angle = np.angle(complex_val)
            
            a.become(Arrow(ORIGIN, RIGHT, buff=0, color=RED))
            a.scale(magnitude)
            a.rotate(angle)
        
        arrow.add_updater(update_arrow)
        self.add(arrow)
        
        # Animate in complex plane
        self.play(
            complex_tracker.animate.set_value(2 * np.exp(1j * PI/4)),
            run_time=2
        )
        self.play(
            complex_tracker.animate.set_value(3 * np.exp(1j * PI)),
            run_time=2
        )
        self.play(
            complex_tracker.animate.set_value(0.5 * np.exp(1j * 2*PI)),
            run_time=3
        )

Multi-Parameter Coordination

class CoordinatedParameters(Scene):
    def construct(self):
        # Create multiple coordinated trackers
        x_pos = ValueTracker(-3)
        y_pos = ValueTracker(0)
        size = ValueTracker(0.5)
        hue = ValueTracker(0)
        
        # Create object that depends on all parameters
        circle = Circle(radius=0.5, fill_opacity=0.8)
        
        def update_circle(c):
            # Position
            c.move_to([x_pos.get_value(), y_pos.get_value(), 0])
            
            # Size
            c.set_width(2 * size.get_value())
            c.set_height(2 * size.get_value())
            
            # Color based on hue value
            import colorsys
            rgb = colorsys.hsv_to_rgb(hue.get_value(), 1, 1)
            c.set_fill(rgb_to_color(rgb))
        
        circle.add_updater(update_circle)
        self.add(circle)
        
        # Coordinate multiple parameter changes
        self.play(
            x_pos.animate.set_value(3),
            y_pos.animate.set_value(2),
            size.animate.set_value(1.5),
            hue.animate.set_value(0.3),
            run_time=4
        )
        
        # Create circular motion with changing properties
        self.play(
            x_pos.animate.set_value(-3),
            y_pos.animate.set_value(-2),
            size.animate.set_value(0.3),
            hue.animate.set_value(0.8),
            run_time=3
        )

Interactive Parameter Control

from manimlib.mobject.interactive import LinearNumberSlider

class InteractiveValueTracking(Scene):
    def setup(self):
        # Create sliders that are themselves value trackers
        self.amplitude_slider = LinearNumberSlider(
            value=1, min_value=0, max_value=3, step=0.1
        )
        self.frequency_slider = LinearNumberSlider(
            value=1, min_value=0.1, max_value=5, step=0.1
        )
        
        # Position sliders
        self.amplitude_slider.to_edge(DOWN).shift(UP * 0.5)
        self.frequency_slider.to_edge(DOWN)
        
        self.add(self.amplitude_slider, self.frequency_slider)
    
    def construct(self):
        # Create responsive visualization
        axes = Axes(x_range=[-3, 3], y_range=[-3, 3])
        
        def get_wave():
            return axes.plot(
                lambda x: (
                    self.amplitude_slider.get_value() * 
                    np.sin(self.frequency_slider.get_value() * x)
                ),
                color=YELLOW
            )
        
        wave = get_wave()
        wave.add_updater(lambda w: w.become(get_wave()))
        
        self.add(axes, wave)
        
        # Create labels that show current values
        amplitude_label = Text("Amplitude: 1.0", font_size=24)
        frequency_label = Text("Frequency: 1.0", font_size=24)
        
        amplitude_label.to_edge(UP).shift(DOWN * 0.5)
        frequency_label.to_edge(UP)
        
        def update_amp_label(label):
            val = self.amplitude_slider.get_value()
            label.become(Text(f"Amplitude: {val:.1f}", font_size=24))
            label.to_edge(UP).shift(DOWN * 0.5)
        
        def update_freq_label(label):
            val = self.frequency_slider.get_value()
            label.become(Text(f"Frequency: {val:.1f}", font_size=24))
            label.to_edge(UP)
        
        amplitude_label.add_updater(update_amp_label)
        frequency_label.add_updater(update_freq_label)
        
        self.add(amplitude_label, frequency_label)
        
        # Interactive exploration time
        self.wait(10)

Integration with Animation System

Chaining Operations

# Value trackers support method chaining
tracker = ValueTracker(0)
tracker.set_value(5).increment_value(2)  # Final value: 7

# Animation chaining
self.play(
    tracker.animate.set_value(10),
    other_object.animate.shift(UP)
)

Updater Patterns

# Time-based updaters (receive dt parameter)
def time_updater(mob, dt):
    mob.rotate(dt * tracker.get_value())

mob.add_updater(time_updater)

# Non-time-based updaters (called every frame)
def position_updater(mob):
    mob.move_to([tracker.get_value(), 0, 0])

mob.add_updater(position_updater, call_updater=True)

Advanced Animation Patterns

# Multiple trackers with different rate functions
self.play(
    tracker1.animate.set_value(5).set_rate_func(smooth),
    tracker2.animate.set_value(10).set_rate_func(linear),
    tracker3.animate.set_value(2).set_rate_func(there_and_back),
    run_time=3
)

# Staggered animations
self.play(
    AnimationGroup(
        tracker1.animate.set_value(5),
        tracker2.animate.set_value(3),
        tracker3.animate.set_value(8),
        lag_ratio=0.3
    ),
    run_time=4
)

Technical Implementation

Data Storage Architecture

Value trackers store data in self.uniforms["value"] as numpy arrays, enabling:

  • GPU shader integration for advanced rendering
  • Consistent handling of scalar and array values
  • Efficient memory management and updates

Animation Integration

  • Inherits full animation capabilities from Mobject base class
  • Compatible with .animate syntax for smooth transitions
  • Supports all interpolation and transformation methods
  • Integrates with rate functions and animation timing

Performance Considerations

  • Value trackers are lightweight and efficient for real-time updates
  • Updater functions are called every frame - keep them optimized
  • Use clear_updaters() when no longer needed to prevent memory leaks
  • For complex calculations, consider caching results when tracker values haven't changed

The value tracking system provides the foundation for sophisticated parameter-dependent animations in ManimGL, enabling smooth interpolation of any numeric parameter while maintaining full integration with the animation and rendering pipeline.

Install with Tessl CLI

npx tessl i tessl/pypi-manimgl

docs

3d-objects.md

advanced-animations.md

animation-system.md

boolean-operations.md

coordinate-systems.md

index.md

interactive-controls.md

mathematical-objects.md

matrix-visualization.md

probability-stats.md

scene-framework.md

text-and-latex.md

utilities-and-constants.md

value-tracking.md

vector-fields.md

tile.json