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

> Learn to interact with Manim's event handler and event dispatcher. Route input using EVENT_DISPATCHER and EventListeners for custom animations.

- Repository: [Grant Sanderson/manim](https://github.com/3b1b/manim)
- Tags: how-to-guide
- Published: 2026-02-28

---

**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`](https://github.com/3b1b/manim/blob/main/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`](https://github.com/3b1b/manim/blob/main/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`](https://github.com/3b1b/manim/blob/main/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`](https://github.com/3b1b/manim/blob/main/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.

```python
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.

```python
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.

```python
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.

```python
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`](https://github.com/3b1b/manim/blob/main/manimlib/event_handler/event_type.py) | Defines the `EventType` enum for mouse and keyboard events. |
| [`manimlib/event_handler/event_listner.py`](https://github.com/3b1b/manim/blob/main/manimlib/event_handler/event_listner.py) | Implements the `EventListener` class that binds callbacks to `Mobject` instances. |
| [`manimlib/event_handler/event_dispatcher.py`](https://github.com/3b1b/manim/blob/main/manimlib/event_handler/event_dispatcher.py) | Contains the `EventDispatcher` singleton logic for routing and state management. |
| [`manimlib/event_handler/__init__.py`](https://github.com/3b1b/manim/blob/main/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`](https://github.com/3b1b/manim/blob/main/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`](https://github.com/3b1b/manim/blob/main/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`](https://github.com/3b1b/manim/blob/main/manimlib/event_handler/__init__.py), where it is exposed as the canonical singleton used throughout the library for managing all event subscriptions and dispatches.