How to Animate Numerical Values Using the ValueTracker in Manim

To animate numerical values in Manim, instantiate a ValueTracker to store a hidden numeric state, connect it to a visual element via an updater function, and call self.play(tracker.animate.set_value(target)) to interpolate the value over time.

The ValueTracker class in the 3b1b/manim library provides a lightweight mechanism for driving animations with numerical data. Because it inherits from Mobject, it integrates seamlessly with Manim’s animation system while remaining invisible on screen, making it ideal for controlling positions, colors, or displayed numbers without managing complex keyframe logic manually.

What Is the ValueTracker?

ValueTracker is a specialized Mobject defined in manimlib/mobject/value_tracker.py that stores a numeric value inside its uniforms dictionary rather than rendering any geometry. This design allows the object to participate in the animation graph while acting purely as a data source.

The core implementation reveals how the value is stored and accessed:

class ValueTracker(Mobject):
    def init_uniforms(self):
        self.uniforms["value"] = np.array(listify(self.value), dtype=self.value_type)
    
    def get_value(self):
        result = self.uniforms["value"]
        return result[0] if len(result) == 1 else result
    
    def set_value(self, value):
        self.uniforms["value"][:] = value
        return self

Because set_value() returns self, you can chain it with .animate to create smooth interpolations. The stored value can be a scalar, a complex number, or a NumPy array, providing flexibility for multi-dimensional tracking.

Connecting ValueTracker to Visual Elements with Updaters

To make the tracked value visible, you attach an updater to a rendered mobject. Updaters are functions called every frame that read the current tracker value and update the visual element accordingly.

A typical pattern for moving an object based on a changing number looks like this:

tracker = ValueTracker(0)  # Start at position 0

dot = Dot()
dot.add_updater(lambda m: m.move_to(tracker.get_value() * RIGHT))
self.add(dot)

# Animate the tracker to move the dot

self.play(tracker.animate.set_value(3), run_time=2)

In this example, the dot remains stationary until self.play interpolates the tracker’s internal value from 0 to 3. Because the updater runs continuously, the dot slides smoothly along the x-axis without requiring explicit coordinate keyframes.

Displaying and Animating Numbers On Screen

While ValueTracker handles the hidden state, Manim provides DecimalNumber (defined in manimlib/mobject/numbers.py) to render typeset numbers that update in real time.

Rendering Numbers with DecimalNumber

DecimalNumber is a VMobject that displays a floating-point value. When combined with a ValueTracker updater, it creates a live counter:

tracker = ValueTracker(0)
number = DecimalNumber(0, num_decimal_places=1).to_edge(UP)
number.add_updater(lambda m: m.set_value(tracker.get_value()))
self.add(number)

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

Using ChangeDecimalToValue for Direct Animation

For cases where you want to animate a number without manually managing a ValueTracker, Manim offers the ChangingDecimal animation class and its subclasses in manimlib/animation/numbers.py. These animations interpolate a DecimalNumber directly by repeatedly calling a number_update_func.

The base ChangingDecimal class works as follows:

class ChangingDecimal(Animation):
    def __init__(self, decimal_mob, number_update_func, ...):
        self.number_update_func = number_update_func
        super().__init__(decimal_mob, ...)
    
    def interpolate_mobject(self, alpha):
        new_value = self.number_update_func(self.time_spanned_alpha(alpha))
        self.mobject.set_value(new_value)

Convenient subclasses include:

  • ChangeDecimalToValue: Linearly interpolates from the current number to a specified target value.
  • CountInFrom: Counts in from a starting value to the current number, useful for intro animations.

Example usage:

number = DecimalNumber(0).to_edge(UP)
self.add(number)

# Animate the number from its current value (0) to 5 over 3 seconds

self.play(ChangeDecimalToValue(number, target_number=5, run_time=3))

Advanced ValueTracker Variants

Manim provides specialized trackers for specific interpolation behaviors, all inheriting from the base ValueTracker class.

ExponentialValueTracker

ExponentialValueTracker stores the logarithm of the desired value rather than the value itself. When animated, this produces exponential growth or decay instead of linear interpolation. This is useful for zooming effects or scaling animations where perceptual speed should increase with size.

exp_tracker = ExponentialValueTracker(1)  # Stores log(1) = 0

dot = Dot().add_updater(lambda m: m.move_to(exp_tracker.get_value() * RIGHT))
self.add(dot)

# Animates from 1 to 4 exponentially

self.play(exp_tracker.animate.set_value(4), run_time=2)

ComplexValueTracker

ComplexValueTracker changes the underlying value_type to np.complex128, allowing you to track complex numbers. This is particularly useful for animating rotations in the complex plane or phasor diagrams where the real and imaginary components must evolve simultaneously.

All variants share the same uniform-based storage mechanism, ensuring they work interchangeably with standard updaters and animations.

Summary

  • ValueTracker is an invisible Mobject that stores numeric state in its uniforms dictionary, enabling it to work with Manim’s animation system.
  • Access the current value with get_value() and update it (for animation) with set_value(), which supports method chaining with .animate.
  • Connect trackers to visual elements using updaters—functions that read the tracker each frame and update geometry, positions, or colors.
  • Display numbers on screen with DecimalNumber, and animate them directly using ChangeDecimalToValue or CountInFrom from manimlib/animation/numbers.py.
  • Use ExponentialValueTracker for exponential interpolation and ComplexValueTracker for complex-valued animations.

Frequently Asked Questions

What is the difference between ValueTracker and DecimalNumber?

ValueTracker is an invisible data container that holds a number but renders nothing on screen, while DecimalNumber is a visual VMobject that displays a number as text. Typically, you use a ValueTracker to store the state and a DecimalNumber with an updater to display it, or you animate the DecimalNumber directly using ChangeDecimalToValue.

Can I animate multiple values simultaneously with ValueTracker?

Yes. Since ValueTracker can store a NumPy array instead of a scalar, you can track multiple dimensions at once. Alternatively, you can create multiple ValueTracker instances and reference them in a single updater function that combines their values to update a visual element’s position, color, or other attributes.

How do I access the current value during an animation?

Call tracker.get_value() inside an updater function or during scene construction. The method retrieves the value from the internal uniforms dictionary. If you need the value to drive another animation or logic, query it within the interpolate method of a custom animation or inside a there_and_back style animation loop.

When should I use ExponentialValueTracker instead of ValueTracker?

Use ExponentialValueTracker when you want the animation to speed up or slow down exponentially rather than linearly. Because it stores the logarithm of the value, interpolating from 1 to 4 takes the same time as from 4 to 16, making it ideal for zooming camera animations, scaling objects where perceptual size matters, or decay effects like radioactive half-life visualizations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →