manim-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +104 -0
  2. package/dist/demo.mp4 +0 -0
  3. package/dist/index.js +65 -0
  4. package/dist/mcp-app.html +142 -0
  5. package/dist/server.js +1492 -0
  6. package/package.json +67 -0
  7. package/references/composer/SKILL.md +154 -0
  8. package/references/composer/references/3b1b-series-patterns.md +217 -0
  9. package/references/composer/references/domain-planning-guides/calculus-planning.md +188 -0
  10. package/references/composer/references/domain-planning-guides/linear-algebra-planning.md +169 -0
  11. package/references/composer/references/domain-planning-guides/ml-planning.md +286 -0
  12. package/references/composer/references/domain-planning-guides/number-theory-planning.md +187 -0
  13. package/references/composer/references/domain-planning-guides/physics-planning.md +249 -0
  14. package/references/composer/references/domain-planning-guides/probability-planning.md +200 -0
  15. package/references/composer/references/mathematical-storytelling.md +359 -0
  16. package/references/composer/references/narrative-patterns.md +221 -0
  17. package/references/composer/references/opening-patterns.md +284 -0
  18. package/references/composer/references/pacing-guide.md +289 -0
  19. package/references/composer/references/scene-archetypes.md +534 -0
  20. package/references/composer/references/scene-examples.md +379 -0
  21. package/references/composer/references/visual-techniques.md +480 -0
  22. package/references/composer/templates/scenes-template.md +147 -0
  23. package/references/manimce/SKILL.md +166 -0
  24. package/references/manimce/examples/3d_visualization.py +373 -0
  25. package/references/manimce/examples/basic_animations.py +212 -0
  26. package/references/manimce/examples/graph_plotting.py +401 -0
  27. package/references/manimce/examples/lorenz_attractor.py +172 -0
  28. package/references/manimce/examples/math_visualization.py +315 -0
  29. package/references/manimce/examples/updater_patterns.py +369 -0
  30. package/references/manimce/rules/3b1b-translation.md +594 -0
  31. package/references/manimce/rules/3d.md +254 -0
  32. package/references/manimce/rules/advanced-animations.md +594 -0
  33. package/references/manimce/rules/animation-groups.md +212 -0
  34. package/references/manimce/rules/animations.md +128 -0
  35. package/references/manimce/rules/api-pitfalls.md +89 -0
  36. package/references/manimce/rules/axes.md +214 -0
  37. package/references/manimce/rules/camera.md +208 -0
  38. package/references/manimce/rules/cli.md +232 -0
  39. package/references/manimce/rules/color-conventions.md +444 -0
  40. package/references/manimce/rules/colors.md +199 -0
  41. package/references/manimce/rules/config.md +264 -0
  42. package/references/manimce/rules/creation-animations.md +158 -0
  43. package/references/manimce/rules/graphing.md +233 -0
  44. package/references/manimce/rules/grouping.md +220 -0
  45. package/references/manimce/rules/latex.md +202 -0
  46. package/references/manimce/rules/lines.md +241 -0
  47. package/references/manimce/rules/long-form-video.md +552 -0
  48. package/references/manimce/rules/mathematical-domains.md +689 -0
  49. package/references/manimce/rules/mobjects.md +116 -0
  50. package/references/manimce/rules/multi-scene-composition.md +112 -0
  51. package/references/manimce/rules/pedagogy.md +532 -0
  52. package/references/manimce/rules/physics-simulations.md +610 -0
  53. package/references/manimce/rules/positioning.md +211 -0
  54. package/references/manimce/rules/scenes.md +121 -0
  55. package/references/manimce/rules/shapes.md +300 -0
  56. package/references/manimce/rules/styling.md +177 -0
  57. package/references/manimce/rules/text-animations.md +222 -0
  58. package/references/manimce/rules/text.md +189 -0
  59. package/references/manimce/rules/timing.md +227 -0
  60. package/references/manimce/rules/transform-animations.md +157 -0
  61. package/references/manimce/rules/updaters.md +226 -0
  62. package/references/manimce/templates/basic_scene.py +64 -0
  63. package/references/manimce/templates/camera_scene.py +100 -0
  64. package/references/manimce/templates/threed_scene.py +138 -0
