Files
calesthioandHanzo Dev 7ffb943132 Initial release — OpenMontage: the first open-source agentic video production system
11 production pipelines, 47 tools, 124 agent skills.
Supports cloud APIs (fal.ai, OpenAI, ElevenLabs, Suno, HeyGen, Runway) and
free local providers (diffusers, Piper TTS, WAN 2.1, Hunyuan, CogVideo).

Co-authored-by: Hanzo Dev <[email protected]>
2026-03-29 08:25:17 -07:00

5.2 KiB

name, description, metadata
name description metadata
updaters Updaters, ValueTracker, and dynamic animations
tags
updater, valuetracker, dynamic, always, add_updater

Updaters and Dynamic Animations

Updaters allow mobjects to automatically update based on other values or mobjects.

Basic Updaters

Add a function that runs every frame.

from manim import *

class UpdaterExample(Scene):
    def construct(self):
        dot = Dot()
        label = Text("Follow me").next_to(dot, UP)

        # Label always follows the dot
        label.add_updater(lambda m: m.next_to(dot, UP))

        self.add(dot, label)
        self.play(dot.animate.shift(RIGHT * 3), run_time=2)
        self.play(dot.animate.shift(DOWN * 2), run_time=2)

Updater Syntax

# Lambda function
mobject.add_updater(lambda m: m.move_to(target.get_center()))

# Named function
def follow_target(mob):
    mob.next_to(target, RIGHT)

mobject.add_updater(follow_target)

# With dt (delta time) parameter
def time_based_update(mob, dt):
    mob.rotate(dt * PI)  # Rotate based on time elapsed

mobject.add_updater(time_based_update)

ValueTracker

A mobject that holds a numeric value. Perfect for animating parameters.

class ValueTrackerExample(Scene):
    def construct(self):
        # Create tracker
        tracker = ValueTracker(0)

        # Create number display
        number = DecimalNumber(0, num_decimal_places=2)
        number.add_updater(lambda m: m.set_value(tracker.get_value()))

        # Create circle that grows with tracker
        circle = Circle()
        circle.add_updater(lambda m: m.set_width(tracker.get_value()))

        self.add(number, circle)

        # Animate the tracker
        self.play(tracker.animate.set_value(4), run_time=3)
        self.play(tracker.animate.set_value(1), run_time=2)

ValueTracker Operations

tracker = ValueTracker(5)

# Get and set value
current = tracker.get_value()
tracker.set_value(10)

# Increment
tracker.increment_value(2.5)

# Arithmetic operators (direct manipulation, no animation)
tracker += 1
tracker -= 2
tracker *= 3
tracker /= 2

# Animate changes
self.play(tracker.animate.set_value(100))
self.play(tracker.animate.increment_value(-50))

DecimalNumber with ValueTracker

Display a changing number:

class NumberDisplay(Scene):
    def construct(self):
        tracker = ValueTracker(0)

        number = DecimalNumber(
            0,
            num_decimal_places=2,
            include_sign=True,
            font_size=72
        )
        number.add_updater(lambda m: m.set_value(tracker.get_value()))
        number.add_updater(lambda m: m.move_to(ORIGIN))

        self.add(number)
        self.play(tracker.animate.set_value(100), run_time=3)

always_redraw

Recreate a mobject every frame based on current values.

class AlwaysRedrawExample(Scene):
    def construct(self):
        tracker = ValueTracker(1)

        # Line that always connects two points based on tracker
        line = always_redraw(
            lambda: Line(
                LEFT * 2,
                RIGHT * 2 * tracker.get_value()
            )
        )

        self.add(line)
        self.play(tracker.animate.set_value(2), run_time=2)
        self.play(tracker.animate.set_value(0.5), run_time=2)

Common Updater Patterns

Following Another Mobject

follower.add_updater(lambda m: m.move_to(leader.get_center()))
follower.add_updater(lambda m: m.next_to(leader, RIGHT))

Pointing at Another Mobject

arrow = Arrow(ORIGIN, RIGHT)
arrow.add_updater(lambda m: m.put_start_and_end_on(
    start.get_center(),
    end.get_center()
))

Rotating Continuously

mobject.add_updater(lambda m, dt: m.rotate(dt * PI))

Matching Properties

# Match color
follower.add_updater(lambda m: m.set_color(leader.get_color()))

# Match position with offset
follower.add_updater(lambda m: m.move_to(leader.get_center() + UP))

Removing Updaters

# Remove specific updater
mobject.remove_updater(updater_function)

# Remove all updaters
mobject.clear_updaters()

# Suspend temporarily
mobject.suspend_updating()
mobject.resume_updating()

Updaters with Animations

Updaters continue running during animations:

class UpdaterDuringAnimation(Scene):
    def construct(self):
        dot = Dot()
        trail = TracedPath(dot.get_center, stroke_color=YELLOW)

        self.add(dot, trail)
        self.play(dot.animate.shift(RIGHT * 3 + UP * 2), run_time=3)

TracedPath

Built-in updater for drawing paths:

class TracedPathExample(Scene):
    def construct(self):
        dot = Dot()
        path = TracedPath(dot.get_center, stroke_width=2, stroke_color=BLUE)

        self.add(dot, path)
        self.play(
            dot.animate.shift(RIGHT * 2),
            dot.animate.shift(UP * 2),
            run_time=3
        )

Best Practices

  1. Use ValueTracker for animated parameters - Clean and controllable
  2. Use always_redraw for complex shapes - When updaters get complicated
  3. Clear updaters when done - Prevent performance issues
  4. Keep updater functions simple - Complex logic can slow rendering
  5. Use dt for time-based animations - Frame-rate independent