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,532 @@
1
+ ---
2
+ name: pedagogy
3
+ description: Pedagogical patterns from 3Blue1Brown for creating effective mathematical explanations in ManimCE
4
+ metadata:
5
+ tags: pedagogy, teaching, 3b1b, pi-creature, intuition, visualization, revelation
6
+ ---
7
+
8
+ # Pedagogical Patterns from 3Blue1Brown
9
+
10
+ These patterns are distilled from the full 3Blue1Brown video corpus (2015-2026). They represent the core teaching philosophy that makes 3b1b videos effective.
11
+
12
+ ## "Never Forget What This Represents"
13
+
14
+ The central 3b1b principle: every algebraic manipulation must be tied back to its geometric or visual meaning. Never let the viewer lose sight of the connection between symbols and pictures.
15
+
16
+ ```python
17
+ from manim import *
18
+
19
+ class TieComputationToGeometry(Scene):
20
+ """Always show both the formula AND what it means visually."""
21
+
22
+ def construct(self):
23
+ # Show the equation
24
+ equation = MathTex(
25
+ r"|\vec{v}|^2 = x^2 + y^2",
26
+ tex_to_color_map={"x": GREEN, "y": RED},
27
+ )
28
+ equation.to_edge(UP)
29
+
30
+ # Show the geometry simultaneously
31
+ plane = NumberPlane().set_opacity(0.3)
32
+ vec = Vector([3, 2], color=YELLOW)
33
+ x_comp = Line(ORIGIN, [3, 0, 0], color=GREEN, stroke_width=4)
34
+ y_comp = Line([3, 0, 0], [3, 2, 0], color=RED, stroke_width=4)
35
+
36
+ x_label = MathTex("x", color=GREEN).next_to(x_comp, DOWN)
37
+ y_label = MathTex("y", color=RED).next_to(y_comp, RIGHT)
38
+
39
+ # Introduce both together
40
+ self.add(plane)
41
+ self.play(
42
+ GrowArrow(vec),
43
+ Write(equation),
44
+ )
45
+ self.wait()
46
+
47
+ # Show the components connecting to the formula
48
+ self.play(Create(x_comp), Write(x_label))
49
+ self.play(Create(y_comp), Write(y_label))
50
+ self.wait()
51
+
52
+ # Highlight the connection: square each component visually
53
+ x_square = Square(side_length=1.5, color=GREEN, fill_opacity=0.3)
54
+ x_square.next_to(x_comp, DOWN, buff=0.5, aligned_edge=LEFT)
55
+ x_sq_label = MathTex("x^2", color=GREEN).move_to(x_square)
56
+
57
+ y_square = Square(side_length=1.0, color=RED, fill_opacity=0.3)
58
+ y_square.next_to(y_comp, RIGHT, buff=0.5, aligned_edge=DOWN)
59
+ y_sq_label = MathTex("y^2", color=RED).move_to(y_square)
60
+
61
+ self.play(
62
+ DrawBorderThenFill(x_square), Write(x_sq_label),
63
+ DrawBorderThenFill(y_square), Write(y_sq_label),
64
+ )
65
+ self.wait(2)
66
+ ```
67
+
68
+ ## Physical Intuition First, Formalization Second
69
+
70
+ Always start with a physical, tangible example before introducing abstract notation. The viewer should already "feel" the answer before seeing the formula.
71
+
72
+ ```python
73
+ class IntuitionFirst(Scene):
74
+ """Show the physical behavior, THEN the equation."""
75
+
76
+ def construct(self):
77
+ # Phase 1: Show the physical behavior (no formulas yet)
78
+ title = Text("What happens when you spin a wheel?", font_size=36)
79
+ title.to_edge(UP)
80
+ self.play(Write(title))
81
+
82
+ wheel = Circle(radius=1.5, color=WHITE)
83
+ dot = Dot(point=wheel.point_from_proportion(0), color=YELLOW)
84
+ dot.add_updater(
85
+ lambda m, dt: m.move_to(
86
+ wheel.point_from_proportion(
87
+ (m.get_center()[0] / (2 * PI * 1.5)) % 1
88
+ )
89
+ )
90
+ )
91
+ self.play(Create(wheel), FadeIn(dot))
92
+
93
+ # Trace the path as the wheel rolls
94
+ traced_path = TracedPath(dot.get_center, stroke_color=YELLOW, stroke_width=2)
95
+ self.add(traced_path)
96
+ self.play(Rotate(wheel, 2 * TAU, about_point=ORIGIN), run_time=4)
97
+ self.wait()
98
+
99
+ # Phase 2: NOW introduce the formula
100
+ self.play(FadeOut(title))
101
+ formula_title = Text("The math behind this:", font_size=36)
102
+ formula_title.to_edge(UP)
103
+ formula = MathTex(
104
+ r"\vec{r}(t) = \begin{bmatrix} t - \sin(t) \\ 1 - \cos(t) \end{bmatrix}"
105
+ )
106
+ formula.next_to(formula_title, DOWN, buff=0.5)
107
+
108
+ self.play(Write(formula_title), Write(formula))
109
+ self.wait(2)
110
+ ```
111
+
112
+ ## Multiple Perspectives on the Same Concept
113
+
114
+ 3b1b regularly shows 2-3 different ways to think about the same idea (algebraic, geometric, physical). This is central to the Essence of Linear Algebra series.
115
+
116
+ ```python
117
+ class MultiplePerspectives(Scene):
118
+ """Show the same concept from different angles."""
119
+
120
+ def construct(self):
121
+ # Perspective 1: Geometric (visual)
122
+ p1_title = Text("Geometric View", font_size=30, color=BLUE)
123
+ p1_title.to_corner(UL)
124
+ det_visual = VGroup(
125
+ Vector([2, 0], color=GREEN),
126
+ Vector([1, 1.5], color=RED),
127
+ )
128
+ parallelogram = Polygon(
129
+ ORIGIN, [2, 0, 0], [3, 1.5, 0], [1, 1.5, 0],
130
+ fill_color=YELLOW, fill_opacity=0.3, stroke_color=YELLOW,
131
+ )
132
+ area_label = MathTex(r"\text{Area}", color=YELLOW)
133
+ area_label.move_to(parallelogram)
134
+
135
+ geo_group = VGroup(det_visual, parallelogram, area_label, p1_title)
136
+ geo_group.shift(3 * LEFT)
137
+
138
+ # Perspective 2: Algebraic (formula)
139
+ p2_title = Text("Algebraic View", font_size=30, color=GREEN)
140
+ p2_title.to_edge(UP).shift(3 * RIGHT)
141
+ formula = MathTex(
142
+ r"\det \begin{bmatrix} a & b \\ c & d \end{bmatrix} = ad - bc"
143
+ )
144
+ formula.next_to(p2_title, DOWN, buff=0.5)
145
+
146
+ alg_group = VGroup(p2_title, formula)
147
+
148
+ # Show them side by side
149
+ self.play(
150
+ Write(p1_title),
151
+ GrowArrow(det_visual[0]), GrowArrow(det_visual[1]),
152
+ )
153
+ self.play(DrawBorderThenFill(parallelogram), Write(area_label))
154
+ self.wait()
155
+
156
+ self.play(Write(p2_title), Write(formula))
157
+ self.wait()
158
+
159
+ # Draw the connection
160
+ arrow = Arrow(
161
+ parallelogram.get_right(), formula.get_left(),
162
+ color=WHITE, buff=0.5,
163
+ )
164
+ same_thing = Text("Same thing!", font_size=24, color=YELLOW)
165
+ same_thing.next_to(arrow, UP)
166
+ self.play(GrowArrow(arrow), Write(same_thing))
167
+ self.wait(2)
168
+ ```
169
+
170
+ ## Pi Creature Reactions for Emotional Engagement
171
+
172
+ In ManimGL, 3b1b uses `PiCreatureScene` and `TeacherStudentsScene` for character reactions. In ManimCE, you can create similar emotional signposting with simpler means, or use the community pi creature plugin.
173
+
174
+ ### Emotional Markers Without Pi Creatures
175
+
176
+ ```python
177
+ class EmotionalMarkers(Scene):
178
+ """Use visual cues to signal emotional beats."""
179
+
180
+ def construct(self):
181
+ # Confusion marker
182
+ equation = MathTex(r"e^{i\pi} + 1 = 0")
183
+ equation.scale(1.5)
184
+ self.play(Write(equation))
185
+
186
+ # "Wait, what?" moment
187
+ question_marks = VGroup(
188
+ *[Text("?", font_size=48, color=YELLOW) for _ in range(3)]
189
+ )
190
+ question_marks.arrange(RIGHT, buff=0.3)
191
+ question_marks.next_to(equation, DOWN, buff=0.5)
192
+ self.play(
193
+ LaggedStartMap(FadeIn, question_marks, scale=2, lag_ratio=0.2)
194
+ )
195
+ self.wait()
196
+
197
+ # "Aha!" moment
198
+ self.play(FadeOut(question_marks))
199
+ checkmark = Text("Ah, that makes sense!", color=GREEN, font_size=36)
200
+ checkmark.next_to(equation, DOWN, buff=0.5)
201
+ self.play(Write(checkmark))
202
+ self.wait()
203
+
204
+ # Surprise/delight
205
+ highlight = SurroundingRectangle(equation, color=YELLOW, buff=0.3)
206
+ self.play(Create(highlight))
207
+ self.play(
208
+ highlight.animate.set_stroke(width=6),
209
+ rate_func=there_and_back,
210
+ run_time=0.8,
211
+ )
212
+ self.wait()
213
+ ```
214
+
215
+ ### Teacher-Student Pattern (from 3b1b's TeacherStudentsScene)
216
+
217
+ ```python
218
+ class TeacherStudentMoment(Scene):
219
+ """Recreate the teacher-student dynamic without pi creatures."""
220
+
221
+ def construct(self):
222
+ # Teacher's statement
223
+ teacher_bubble = RoundedRectangle(
224
+ corner_radius=0.3, width=5, height=1.5,
225
+ color=BLUE, fill_opacity=0.15,
226
+ ).to_edge(LEFT).shift(UP)
227
+ teacher_text = Text(
228
+ "Think about what the\ndeterminant represents",
229
+ font_size=28,
230
+ ).move_to(teacher_bubble)
231
+
232
+ # Student's response
233
+ student_bubble = RoundedRectangle(
234
+ corner_radius=0.3, width=4, height=1.2,
235
+ color=GREEN, fill_opacity=0.15,
236
+ ).to_edge(RIGHT).shift(DOWN)
237
+ student_text = Text(
238
+ "Scaling factor\nfor areas!",
239
+ font_size=28,
240
+ ).move_to(student_bubble)
241
+
242
+ self.play(FadeIn(teacher_bubble), Write(teacher_text))
243
+ self.wait(2)
244
+ self.play(FadeIn(student_bubble), Write(student_text))
245
+ self.wait(2)
246
+ ```
247
+
248
+ ## Building on Prior Knowledge (Explicit Recap Scenes)
249
+
250
+ Many 3b1b series videos open by briefly recapping the previous video's key idea.
251
+
252
+ ```python
253
+ class RecapPreviousVideo(Scene):
254
+ """Brief recap before diving into new content."""
255
+
256
+ def construct(self):
257
+ # Mini header
258
+ recap_label = Text("Quick recap from last time:", font_size=28, color=GREY_A)
259
+ recap_label.to_corner(UL)
260
+ self.play(Write(recap_label))
261
+
262
+ # Show the key takeaway from previous video (dimmed)
263
+ prev_result = MathTex(
264
+ r"A\vec{v} = \lambda \vec{v}",
265
+ font_size=48,
266
+ )
267
+ prev_result.set_opacity(0.7)
268
+ prev_context = Text(
269
+ "An eigenvector stays on its own span",
270
+ font_size=24, color=GREY_B,
271
+ )
272
+ prev_context.next_to(prev_result, DOWN)
273
+
274
+ self.play(FadeIn(VGroup(prev_result, prev_context), shift=0.5 * UP))
275
+ self.wait(2)
276
+
277
+ # Transition to new content
278
+ new_title = Text("Today: How to compute eigenvalues", font_size=40)
279
+ new_title.to_edge(UP)
280
+ self.play(
281
+ FadeOut(VGroup(recap_label, prev_result, prev_context), shift=UP),
282
+ Write(new_title),
283
+ )
284
+ self.wait()
285
+ ```
286
+
287
+ ## Controlled Revelation Pattern
288
+
289
+ Information is revealed progressively. Question marks become answers. Blank spaces get filled in. This creates a sense of discovery.
290
+
291
+ ```python
292
+ class ControlledRevelation(Scene):
293
+ """Reveal information progressively -- question marks become answers."""
294
+
295
+ def construct(self):
296
+ # Start with unknowns
297
+ equation = MathTex(
298
+ r"\det(A - ", r"\lambda", r"I) = ", r"?"
299
+ )
300
+ equation.set_color_by_tex(r"\lambda", TEAL)
301
+ equation[-1].set_color(RED)
302
+ equation.scale(1.3)
303
+ self.play(Write(equation))
304
+ self.wait(2)
305
+
306
+ # Replace the question mark with the actual answer
307
+ answer = MathTex(r"0", color=GREEN)
308
+ answer.scale(1.3)
309
+ answer.move_to(equation[-1])
310
+ self.play(
311
+ FadeOut(equation[-1], shift=UP),
312
+ FadeIn(answer, shift=UP),
313
+ )
314
+ self.wait()
315
+
316
+ # Expand progressively
317
+ expanded = MathTex(
318
+ r"(3-\lambda)(1-\lambda) - 2 = 0",
319
+ tex_to_color_map={r"\lambda": TEAL},
320
+ )
321
+ expanded.next_to(equation, DOWN, buff=0.8)
322
+ self.play(Write(expanded, run_time=2))
323
+ self.wait()
324
+
325
+ # Factor
326
+ factored = MathTex(
327
+ r"\lambda^2 - 4\lambda + 1 = 0",
328
+ tex_to_color_map={r"\lambda": TEAL},
329
+ )
330
+ factored.next_to(expanded, DOWN, buff=0.5)
331
+ self.play(Write(factored))
332
+ self.wait(2)
333
+ ```
334
+
335
+ ### Progressive Detail on Diagrams
336
+
337
+ ```python
338
+ class ProgressiveDetail(Scene):
339
+ """Add detail to a diagram in stages."""
340
+
341
+ def construct(self):
342
+ # Stage 1: Simple shape
343
+ circle = Circle(radius=2, color=WHITE)
344
+ self.play(Create(circle))
345
+ self.wait()
346
+
347
+ # Stage 2: Add points
348
+ points = VGroup(*[
349
+ Dot(circle.point_from_proportion(i / 5), color=YELLOW)
350
+ for i in range(5)
351
+ ])
352
+ self.play(LaggedStartMap(FadeIn, points, scale=2, lag_ratio=0.15))
353
+ self.wait()
354
+
355
+ # Stage 3: Add chords
356
+ lines = VGroup()
357
+ for i in range(5):
358
+ for j in range(i + 1, 5):
359
+ line = Line(
360
+ points[i].get_center(), points[j].get_center(),
361
+ stroke_width=1, color=BLUE,
362
+ )
363
+ lines.add(line)
364
+ self.play(LaggedStartMap(Create, lines, lag_ratio=0.05))
365
+ self.wait()
366
+
367
+ # Stage 4: Add intersection dots
368
+ intersections = VGroup(*[
369
+ Dot(radius=0.05, color=RED)
370
+ for _ in range(3) # Simplified
371
+ ])
372
+ # ... position at actual intersections
373
+ self.play(LaggedStartMap(FadeIn, intersections, scale=3, lag_ratio=0.1))
374
+ self.wait()
375
+
376
+ # Stage 5: Count regions with labels
377
+ counts = VGroup(*[
378
+ Integer(n, font_size=24, color=GREEN)
379
+ for n in [1, 2, 4, 8, 16]
380
+ ])
381
+ # ... position in each configuration
382
+ ```
383
+
384
+ ## The "Build False Confidence, Then Reveal Truth" Pattern
385
+
386
+ From Moser's Circle Problem: present a pattern that looks obvious, then show it breaks. This is a powerful teaching tool for mathematical rigor.
387
+
388
+ ```python
389
+ class FalseConfidence(Scene):
390
+ """Moser pattern: build expectation, then subvert it."""
391
+
392
+ def construct(self):
393
+ # Phase 1: Accumulate evidence for the "obvious" pattern
394
+ title = Text("Powers of 2?", font_size=48)
395
+ title.to_edge(UP)
396
+ self.play(Write(title))
397
+
398
+ values = [1, 2, 4, 8, 16]
399
+ value_mobs = VGroup()
400
+ for v in values:
401
+ mob = Integer(v, font_size=42)
402
+ value_mobs.add(mob)
403
+ value_mobs.arrange(RIGHT, buff=1.0)
404
+ value_mobs.next_to(title, DOWN, buff=1.0)
405
+
406
+ for mob in value_mobs:
407
+ self.play(FadeIn(mob, shift=0.25 * UP), run_time=0.5)
408
+ self.wait(0.5)
409
+
410
+ # Viewer expects 32 next
411
+ dots = MathTex(r"\ldots")
412
+ dots.next_to(value_mobs, RIGHT)
413
+ next_val = Integer(32, font_size=42, color=GREEN)
414
+ next_val.next_to(dots, RIGHT)
415
+ question = Text("32?", font_size=42, color=GREEN)
416
+ question.next_to(dots, RIGHT)
417
+ self.play(Write(dots), FadeIn(question))
418
+ self.wait(2)
419
+
420
+ # Phase 2: The REVEAL -- it's actually 31!
421
+ actual = Integer(31, font_size=42, color=RED)
422
+ actual.move_to(question)
423
+ cross = Cross(question, stroke_color=RED, stroke_width=6)
424
+
425
+ self.play(Create(cross))
426
+ self.wait()
427
+ self.play(
428
+ FadeOut(question), FadeOut(cross),
429
+ FadeIn(actual, scale=1.5),
430
+ )
431
+
432
+ # Update title
433
+ new_title = Text("NOT powers of 2!", font_size=48, color=RED)
434
+ new_title.to_edge(UP)
435
+ self.play(
436
+ FadeOut(title),
437
+ FadeIn(new_title),
438
+ )
439
+ self.wait(2)
440
+ ```
441
+
442
+ ## Signposting and Navigation
443
+
444
+ 3b1b frequently tells the viewer where they are in the explanation:
445
+
446
+ ```python
447
+ class Signposting(Scene):
448
+ """Visual navigation through a multi-step argument."""
449
+
450
+ def construct(self):
451
+ # Step tracker
452
+ steps = VGroup(
453
+ Text("1. Setup", font_size=24),
454
+ Text("2. Key insight", font_size=24),
455
+ Text("3. Proof", font_size=24),
456
+ Text("4. Implications", font_size=24),
457
+ )
458
+ steps.arrange(DOWN, aligned_edge=LEFT, buff=0.3)
459
+ steps.to_corner(UL)
460
+ steps.set_opacity(0.3)
461
+
462
+ self.add(steps)
463
+
464
+ # Highlight current step
465
+ for i, step in enumerate(steps):
466
+ if i > 0:
467
+ steps[i - 1].animate.set_opacity(0.3)
468
+ self.play(step.animate.set_opacity(1.0).set_color(YELLOW))
469
+ self.wait()
470
+ # ... do the content for this step ...
471
+ self.wait(2)
472
+ ```
473
+
474
+ ## Analogy Pattern
475
+
476
+ 3b1b often introduces a concept through analogy to a familiar domain:
477
+
478
+ ```python
479
+ class AnalogyPattern(Scene):
480
+ """Introduce abstract concept through familiar analogy."""
481
+
482
+ def construct(self):
483
+ # The familiar thing
484
+ familiar_title = Text("A recipe", font_size=36)
485
+ familiar_title.to_edge(UP).shift(3 * LEFT)
486
+ recipe_items = VGroup(
487
+ Text("Ingredients:", font_size=24),
488
+ Text(" 2 cups flour", font_size=20),
489
+ Text(" 1 cup sugar", font_size=20),
490
+ Text(" 3 eggs", font_size=20),
491
+ ).arrange(DOWN, aligned_edge=LEFT)
492
+ recipe_items.next_to(familiar_title, DOWN)
493
+
494
+ # The abstract thing
495
+ abstract_title = Text("A linear combination", font_size=36)
496
+ abstract_title.to_edge(UP).shift(3 * RIGHT)
497
+ math_items = VGroup(
498
+ MathTex(r"\text{Components:}", font_size=24),
499
+ MathTex(r"2\hat{\imath}", font_size=24, color=GREEN),
500
+ MathTex(r"1\hat{\jmath}", font_size=24, color=RED),
501
+ MathTex(r"3\hat{k}", font_size=24, color=BLUE),
502
+ ).arrange(DOWN, aligned_edge=LEFT)
503
+ math_items.next_to(abstract_title, DOWN)
504
+
505
+ # Show familiar first
506
+ self.play(Write(familiar_title), FadeIn(recipe_items, lag_ratio=0.2))
507
+ self.wait(2)
508
+
509
+ # Then the abstract, drawing parallels
510
+ self.play(Write(abstract_title), FadeIn(math_items, lag_ratio=0.2))
511
+
512
+ # Draw arrows showing the analogy
513
+ arrows = VGroup(*[
514
+ Arrow(recipe_items[i + 1].get_right(), math_items[i + 1].get_left(), buff=0.3)
515
+ for i in range(3)
516
+ ])
517
+ self.play(LaggedStartMap(GrowArrow, arrows, lag_ratio=0.2))
518
+ self.wait(2)
519
+ ```
520
+
521
+ ## Best Practices Summary
522
+
523
+ 1. **Always tie algebra to geometry** -- if you write a formula, show what it means visually
524
+ 2. **Physical intuition first** -- let the viewer "feel" the concept before formalizing
525
+ 3. **Show multiple perspectives** -- algebraic, geometric, and physical views of the same idea
526
+ 4. **Progressive revelation** -- question marks become answers, details accumulate
527
+ 5. **Build false confidence** (when appropriate) -- show a pattern, then break it
528
+ 6. **Signpost your progress** -- tell the viewer where they are in the argument
529
+ 7. **Use emotional markers** -- surprise, confusion, and satisfaction moments
530
+ 8. **Recap before extending** -- briefly review before building on prior concepts
531
+ 9. **Analogy first** -- connect new concepts to familiar ones
532
+ 10. **Never let notation become opaque** -- if a symbol appears, the viewer should know what it means