@@ -0,0 +1,594 @@
1
+ ---
2
+ name: advanced-animations
3
+ description: Advanced animation patterns from 3b1b including TransformMatchingTex, VShowPassingFlash, UpdateFromAlphaFunc, generate_target, always_redraw, and more
4
+ metadata:
5
+ tags: advanced, transform, matching-tex, alpha-func, updater, always-redraw, generate-target, move-to-target, homotopy, number-changeable
6
+ ---
7
+
8
+ # Advanced Animation Patterns
9
+
10
+ These are the advanced animation techniques used throughout 3b1b's videos, translated to ManimCE equivalents.
11
+
12
+ ## TransformMatchingTex for Equation Manipulation
13
+
14
+ Transform equations term-by-term using matching LaTeX substrings. This is the backbone of algebraic derivation scenes.
15
+
16
+ ```python
17
+ from manim import *
18
+
19
+ class TransformMatchingTexDemo(Scene):
20
+ """Smoothly morph one equation into another, matching terms by LaTeX."""
21
+
22
+ def construct(self):
23
+ # Step 1: Start equation
24
+ eq1 = MathTex(
25
+ r"x^2", r"+", r"2x", r"+", r"1", r"=", r"0"
26
+ )
27
+ self.play(Write(eq1))
28
+ self.wait()
29
+
30
+ # Step 2: Factor
31
+ eq2 = MathTex(
32
+ r"(", r"x", r"+", r"1", r")^2", r"=", r"0"
33
+ )
34
+ self.play(TransformMatchingTex(eq1, eq2))
35
+ self.wait()
36
+
37
+ # Step 3: Take square root
38
+ eq3 = MathTex(r"x", r"+", r"1", r"=", r"0")
39
+ self.play(TransformMatchingTex(eq2, eq3))
40
+ self.wait()
41
+
42
+ # Step 4: Solve
43
+ eq4 = MathTex(r"x", r"=", r"-1")
44
+ self.play(TransformMatchingTex(eq3, eq4))
45
+ self.wait(2)
46
+ ```
47
+
48
+ ### TransformMatchingShapes for Non-TeX Morphing
49
+
50
+ ```python
51
+ class TransformMatchingShapesDemo(Scene):
52
+ """Morph shapes that share visual similarity."""
53
+
54
+ def construct(self):
55
+ text1 = Text("Hello World", font_size=48)
56
+ text2 = Text("World Hello", font_size=48, color=YELLOW)
57
+
58
+ self.play(Write(text1))
59
+ self.wait()
60
+ self.play(TransformMatchingShapes(text1, text2))
61
+ self.wait()
62
+ ```
63
+
64
+ ## VShowPassingFlash for Flowing Effects
65
+
66
+ A flash of light that travels along a path. Used in `custom/end_screen.py` and throughout for highlighting flow/movement.
67
+
68
+ ```python
69
+ class FlashingEffect(Scene):
70
+ """VShowPassingFlash: a pulse traveling along a path."""
71
+
72
+ def construct(self):
73
+ # Create a path
74
+ circle = Circle(radius=2, color=BLUE)
75
+ self.add(circle)
76
+
77
+ # Flash traveling around the circle
78
+ flash_circle = circle.copy().set_stroke(YELLOW, width=8)
79
+ self.play(
80
+ ShowPassingFlash(flash_circle, time_width=0.3),
81
+ run_time=2,
82
+ )
83
+ self.wait()
84
+
85
+ # Multiple flashes on different paths
86
+ paths = VGroup(*[
87
+ Line(3 * LEFT + i * 0.5 * UP, 3 * RIGHT + i * 0.5 * UP, color=BLUE)
88
+ for i in range(-3, 4)
89
+ ])
90
+ self.add(paths)
91
+
92
+ flash_paths = paths.copy().set_stroke(WHITE, 5)
93
+ self.play(
94
+ LaggedStartMap(
95
+ ShowPassingFlash, flash_paths,
96
+ time_width=0.5, lag_ratio=0.15,
97
+ ),
98
+ run_time=3,
99
+ )
100
+ self.wait()
101
+ ```
102
+
103
+ ## UpdateFromAlphaFunc for Custom Interpolation
104
+
105
+ Directly from `_2020/sir.py` and `_2017/mug.py` -- gives you full control over how a mobject changes as alpha goes from 0 to 1.
106
+
107
+ ```python
108
+ class AlphaFuncDemo(Scene):
109
+ """UpdateFromAlphaFunc: custom animation via alpha parameter."""
110
+
111
+ def construct(self):
112
+ circle = Circle(radius=1, color=WHITE)
113
+ self.add(circle)
114
+
115
+ # Custom color transition using alpha
116
+ start_color = BLUE
117
+ end_color = RED
118
+
119
+ self.play(
120
+ UpdateFromAlphaFunc(
121
+ circle,
122
+ lambda mob, alpha: mob.set_color(
123
+ interpolate_color(start_color, end_color, alpha)
124
+ ),
125
+ run_time=2,
126
+ )
127
+ )
128
+ self.wait()
129
+
130
+ # Complex multi-property animation
131
+ square = Square(side_length=1, color=GREEN, fill_opacity=0.5)
132
+ self.add(square)
133
+
134
+ def complex_update(mob, alpha):
135
+ mob.set_width(1 + 2 * alpha)
136
+ mob.set_fill(opacity=1 - 0.5 * alpha)
137
+ mob.rotate(alpha * PI / 4)
138
+ mob.set_color(interpolate_color(GREEN, YELLOW, alpha))
139
+
140
+ self.play(
141
+ UpdateFromAlphaFunc(square, complex_update, run_time=3)
142
+ )
143
+ self.wait()
144
+ ```
145
+
146
+ ## always_redraw vs add_updater
147
+
148
+ ### always_redraw: Rebuild Every Frame
149
+
150
+ Use when the mobject needs to be completely reconstructed each frame (e.g., geometry that depends on a moving point).
151
+
152
+ ```python
153
+ class AlwaysRedrawDemo(Scene):
154
+ """always_redraw: complete reconstruction every frame."""
155
+
156
+ def construct(self):
157
+ axes = Axes(x_range=[-3, 3], y_range=[-2, 4], x_length=8, y_length=5)
158
+ graph = axes.plot(lambda x: x**2, color=YELLOW)
159
+ self.add(axes, graph)
160
+
161
+ # Tracker controls the x-position
162
+ x_tracker = ValueTracker(-2)
163
+
164
+ # Dot on the graph (always_redraw reconstructs it each frame)
165
+ dot = always_redraw(
166
+ lambda: Dot(
167
+ axes.i2gp(x_tracker.get_value(), graph),
168
+ color=RED,
169
+ )
170
+ )
171
+
172
+ # Tangent line that follows the dot
173
+ tangent = always_redraw(
174
+ lambda: axes.get_secant_slope_group(
175
+ x_tracker.get_value(), graph, dx=0.01,
176
+ secant_line_color=BLUE,
177
+ secant_line_length=4,
178
+ )
179
+ )
180
+
181
+ # Horizontal and vertical lines to axes
182
+ h_line = always_redraw(
183
+ lambda: axes.get_horizontal_line(
184
+ axes.i2gp(x_tracker.get_value(), graph),
185
+ line_config={"dashed_ratio": 0.5},
186
+ )
187
+ )
188
+ v_line = always_redraw(
189
+ lambda: axes.get_vertical_line(
190
+ axes.i2gp(x_tracker.get_value(), graph),
191
+ line_config={"dashed_ratio": 0.5},
192
+ )
193
+ )
194
+
195
+ self.add(dot, tangent, h_line, v_line)
196
+
197
+ # Animate the tracker
198
+ self.play(x_tracker.animate.set_value(2), run_time=5)
199
+ self.wait()
200
+ ```
201
+
202
+ ### add_updater: Modify In-Place
203
+
204
+ Use when you need to adjust properties of an existing mobject without rebuilding it.
205
+
206
+ ```python
207
+ class AddUpdaterDemo(Scene):
208
+ """add_updater: modify an existing mobject each frame."""
209
+
210
+ def construct(self):
211
+ # A label that always follows another object
212
+ dot = Dot(color=YELLOW)
213
+ label = Text("Point", font_size=24)
214
+ label.add_updater(lambda m: m.next_to(dot, UR, buff=0.1))
215
+
216
+ self.add(dot, label)
217
+
218
+ # Move the dot around
219
+ self.play(dot.animate.shift(3 * RIGHT + UP), run_time=2)
220
+ self.play(dot.animate.shift(2 * DOWN + LEFT), run_time=2)
221
+ self.wait()
222
+
223
+ # Counter that tracks a value
224
+ counter = Integer(0, font_size=48)
225
+ counter.to_corner(UR)
226
+ value_tracker = ValueTracker(0)
227
+ counter.add_updater(
228
+ lambda m: m.set_value(int(value_tracker.get_value()))
229
+ )
230
+
231
+ self.add(counter)
232
+ self.play(value_tracker.animate.set_value(100), run_time=3)
233
+ self.wait()
234
+ ```
235
+
236
+ ### ManimGL f_always to ManimCE
237
+
238
+ In ManimGL, `f_always` provides a shorthand for method-calling updaters:
239
+ ```python
240
+ # ManimGL:
241
+ dot.f_always.move_to(lambda: axes.i2gp(tracker.get_value(), graph))
242
+
243
+ # ManimCE equivalent:
244
+ dot.add_updater(lambda m: m.move_to(axes.i2gp(tracker.get_value(), graph)))
245
+ ```
246
+
247
+ ## generate_target() / MoveToTarget Pattern
248
+
249
+ The classic 3b1b pattern for complex repositioning: create a target copy, modify it, then animate to it.
250
+
251
+ ```python
252
+ class GenerateTargetDemo(Scene):
253
+ """generate_target: set up a target state, then animate to it."""
254
+
255
+ def construct(self):
256
+ # Create initial mobject
257
+ equation = MathTex(r"E = mc^2", font_size=72)
258
+ self.play(Write(equation))
259
+ self.wait()
260
+
261
+ # Generate a target and modify it
262
+ equation.generate_target()
263
+ equation.target.scale(0.5)
264
+ equation.target.to_corner(UL)
265
+ equation.target.set_opacity(0.6)
266
+
267
+ # Animate to target
268
+ self.play(MoveToTarget(equation), run_time=1.5)
269
+ self.wait()
270
+
271
+ # This is equivalent to (but more flexible than):
272
+ # self.play(equation.animate.scale(0.5).to_corner(UL).set_opacity(0.6))
273
+ # generate_target is preferred when you need to inspect/adjust the target
274
+
275
+ # Complex rearrangement example
276
+ items = VGroup(*[
277
+ Circle(radius=0.3, color=random_bright_color(), fill_opacity=0.7)
278
+ for _ in range(6)
279
+ ])
280
+ items.arrange(RIGHT, buff=0.3)
281
+ self.play(LaggedStartMap(FadeIn, items, lag_ratio=0.1))
282
+ self.wait()
283
+
284
+ # Rearrange into 2x3 grid
285
+ for i, item in enumerate(items):
286
+ item.generate_target()
287
+ row, col = divmod(i, 3)
288
+ item.target.move_to(
289
+ 2 * DOWN + (col - 1) * 1.2 * RIGHT + row * 1.2 * DOWN
290
+ )
291
+
292
+ self.play(
293
+ LaggedStartMap(MoveToTarget, items, lag_ratio=0.1),
294
+ run_time=1.5,
295
+ )
296
+ self.wait()
297
+ ```
298
+
299
+ ## make_number_changeable() Equivalent
300
+
301
+ In ManimGL, `label.make_number_changeable("0.00")` creates a dynamic decimal that can be updated. ManimCE uses `DecimalNumber` with updaters.
302
+
303
+ ```python
304
+ class DynamicNumbers(Scene):
305
+ """ManimCE equivalent of make_number_changeable."""
306
+
307
+ def construct(self):
308
+ # Static template with a placeholder for the number
309
+ tracker = ValueTracker(0)
310
+
311
+ # Create the label with a DecimalNumber
312
+ prefix = MathTex(r"c = ", font_size=36)
313
+ number = DecimalNumber(
314
+ 0, num_decimal_places=4, font_size=36, color=RED,
315
+ include_sign=True,
316
+ )
317
+ number.add_updater(lambda m: m.set_value(tracker.get_value()))
318
+ number.add_updater(lambda m: m.next_to(prefix, RIGHT, buff=0.1))
319
+
320
+ label = VGroup(prefix, number)
321
+ label.to_corner(UR)
322
+ self.add(label)
323
+
324
+ # Animate the value changing
325
+ self.play(tracker.animate.set_value(0.01), run_time=3)
326
+ self.wait()
327
+ self.play(tracker.animate.set_value(1.5), run_time=2)
328
+ self.wait()
329
+
330
+ # Alternative: use a variable
331
+ var = Variable(0, MathTex(r"\theta"), num_decimal_places=2)
332
+ var.to_edge(DOWN)
333
+ self.add(var)
334
+ self.play(var.tracker.animate.set_value(PI), run_time=2)
335
+ self.wait()
336
+ ```
337
+
338
+ ## Homotopy for Continuous Deformation
339
+
340
+ From `_2016/eola/chapter1.py` -- continuously deform a mobject via a homotopy function.
341
+
342
+ ```python
343
+ class HomotopyDemo(Scene):
344
+ """Homotopy: continuously deform space."""
345
+
346
+ def construct(self):
347
+ plane = NumberPlane()
348
+ self.add(plane)
349
+
350
+ # Plane wave homotopy (from EoLA)
351
+ def wave_homotopy(x, y, z, t):
352
+ """Deform the plane with a wave effect."""
353
+ offset = np.sin(x - t * TAU) * 0.5 * t
354
+ return [x, y + offset, z]
355
+
356
+ self.play(
357
+ Homotopy(wave_homotopy, plane),
358
+ run_time=3,
359
+ )
360
+ self.wait()
361
+ ```
362
+
363
+ ## ChangeSpeed / Rate Function Composition
364
+
365
+ Control animation speed dynamically. In ManimCE, use custom rate functions.
366
+
367
+ ```python
368
+ class SpeedControl(Scene):
369
+ """Custom rate functions for speed control."""
370
+
371
+ def construct(self):
372
+ circle = Circle(radius=2, color=BLUE)
373
+
374
+ # Custom rate function: slow start, fast middle, slow end
375
+ def custom_rate(t):
376
+ return smooth(t) # Built-in smooth (ease in/out)
377
+
378
+ # Rush into (accelerating)
379
+ dot1 = Dot(color=RED)
380
+ dot1.move_to(3 * LEFT)
381
+ self.play(
382
+ dot1.animate.move_to(3 * RIGHT),
383
+ rate_func=rate_functions.rush_into,
384
+ run_time=2,
385
+ )
386
+
387
+ # Rush from (decelerating)
388
+ dot2 = Dot(color=GREEN)
389
+ dot2.move_to(3 * LEFT + DOWN)
390
+ self.play(
391
+ dot2.animate.move_to(3 * RIGHT + DOWN),
392
+ rate_func=rate_functions.rush_from,
393
+ run_time=2,
394
+ )
395
+
396
+ # There and back
397
+ dot3 = Dot(color=YELLOW)
398
+ self.play(
399
+ dot3.animate.shift(3 * RIGHT),
400
+ rate_func=there_and_back,
401
+ run_time=2,
402
+ )
403
+ self.wait()
404
+
405
+ # Custom double-speed function
406
+ def double_speed(t):
407
+ """Play at 2x speed, then hold."""
408
+ return min(2 * t, 1)
409
+
410
+ square = Square(color=PURPLE)
411
+ self.play(
412
+ Create(square),
413
+ rate_func=double_speed,
414
+ run_time=2, # Actually creates in 1s, holds for 1s
415
+ )
416
+ self.wait()
417
+ ```
418
+
419
+ ## Cycle Animation (Looping)
420
+
421
+ Create looping animations using updaters or repeated play calls.
422
+
423
+ ```python
424
+ class CycleAnimationDemo(Scene):
425
+ """Looping animations using updaters."""
426
+
427
+ def construct(self):
428
+ # Method 1: Continuous rotation via updater
429
+ square = Square(color=BLUE, fill_opacity=0.5)
430
+ square.add_updater(lambda m, dt: m.rotate(dt * PI / 2))
431
+ self.add(square)
432
+ self.wait(4) # Rotates continuously
433
+
434
+ square.clear_updaters()
435
+
436
+ # Method 2: Pulsing effect via updater
437
+ circle = Circle(radius=1, color=RED, fill_opacity=0.3)
438
+ time_tracker = ValueTracker(0)
439
+ time_tracker.add_updater(lambda m, dt: m.increment_value(dt))
440
+
441
+ circle.add_updater(
442
+ lambda m: m.set_width(
443
+ 2 + 0.3 * np.sin(time_tracker.get_value() * 2 * PI)
444
+ )
445
+ )
446
+
447
+ self.add(circle, time_tracker)
448
+ self.wait(5)
449
+ ```
450
+
451
+ ## LaggedStart / LaggedStartMap Patterns
452
+
453
+ The most-used animation combinator in 3b1b's code.
454
+
455
+ ```python
456
+ class LaggedPatterns(Scene):
457
+ """The many uses of LaggedStart and LaggedStartMap."""
458
+
459
+ def construct(self):
460
+ # LaggedStartMap: apply the same animation to each submobject
461
+ dots = VGroup(*[Dot(color=YELLOW) for _ in range(10)])
462
+ dots.arrange(RIGHT, buff=0.3)
463
+
464
+ self.play(
465
+ LaggedStartMap(FadeIn, dots, shift=0.5 * UP, lag_ratio=0.15),
466
+ run_time=2,
467
+ )
468
+ self.wait()
469
+
470
+ # LaggedStart: different animations, staggered
471
+ anims = [
472
+ dot.animate.shift(UP * np.random.uniform(0.5, 1.5))
473
+ for dot in dots
474
+ ]
475
+ self.play(LaggedStart(*anims, lag_ratio=0.1, run_time=2))
476
+ self.wait()
477
+
478
+ # LaggedStartMap with GrowArrow
479
+ arrows = VGroup(*[
480
+ Arrow(ORIGIN, RIGHT, color=BLUE) for _ in range(5)
481
+ ])
482
+ arrows.arrange(DOWN, buff=0.3).shift(3 * LEFT)
483
+
484
+ self.play(
485
+ LaggedStartMap(GrowArrow, arrows, lag_ratio=0.2, run_time=1.5)
486
+ )
487
+ self.wait()
488
+
489
+ # LaggedStartMap for removal
490
+ self.play(
491
+ LaggedStartMap(FadeOut, dots, shift=DOWN, lag_ratio=0.05),
492
+ run_time=1,
493
+ )
494
+ self.wait()
495
+ ```
496
+
497
+ ## Succession for Sequential Animations
498
+
499
+ ```python
500
+ class SuccessionDemo(Scene):
501
+ """Succession: chain animations into one play() call."""
502
+
503
+ def construct(self):
504
+ circle = Circle(color=BLUE)
505
+ square = Square(color=RED)
506
+ triangle = Triangle(color=GREEN)
507
+
508
+ self.play(
509
+ Succession(
510
+ Create(circle),
511
+ circle.animate.shift(LEFT * 2),
512
+ FadeIn(square),
513
+ square.animate.shift(RIGHT * 2),
514
+ FadeIn(triangle),
515
+ run_time=6,
516
+ )
517
+ )
518
+ self.wait()
519
+ ```
520
+
521
+ ## AnimationGroup with time_span (ManimCE Specific)
522
+
523
+ ```python
524
+ class TimeSpanDemo(Scene):
525
+ """Control when animations start and stop within a play() call."""
526
+
527
+ def construct(self):
528
+ c1 = Circle(color=BLUE).shift(2 * LEFT)
529
+ c2 = Circle(color=RED)
530
+ c3 = Circle(color=GREEN).shift(2 * RIGHT)
531
+
532
+ # c1 fades in from 0-1s, c2 from 0.5-1.5s, c3 from 1-2s
533
+ self.play(
534
+ FadeIn(c1),
535
+ FadeIn(c2),
536
+ FadeIn(c3),
537
+ run_time=2,
538
+ )
539
+ self.wait()
540
+ ```
541
+
542
+ ## Indicate / Circumscribe for Emphasis
543
+
544
+ ```python
545
+ class EmphasisAnimations(Scene):
546
+ """Drawing attention to specific elements."""
547
+
548
+ def construct(self):
549
+ equation = MathTex(r"a^2 + b^2 = c^2")
550
+ equation.scale(1.5)
551
+ self.add(equation)
552
+
553
+ # Circumscribe: draw a shape around
554
+ self.play(Circumscribe(equation[0][:2], color=YELLOW)) # a^2
555
+ self.wait()
556
+
557
+ # Indicate: briefly highlight
558
+ self.play(Indicate(equation[0][4:6], color=RED)) # b^2
559
+ self.wait()
560
+
561
+ # Flash: point flash effect
562
+ self.play(Flash(equation[0][-2:], color=GREEN)) # c^2
563
+ self.wait()
564
+
565
+ # Wiggle: shake for attention
566
+ self.play(Wiggle(equation))
567
+ self.wait()
568
+ ```
569
+
570
+ ## CountInFrom for Animated Number Reveal
571
+
572
+ ```python
573
+ class CountInDemo(Scene):
574
+ """Animate a number counting up from zero."""
575
+
576
+ def construct(self):
577
+ # Large integer counting up
578
+ number = Integer(12288, font_size=72, color=YELLOW)
579
+ self.play(CountInFrom(number, 0), run_time=2)
580
+ self.wait()
581
+ ```
582
+
583
+ ## Best Practices
584
+
585
+ 1. **TransformMatchingTex** for equation derivations -- split LaTeX into meaningful substrings
586
+ 2. **generate_target / MoveToTarget** for complex repositioning of existing mobjects
587
+ 3. **always_redraw** when geometry must be rebuilt from scratch each frame (e.g., tangent lines)
588
+ 4. **add_updater** when modifying properties of an existing mobject (position, color, value)
589
+ 5. **LaggedStartMap** for any operation applied to a group -- controls the cascade timing
590
+ 6. **UpdateFromAlphaFunc** for fully custom animation logic
591
+ 7. **DecimalNumber with updater** instead of ManimGL's `make_number_changeable`
592
+ 8. **rate_func** for speed control: `smooth`, `rush_into`, `rush_from`, `there_and_back`, `linear`
593
+ 9. **ShowPassingFlash** for flowing highlight effects along paths
594
+ 10. **Circumscribe / Indicate / Flash** for drawing attention to specific parts