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, anEventType, and a callback; they are defined inmanimlib/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
Mobjectcontains the mouse point receive the event), while keyboard events broadcast globally. - Callbacks receive the target
Mobjectand a dictionary of event data; returningFalsestops 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →