0
# Interactive Controls
1
2
ManimGL provides a comprehensive set of interactive controls that enable real-time user interaction with animations. These controls allow for dynamic parameter adjustment, UI widgets, and interactive scene manipulation, making ManimGL ideal for exploratory mathematical visualization and educational content.
3
4
## Capabilities
5
6
### Motion and Dragging
7
8
Enable any mobject to be draggable with mouse interactions, providing immediate visual feedback and manipulation.
9
10
```python { .api }
11
class MotionMobject(Mobject):
12
def __init__(self, mobject: Mobject, **kwargs):
13
"""
14
Make any mobject draggable with mouse interactions.
15
16
Parameters:
17
- mobject: Any Mobject to make draggable
18
"""
19
20
def mob_on_mouse_drag(self, mob: Mobject, event_data: dict[str, np.ndarray]) -> bool:
21
"""Handle mouse drag events for the wrapped mobject."""
22
```
23
24
### Button Controls
25
26
Convert any mobject into clickable buttons with custom callback functionality for interactive triggers.
27
28
```python { .api }
29
class Button(Mobject):
30
def __init__(self, mobject: Mobject, on_click: Callable[[Mobject]], **kwargs):
31
"""
32
Create a clickable button from any mobject.
33
34
Parameters:
35
- mobject: Any Mobject to make clickable
36
- on_click: Callback function that receives the mobject as argument
37
"""
38
39
def mob_on_mouse_press(self, mob: Mobject, event_data) -> bool:
40
"""Handle mouse press events and execute callback."""
41
```
42
43
### Toggle Controls
44
45
Boolean controls with visual feedback for binary state management and switching between options.
46
47
```python { .api }
48
class EnableDisableButton(ControlMobject):
49
def __init__(
50
self,
51
value: bool = True,
52
value_type: np.dtype = np.dtype(bool),
53
rect_kwargs: dict = {"width": 0.5, "height": 0.5, "fill_opacity": 1.0},
54
enable_color: ManimColor = GREEN,
55
disable_color: ManimColor = RED,
56
**kwargs
57
):
58
"""
59
Toggle button with color feedback for boolean values.
60
61
Parameters:
62
- value: Initial boolean state (default: True)
63
- rect_kwargs: Rectangle styling dictionary
64
- enable_color: Color when enabled (default: GREEN)
65
- disable_color: Color when disabled (default: RED)
66
"""
67
68
def toggle_value(self) -> None:
69
"""Toggle the button state between True and False."""
70
71
class Checkbox(ControlMobject):
72
def __init__(
73
self,
74
value: bool = True,
75
value_type: np.dtype = np.dtype(bool),
76
rect_kwargs: dict = {"width": 0.5, "height": 0.5, "fill_opacity": 0.0},
77
checkmark_kwargs: dict = {"stroke_color": GREEN, "stroke_width": 6},
78
cross_kwargs: dict = {"stroke_color": RED, "stroke_width": 6},
79
box_content_buff: float = SMALL_BUFF,
80
**kwargs
81
):
82
"""
83
Checkbox control with checkmark/cross visual indicators.
84
85
Parameters:
86
- value: Initial boolean state
87
- rect_kwargs: Checkbox box styling
88
- checkmark_kwargs: Checkmark appearance when True
89
- cross_kwargs: Cross appearance when False
90
- box_content_buff: Buffer around checkbox content
91
"""
92
93
def toggle_value(self) -> None:
94
"""Toggle between checked and unchecked states."""
95
96
def get_checkmark(self) -> VGroup:
97
"""Create checkmark symbol for True state."""
98
99
def get_cross(self) -> VGroup:
100
"""Create cross symbol for False state."""
101
```
102
103
### Numeric Input Controls
104
105
Slider controls for selecting numeric values within specified ranges with visual feedback and step control.
106
107
```python { .api }
108
class LinearNumberSlider(ControlMobject):
109
def __init__(
110
self,
111
value: float = 0,
112
value_type: type = np.float64,
113
min_value: float = -10.0,
114
max_value: float = 10.0,
115
step: float = 1.0,
116
rounded_rect_kwargs: dict = {
117
"height": 0.075, "width": 2, "corner_radius": 0.0375
118
},
119
circle_kwargs: dict = {
120
"radius": 0.1, "stroke_color": GREY_A,
121
"fill_color": GREY_A, "fill_opacity": 1.0
122
},
123
**kwargs
124
):
125
"""
126
Horizontal slider for numeric value selection.
127
128
Parameters:
129
- value: Initial numeric value
130
- value_type: Data type for the value
131
- min_value: Minimum allowed value
132
- max_value: Maximum allowed value
133
- step: Step size for value increments
134
- rounded_rect_kwargs: Slider bar appearance
135
- circle_kwargs: Slider handle appearance
136
"""
137
138
def slider_on_mouse_drag(self, mob, event_data: dict[str, np.ndarray]) -> bool:
139
"""Handle mouse drag events for slider interaction."""
140
141
def get_value_from_point(self, point: np.ndarray) -> float:
142
"""Convert screen position to slider value."""
143
```
144
145
### Color Selection
146
147
Advanced color picker with separate controls for red, green, blue, and alpha channels with live preview.
148
149
```python { .api }
150
class ColorSliders(ControlMobject):
151
def __init__(
152
self,
153
sliders_kwargs: dict = {},
154
rect_kwargs: dict = {"width": 2.0, "height": 0.5, "stroke_opacity": 1.0},
155
background_grid_kwargs: dict = {
156
"colors": [GREY_A, GREY_C], "single_square_len": 0.1
157
},
158
sliders_buff: float = MED_LARGE_BUFF,
159
default_rgb_value: int = 255,
160
default_a_value: int = 1,
161
**kwargs
162
):
163
"""
164
RGBA color picker with separate channel sliders.
165
166
Parameters:
167
- sliders_kwargs: Common settings for all sliders
168
- rect_kwargs: Color preview box styling
169
- background_grid_kwargs: Checkerboard background settings
170
- sliders_buff: Vertical spacing between sliders
171
- default_rgb_value: Initial RGB channel values (0-255)
172
- default_a_value: Initial alpha value (0-1)
173
"""
174
175
def set_value(self, r: float, g: float, b: float, a: float):
176
"""Set RGBA values directly."""
177
178
def get_value(self) -> np.ndarray:
179
"""Get current RGBA values as array."""
180
181
def get_picked_color(self) -> str:
182
"""Get current color as hex string."""
183
184
def get_picked_opacity(self) -> float:
185
"""Get current alpha value."""
186
187
def get_background(self) -> VGroup:
188
"""Create checkerboard background for transparency preview."""
189
```
190
191
### Text Input
192
193
Interactive text input fields with keyboard support and focus management for dynamic text content.
194
195
```python { .api }
196
class Textbox(ControlMobject):
197
def __init__(
198
self,
199
value: str = "",
200
value_type: np.dtype = np.dtype(object),
201
box_kwargs: dict = {
202
"width": 2.0, "height": 1.0,
203
"fill_color": WHITE, "fill_opacity": 1.0
204
},
205
text_kwargs: dict = {"color": BLUE},
206
text_buff: float = MED_SMALL_BUFF,
207
isInitiallyActive: bool = False,
208
active_color: ManimColor = BLUE,
209
deactive_color: ManimColor = RED,
210
**kwargs
211
):
212
"""
213
Interactive text input field with keyboard support.
214
215
Parameters:
216
- value: Initial text content
217
- box_kwargs: Text box appearance
218
- text_kwargs: Text styling
219
- text_buff: Padding inside text box
220
- isInitiallyActive: Whether textbox starts focused
221
- active_color: Border color when active
222
- deactive_color: Border color when inactive
223
"""
224
225
def update_text(self, value: str) -> None:
226
"""Update the displayed text content."""
227
228
def active_anim(self, isActive: bool) -> None:
229
"""Animate focus state changes."""
230
231
def box_on_mouse_press(self, mob, event_data) -> bool:
232
"""Handle mouse clicks to focus/unfocus."""
233
234
def on_key_press(self, mob: Mobject, event_data: dict[str, int]) -> bool | None:
235
"""Handle keyboard input events."""
236
```
237
238
### Control Organization
239
240
Container system for organizing and managing multiple interactive controls with collapsible panels.
241
242
```python { .api }
243
class ControlPanel(Group):
244
def __init__(
245
self,
246
*controls: ControlMobject,
247
panel_kwargs: dict = {
248
"width": FRAME_WIDTH/4, "height": MED_SMALL_BUFF + FRAME_HEIGHT,
249
"fill_color": GREY_C, "fill_opacity": 1.0, "stroke_width": 0.0
250
},
251
opener_kwargs: dict = {
252
"width": FRAME_WIDTH/8, "height": 0.5,
253
"fill_color": GREY_C, "fill_opacity": 1.0
254
},
255
opener_text_kwargs: dict = {"text": "Control Panel", "font_size": 20},
256
**kwargs
257
):
258
"""
259
Collapsible panel container for organizing controls.
260
261
Parameters:
262
- controls: Variable number of ControlMobject instances
263
- panel_kwargs: Panel background styling
264
- opener_kwargs: Panel handle styling
265
- opener_text_kwargs: Handle text appearance
266
"""
267
268
def add_controls(self, *new_controls: ControlMobject) -> None:
269
"""Add new controls to the panel."""
270
271
def remove_controls(self, *controls_to_remove: ControlMobject) -> None:
272
"""Remove controls from the panel."""
273
274
def open_panel(self):
275
"""Expand the control panel."""
276
277
def close_panel(self):
278
"""Collapse the control panel."""
279
280
def panel_opener_on_mouse_drag(self, mob, event_data: dict[str, np.ndarray]) -> bool:
281
"""Handle panel dragging for repositioning."""
282
283
def panel_on_mouse_scroll(self, mob, event_data: dict[str, np.ndarray]) -> bool:
284
"""Handle mouse scroll for content navigation."""
285
```
286
287
### Base Control Framework
288
289
Foundation classes for creating custom interactive controls with value tracking and animation integration.
290
291
```python { .api }
292
class ControlMobject(ValueTracker):
293
def __init__(self, value: float, *mobjects: Mobject, **kwargs):
294
"""
295
Abstract base class for interactive controls.
296
297
Parameters:
298
- value: Initial value for the control
299
- mobjects: Mobjects to include in the control
300
"""
301
302
def set_value(self, value: float):
303
"""Set the control's value programmatically."""
304
305
def assert_value(self, value):
306
"""Abstract method for value validation."""
307
308
def set_value_anim(self, value):
309
"""Abstract method for animated value updates."""
310
```
311
312
## Usage Examples
313
314
### Basic Interactive Animation
315
316
```python
317
from manimlib import *
318
319
class InteractiveDemo(Scene):
320
def setup(self):
321
# Create controls
322
self.slider = LinearNumberSlider(
323
value=1.0, min_value=0.5, max_value=3.0, step=0.1
324
)
325
self.color_picker = ColorSliders()
326
self.checkbox = Checkbox(value=True)
327
328
# Position controls
329
self.slider.to_edge(DOWN)
330
self.color_picker.to_edge(RIGHT)
331
self.checkbox.to_edge(UP)
332
333
self.add(self.slider, self.color_picker, self.checkbox)
334
335
def construct(self):
336
# Create reactive circle
337
circle = Circle(radius=1, color=BLUE)
338
339
# Add updater that responds to controls
340
def circle_updater(c):
341
# Update radius from slider
342
new_radius = self.slider.get_value()
343
c.set_width(2 * new_radius)
344
345
# Update color from color picker
346
if self.checkbox.get_value():
347
c.set_fill(
348
color=self.color_picker.get_picked_color(),
349
opacity=self.color_picker.get_picked_opacity()
350
)
351
else:
352
c.set_fill(BLUE, opacity=1)
353
354
circle.add_updater(circle_updater)
355
self.add(circle)
356
357
# Make circle draggable
358
self.add(MotionMobject(circle))
359
360
self.wait(10) # Interactive exploration time
361
```
362
363
### Button-Triggered Animations
364
365
```python
366
class ButtonDemo(Scene):
367
def setup(self):
368
self.circles = VGroup()
369
370
# Create buttons
371
add_button = Button(
372
Text("Add Circle", font_size=24).set_fill(WHITE).add_background_rectangle(),
373
on_click=self.add_circle
374
)
375
376
clear_button = Button(
377
Text("Clear", font_size=24).set_fill(WHITE).add_background_rectangle(),
378
on_click=self.clear_circles
379
)
380
381
# Position buttons
382
buttons = VGroup(add_button, clear_button)
383
buttons.arrange(RIGHT, buff=1)
384
buttons.to_edge(DOWN)
385
386
self.add(buttons)
387
388
def add_circle(self, button):
389
"""Callback to add a new circle"""
390
circle = Circle(radius=0.3, color=random_color())
391
circle.move_to(3 * np.random.random(3) - 1.5)
392
self.circles.add(circle)
393
self.add(circle)
394
395
def clear_circles(self, button):
396
"""Callback to clear all circles"""
397
self.remove(self.circles)
398
self.circles = VGroup()
399
400
def construct(self):
401
self.wait(10)
402
```
403
404
### Control Panel Organization
405
406
```python
407
class ControlPanelDemo(Scene):
408
def setup(self):
409
# Create individual controls
410
amplitude_slider = LinearNumberSlider(
411
value=1.0, min_value=0.1, max_value=2.0, step=0.1
412
)
413
frequency_slider = LinearNumberSlider(
414
value=1.0, min_value=0.1, max_value=5.0, step=0.1
415
)
416
color_controls = ColorSliders()
417
show_axes = Checkbox(value=True)
418
419
# Organize in panel with labels
420
panel = ControlPanel(
421
Text("Amplitude", font_size=20), amplitude_slider,
422
Text("Frequency", font_size=20), frequency_slider,
423
Text("Wave Color", font_size=20), color_controls,
424
Text("Show Axes", font_size=20), show_axes
425
)
426
427
panel.to_edge(LEFT)
428
self.add(panel)
429
430
# Store controls for access
431
self.amplitude_slider = amplitude_slider
432
self.frequency_slider = frequency_slider
433
self.color_controls = color_controls
434
self.show_axes = show_axes
435
436
def construct(self):
437
# Create responsive function plot
438
axes = Axes(x_range=[-3, 3], y_range=[-2, 2])
439
440
def get_wave():
441
return axes.plot(
442
lambda x: self.amplitude_slider.get_value() * np.sin(
443
self.frequency_slider.get_value() * x
444
),
445
color=self.color_controls.get_picked_color()
446
)
447
448
wave = get_wave()
449
450
def wave_updater(w):
451
new_wave = get_wave()
452
w.become(new_wave)
453
454
def axes_updater(a):
455
a.set_opacity(1 if self.show_axes.get_value() else 0)
456
457
wave.add_updater(wave_updater)
458
axes.add_updater(axes_updater)
459
460
self.add(axes, wave)
461
self.wait(15)
462
```
463
464
## Interactive Scene Framework
465
466
```python { .api }
467
class InteractiveScene(Scene):
468
"""
469
Enhanced scene class with built-in selection and manipulation tools.
470
471
Features:
472
- Object selection with Ctrl+click/drag
473
- Grabbing modes (G/H/V keys for general/horizontal/vertical)
474
- Resizing with T key
475
- Copy/paste operations (Cmd+C/V/X)
476
- Color palette toggle
477
- Comprehensive keyboard shortcuts
478
"""
479
480
def add_to_selection(self, *mobjects):
481
"""Add mobjects to current selection."""
482
483
def clear_selection(self):
484
"""Clear current selection."""
485
486
def toggle_from_selection(self, mobject):
487
"""Toggle mobject in/out of selection."""
488
489
def copy_selection(self):
490
"""Copy selected mobjects to clipboard."""
491
492
def paste_selection(self):
493
"""Paste mobjects from clipboard."""
494
495
def delete_selection(self):
496
"""Delete currently selected mobjects."""
497
```
498
499
The interactive controls system in ManimGL provides a complete framework for building dynamic, responsive mathematical animations with real-time user interaction and parameter adjustment capabilities.