How to Interact with Manim's Event Handler and Event Dispatcher System

Manim's event system uses a global EVENT_DISPATCHER singleton to route mouse and keyboard input to Mobject instances via registered EventListener objects that bind specific EventType triggers to Python callbacks.

The 3b1b/manim library implements a lightweight event-handling framework that enables interactive animations. To interact with Manim's event handler and event dispatcher system effectively, you must understand how the EventDispatcher singleton manages listener registration, how EventListener objects couple callbacks to geometric objects, and how the system filters events based on mouse position or key state.

Core Components of the Event System

EventType Enum

The EventType enumeration in manimlib/event_handler/event_type.py defines all supported interaction identifiers. These include mouse motion, button press and release, drag events, scroll events, and keyboard press and release events. Each variant represents a distinct phase of user input that the dispatcher can track and route.

EventListener Class

Located in manimlib/event_handler/event_listner.py, the EventListener class acts as a binding container. It stores a reference to a target Mobject, an EventType, and a user-provided callback with the signature Callable[[Mobject, dict], Any]. The class implements custom equality logic so that duplicate listeners can be detected and removed safely without affecting other registrations.

EventDispatcher Singleton

manimlib/event_handler/event_dispatcher.py contains the EventDispatcher class, which functions as the central runtime singleton. It maintains internal dictionaries mapping EventType to lists of EventListener objects, tracks the current mouse point, drag point, and pressed keys, and forwards incoming events to the appropriate callbacks. The dispatcher exposes Pythonic dunder methods—+= for adding listeners, -= for removing them, () for manual dispatch, and len() for counting active listeners—to simplify interaction.

How the Event Dispatcher Routes Input

Registration Process

When a Mobject needs to react to input, it instantiates an EventListener and registers it with the global dispatcher. In manimlib/event_handler/__init__.py, the library exposes EVENT_DISPATCHER as the canonical singleton instance. Registration occurs via EVENT_DISPATCHER += listener, which invokes the dispatcher’s add_listener method and appends the listener to the appropriate EventType bucket.

Dispatch Loop and Filtering

The renderer calls EVENT_DISPATCHER(event_type, **event_data) whenever a low-level GUI event occurs. The dispatcher updates its internal state—mouse coordinates, drag positions, or key maps—then determines which listeners to notify.

For drag events, the dispatcher filters listeners whose associated Mobject is under the current mouse position using is_point_touching. For standard mouse events, it iterates only over listeners whose Mobject contains the mouse point. For keyboard events, the dispatcher bypasses geometry checks entirely and broadcasts to all listeners registered for that EventType.

Propagation Control

A callback can control event propagation by returning False or any falsy value. When the dispatcher detects a falsy return, it halts further execution of subsequent listeners for that specific event, allowing higher-priority handlers to consume the interaction exclusively.

Utility Queries

The dispatcher provides synchronous state inspection methods useful inside callbacks:

  • get_mouse_point() returns the current mouse location in scene coordinates.
  • get_mouse_drag_point() returns the drag anchor point.
  • is_key_pressed(symbol) checks if a specific key code is currently held.

Practical Examples of Event Handling in Manim

Handling Mouse Clicks on a Mobject

The following pattern creates a clickable square that changes color when pressed. It demonstrates registration in __init__ and the callback signature expected by the dispatcher.

from manimlib import *
from manimlib.event_handler import EVENT_DISPATCHER
from manimlib.event_handler.event_type import EventType

class ClickableSquare(Square):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        listener = EventListener(
            self,
            EventType.MousePressEvent,
            self.on_click,
        )
        EVENT_DISPATCHER += listener

    def on_click(self, mob, event_data):
        mob.set_fill(RED, opacity=0.7)
        print(f"Clicked at {event_data['point']}")

Implementing Drag and Drop

Drag events require the dispatcher to verify that the mouse is over the target Mobject. The callback receives the new mouse position and updates the object’s location accordingly.

class DraggableCircle(Circle):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.drag_listener = EventListener(
            self,
            EventType.MouseDragEvent,
            self.on_drag,
        )
        EVENT_DISPATCHER += self.drag_listener

    def on_drag(self, mob, event_data):
        new_point = event_data["point"]
        mob.move_to(new_point)
        return False  # Consume the event exclusively

Global Keyboard Shortcuts

Keyboard events bypass geometry checks, allowing global hotkeys. Pass None as the Mobject argument when the listener does not relate to a specific shape.

class GlobalKeyHandler:
    def __init__(self):
        self.key_listener = EventListener(
            None,  # No associated Mobject

            EventType.KeyPressEvent,
            self.on_key_press,
        )
        EVENT_DISPATCHER += self.key_listener

    def on_key_press(self, _, event_data):
        if event_data["symbol"] == 32:  # Space bar

            self.toggle_pause()

    def toggle_pause(self):
        print("Pause toggled")

Querying Dispatcher State

Callbacks can inspect the global dispatcher to implement modifier-key behaviors or coordinate-aware logic.

def on_mouse_move(self, mob, event_data):
    mouse_pos = EVENT_DISPATCHER.get_mouse_point()
    shift_held = EVENT_DISPATCHER.is_key_pressed(16)  # 16 = Shift

    if shift_held:
        mob.set_fill(YELLOW, opacity=0.9)

Key Source Files

File Purpose
manimlib/event_handler/event_type.py Defines the EventType enum for mouse and keyboard events.
manimlib/event_handler/event_listner.py Implements the EventListener class that binds callbacks to Mobject instances.
manimlib/event_handler/event_dispatcher.py Contains the EventDispatcher singleton logic for routing and state management.
manimlib/event_handler/__init__.py Exposes the global EVENT_DISPATCHER instance used throughout the library.

Summary

  • EventType enumerates supported interactions like mouse presses, drags, and key events in manimlib/event_handler/event_type.py.
  • EventListener objects couple a Mobject, an EventType, and a callback; they are defined in manimlib/event_handler/event_listner.py.
  • EVENT_DISPATCHER is the global singleton that manages registration via += and -= operators, routes events to listeners, and tracks input state.
  • Mouse events filter by geometry (only listeners whose Mobject contains the mouse point receive the event), while keyboard events broadcast globally.
  • Callbacks receive the target Mobject and a dictionary of event data; returning False stops further propagation.

Frequently Asked Questions

How do I register an event listener in Manim?

Create an EventListener instance with your Mobject, the desired EventType, and a callback function, then add it to the global dispatcher using the += operator. For example: EVENT_DISPATCHER += EventListener(my_mob, EventType.MousePressEvent, my_callback).

What is the difference between EventListener and EventDispatcher?

EventListener is a data container that stores the relationship between a specific Mobject, an event type, and a callback function. EventDispatcher is the active singleton that maintains lists of listeners, receives raw input events, and invokes the appropriate callbacks based on geometry or event type.

How can I stop event propagation in Manim?

Return False (or any falsy value) from your event callback. When the EventDispatcher detects a falsy return value, it halts further execution of subsequent listeners for that specific event, preventing lower-priority handlers from receiving the interaction.

Where is the EVENT_DISPATCHER singleton defined?

The global EVENT_DISPATCHER instance is instantiated in manimlib/event_handler/__init__.py, where it is exposed as the canonical singleton used throughout the library for managing all event subscriptions and dispatches.

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 →