# How to Use the Window Class for Interactive Playback and Development in Manim

> Learn how to use the Manim Window class for interactive playback and development. Pause, step, and inspect animations in real time for faster Manim development.

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

---

**The `Window` class in [`manimlib/window.py`](https://github.com/3b1b/manim/blob/main/manimlib/window.py) creates an interactive Pyglet window that renders Manim scenes in real-time, allowing you to pause, step through frames, and inspect animations during development.**

Manim's **Window class** serves as the bridge between your animation code and real-time visualization. Located in the [3b1b/manim](https://github.com/3b1b/manim) repository, this class inherits from `PygletWindow` and manages the OpenGL rendering loop while forwarding keyboard and mouse events to the active scene. Understanding how to leverage this class transforms Manim from a batch video generator into an interactive animation development environment.

## Understanding the Window Class Architecture

The Window class orchestrates three critical components: the **Scene** (animation logic), the **Renderer** (OpenGL drawing), and the **Pyglet event loop** (user input handling).

### Core Components and Responsibilities

In [`manimlib/window.py`](https://github.com/3b1b/manim/blob/main/manimlib/window.py), the `Window` class (starting at line 23) inherits from `PygletWindow` and implements the following key methods:

- **`__init__(self, scene, config)`** – Instantiates the `Renderer` with `Renderer(self.scene, config)`, sets up the OpenGL context, and registers event callbacks (lines 30-55).
- **`on_draw(self)`** – The core rendering loop called by Pyglet each frame; invokes `self.renderer.render_frame()` to draw the current scene state (lines 78-90).
- **`on_key_press(self, symbol, modifiers)`** – Maps keyboard input to scene actions: **Space** or **P** for pause/play, **Left/Right arrows** for frame stepping, **S** for screenshots, and **Q/Esc** for quitting (lines 100-120).

### How Window Connects Scene and Renderer

The Window class maintains references to both the **Scene** (`self.scene`) and **Renderer** (`self.renderer`). During each frame:

1. The Pyglet event loop triggers `on_draw()`.
2. `on_draw()` calls `self.renderer.render_frame()`, which processes the scene's mobjects and camera state.
3. If the animation is playing, `self.scene.update(dt)` advances the animation clock.
4. User input events are forwarded via `self.scene.handle_input(event)`, allowing real-time interaction with mobjects.

This architecture enables the **preview mode** (`-p` flag) to function as an interactive development tool rather than a passive video player.

## Launching Interactive Playback from the Command Line

The most common way to utilize the Window class is through Manim's command-line interface with the **preview flag**.

Run your scene with the `-p` flag to automatically instantiate a Window after rendering the first frame:

```bash

# Render MyScene and open an interactive preview window

manim -pql my_scene.py MyScene

```

**Flag breakdown:**
- **`-p`** – Triggers the preview mode; creates a `Window` instance and starts the Pyglet event loop via `pyglet.app.run()`.
- **`-ql`** – Quick low-quality rendering (fast feedback for development).

When the window appears, use these **keyboard shortcuts**:
- **Space** or **P** – Pause or resume playback.
- **Left/Right Arrow** – Step backward or forward one frame.
- **S** – Save a screenshot of the current frame.
- **Q** or **Esc** – Quit the application.

According to [`manimlib/command_line.py`](https://github.com/3b1b/manim/blob/main/manimlib/command_line.py) (around line 200), the CLI checks `config["preview"]` and conditionally executes `Window(scene, config)` followed by the Pyglet main loop.

## Programmatic Control of the Window Class

For advanced use cases, instantiate the `Window` class directly within Python to create custom interactive workflows.

This pattern is useful when embedding Manim into larger applications or building custom development tools:

```python

# my_preview.py

from manimlib.scene import Scene
from manimlib.window import Window
from manimlib import config

class Demo(Scene):
    def construct(self):
        circle = Circle()
        self.play(ShowCreation(circle))

if __name__ == "__main__":
    # Initialize the scene

    scene = Demo()
    scene.render()  # Generates the first frame

    
    # Manually create the interactive window

    win = Window(scene, config)  # config contains preview flags

    win.run()  # Starts the Pyglet event loop

```

**Key methods when using Window programmatically:**
- **`Window(scene, config)`** – Constructor that binds the scene and initializes the renderer.
- **`win.run()`** – Enters the Pyglet event loop (equivalent to `pyglet.app.run()`).

This approach allows you to intercept the window creation process and modify configuration parameters before the interactive session begins.

## Customizing Interactive Behavior

The `Window` class can be subclassed to add custom UI elements, shortcuts, or rendering behaviors specific to your animation workflow.

### Extending Window with Custom Shortcuts

Override Pyglet event callbacks to implement custom functionality while preserving default behavior:

```python
from manimlib.window import Window
from pyglet.window import key

class MyWindow(Window):
    def on_key_press(self, symbol, modifiers):
        # Always call super() first to keep default shortcuts (space, arrows, etc.)

        super().on_key_press(symbol, modifiers)
        
        # Add custom fullscreen toggle with 'F' key

        if symbol == key.F:
            self.set_fullscreen(not self.fullscreen)
            
        # Add custom reset view with 'R' key

        if symbol == key.R:
            self.scene.camera.frame.set_height(8)  # Reset camera zoom

            self.scene.camera.frame.center()         # Recenter camera

# Usage in your script

win = MyWindow(scene, config)
win.run()

```

**Available Pyglet callbacks to override:**
- **`on_mouse_drag(x, y, dx, dy, buttons, modifiers)`** – For custom camera controls or object manipulation.
- **`on_resize(width, height)`** – To adjust rendering resolution dynamically.
- **`on_close()`** – To add cleanup logic before the window closes.

When subclassing, ensure you call `super()` methods to maintain the core rendering loop and default keyboard shortcuts defined in the base `Window` class.

## Summary

- The **`Window` class** in [`manimlib/window.py`](https://github.com/3b1b/manim/blob/main/manimlib/window.py) provides real-time interactive playback by bridging Manim's Scene/Renderer with Pyglet's windowing system.
- **Command-line preview** (`manim -pql`) automatically instantiates a Window, enabling keyboard controls for pause/play, frame stepping, and screenshots.
- **Programmatic usage** allows manual Window creation via `Window(scene, config)` and `win.run()` for embedding in custom workflows.
- **Subclassing Window** lets you override Pyglet callbacks (`on_key_press`, `on_mouse_drag`) to add custom shortcuts while preserving default behavior with `super()` calls.

## Frequently Asked Questions

### What is the Window class in Manim?

The **Window class** is a Pyglet-based window implementation located in [`manimlib/window.py`](https://github.com/3b1b/manim/blob/main/manimlib/window.py) that enables real-time rendering and interaction with Manim animations. It inherits from `PygletWindow` and manages the OpenGL rendering loop, forwarding user input events to the active Scene while displaying the output of the Renderer.

### How do I enable interactive playback in Manim?

To enable interactive playback, run your scene with the **preview flag** (`-p` or `--preview`): `manim -pql my_scene.py MyScene`. This flag instructs the command-line driver to instantiate the `Window` class and start the Pyglet event loop after rendering the first frame, opening an interactive window where you can control playback with keyboard shortcuts.

### Can I customize keyboard shortcuts in the Manim Window?

Yes, you can customize keyboard shortcuts by **subclassing the Window class** and overriding the `on_key_press` method. Always call `super().on_key_press(symbol, modifiers)` first to preserve default shortcuts (space for pause, arrows for stepping, s for screenshots), then add your own logic for custom keys such as toggling fullscreen with the F key or resetting the camera view.

### How does the Window class handle rendering?

The Window class handles rendering through its **`on_draw`** method, which Pyglet calls every frame. This method invokes `self.renderer.render_frame()` to draw the scene's mobjects using OpenGL. The Renderer (instantiated in `Window.__init__`) manages the OpenGL context and VBOs, while the Window coordinates the timing and user input between the Pyglet event loop and the Scene's animation clock.