| name | galaxy-formation-animation |
| description | Create a concise Manim animation explaining galaxy formation for 11th‑grade students. Includes best‑practice steps, common pitfalls, and reusable code snippets. |
Overview
This skill outlines a reproducible workflow for generating a ~1‑2 min Manim animation that covers:
- Early‑Universe density fluctuations
- Dark‑matter halo collapse
- Gas cooling & star formation
- Mergers → elliptical vs spiral galaxies
- Summary of key take‑aways
The animation uses clean transitions – each textual block is placed in a VGroup and fully FadeOut before the next appears, preventing overlapping captions.
Prerequisites
- Python 3.12+ with Manim Community Edition (
pip install manim).
- A virtual environment (e.g.,
~/.venvs/manim).
numpy for random positioning.
Common Pitfalls & Fixes
| Symptom | Cause | Fix |
|---|
ValueError: operands could not be broadcast together with shapes (2,) (3,) | move_to called with a 2‑D NumPy array. Manim expects a 3‑D vector. | Use np.append(np.random.rand(2)*scale‑offset, 0) or np.array([x, y, 0]) for all move_to calls. |
| Overlapping text between steps | Not clearing previous text groups. | Wrap each step’s text and graphics in a VGroup (e.g., step = VGroup(title, caption)) and self.play(FadeOut(step)) before starting the next. |
| Long render times for drafts | Using high‑quality settings unintentionally. | Render drafts with -ql (quick low‑res) and only use -qh for final export. |
Template Script (script.py)
"""
Galaxy Formation – Simple Manim Explainer
Target: ~1‑2 min, ~70 animations, clean transitions.
"""
from manim import *
import numpy as np
Create a concise Manim animation explaining galaxy formation for 11th‑grade students. Includes best‑practice steps, common pitfalls, and reusable code snippets.
- Confirm required inputs and credentials are available.
- Run the smallest safe command or example before scaling up.
- Check produced files, API responses, or plots before reporting success.
BG = "#0D1117"
BLUE = "#58C4DD"
GREEN = "#83C167"
YELLOW = "#FFFF00"
WHITE = "#EAEAEA"
DIM = "#7A8599"
RED = "#FF6B6B"
PURPLE = "#C792EA"
ORANGE = "#FFB347"
MONO = "DejaVu Sans Mono"
class GalaxyFormation(Scene):
def construct(self):
self.camera.background_color = BG
self._part1_fluctuations()
self._part2_halo_collapse()
self._part3_gas_cooling_star_formation()
self._part4_mergers_and_morphology()
self._part5_summary()
def _part1_fluctuations(self):
title = Text("Galaxy Formation", font_size=, color=BLUE, weight=BOLD, font=MONO)
.play(Write(title), run_time=)
.wait()
dots = VGroup(*[Dot(radius=, color=WHITE).move_to(np.append(np.random.rand()*-, )) _ ()])
.play(Create(dots), run_time=)
.wait()
overdense = VGroup(*[dots[i] i [,,,]])
d overdense:
.play(d.animate.set_color(YELLOW), run_time=)
cap = Text(, font_size=, color=YELLOW, font=MONO)
cap.to_edge(DOWN, buff=)
.play(Write(cap), run_time=)
.wait()
.play(FadeOut(VGroup(title, dots, overdense, cap)), run_time=)
.wait()
():
title = Text(, font_size=, color=BLUE, weight=BOLD, font=MONO)
.play(Write(title), run_time=)
halo = Circle(radius=, color=PURPLE, stroke_width=).set_opacity()
.play(Create(halo), run_time=)
.play(Transform(halo, Circle(radius=, color=PURPLE, stroke_width=)), run_time=)
.play(halo.animate.set_width(), run_time=)
particles = VGroup(*[Dot(radius=, color=PURPLE).move_to(np.append(np.random.rand()*-, )) _ ()])
.play(FadeIn(particles, scale=), run_time=)
cap = Text(, font_size=, color=YELLOW, font=MONO)
cap.to_edge(DOWN, buff=)
.play(Write(cap), run_time=)
.wait()
.play(FadeOut(VGroup(title, halo, particles, cap)), run_time=)
.wait()
():
title = Text(, font_size=, color=BLUE, weight=BOLD, font=MONO)
.play(Write(title), run_time=)
halo = Circle(radius=, color=PURPLE, stroke_width=).set_opacity()
.play(Create(halo), run_time=)
gas = Circle(radius=, color=BLUE).set_opacity()
.play(Create(gas), run_time=)
.play(gas.animate.set_width().set_opacity(), run_time=)
stars = VGroup(*[Dot(radius=, color=YELLOW).move_to(np.append(np.random.rand()*-, )) _ ()])
.play(FadeIn(stars, scale=), run_time=)
cap = Text(, font_size=, color=YELLOW, font=MONO)
cap.to_edge(DOWN, buff=)
.play(Write(cap), run_time=)
.wait()
.play(FadeOut(VGroup(title, halo, gas, stars, cap)), run_time=)
.wait()
():
title = Text(, font_size=, color=BLUE, weight=BOLD, font=MONO)
.play(Write(title), run_time=)
():
d = Circle(radius=radius, color=BLUE).move_to(centre)
arms = VGroup()
a np.linspace(, *np.pi, , endpoint=):
arm = Arc(radius*, a+arm_angle, a+arm_angle+, color=WHITE, stroke_width=)
arm.move_to(centre)
arms.add(arm)
VGroup(d, arms)
gal1 = disk(, LEFT*, )
gal2 = disk(, RIGHT*, )
.play(FadeIn(gal1, scale=), FadeIn(gal2, scale=), run_time=)
arrow1 = Arrow(gal1.get_center(), ORIGIN, color=RED, buff=)
arrow2 = Arrow(gal2.get_center(), ORIGIN, color=RED, buff=)
.play(Create(arrow1), Create(arrow2), run_time=)
.play(gal1.animate.move_to(ORIGIN), gal2.animate.move_to(ORIGIN), run_time=)
.play(gal1.animate.scale(), gal2.animate.scale(), run_time=)
ellipse = Ellipse(width=, height=, color=ORANGE).set_opacity()
.play(FadeIn(ellipse), run_time=)
cap1 = Text(, font_size=, color=YELLOW, font=MONO)
cap1.to_edge(DOWN, buff=)
.play(Write(cap1), run_time=)
.wait()
.play(FadeOut(VGroup(gal1, gal2, arrow1, arrow2, ellipse, cap1)), run_time=)
disk_big = disk(, ORIGIN, )
.play(FadeIn(disk_big, scale=), run_time=)
cap2 = Text(, font_size=, color=YELLOW, font=MONO)
cap2.to_edge(DOWN, buff=)
.play(Write(cap2), run_time=)
.play(FadeOut(VGroup(title, disk_big, cap2)), run_time=)
.wait()
():
title = Text(, font_size=, color=BLUE, weight=BOLD, font=MONO)
.play(Write(title), run_time=)
points = [
(, ),
(, ),
(, ),
(, ),
(, ),
]
y =
num, txt points:
n = Text(num, font_size=, color=YELLOW, font=MONO, weight=BOLD)
t = Text(txt, font_size=, color=WHITE, font=MONO).next_to(n, RIGHT, buff=)
line = VGroup(n, t).to_edge(LEFT, buff=).shift(DOWN * y)
.play(Write(line), run_time=)
.wait()
y -=
closing = Text(, font_size=, color=GREEN, font=MONO)
closing.to_edge(DOWN, buff=)
.play(Write(closing), run_time=)
.wait()
.play(FadeOut(VGroup(title, closing)), run_time=)
Execution Steps
- Save the script as
script.py inside a project folder (e.g., ~/galaxy-animation/).
- Render a quick draft:
cd ~/galaxy-animation
~/.venvs/manim/bin/manim -ql script.py GalaxyFormation
- For the final high‑resolution version:
~/.venvs/manim/bin/manim -qh script.py GalaxyFormation
The output will be in media/videos/script/480p15/ (draft) and media/videos/script/1080p60/ (HQ).
Tips & Extensions
- Adjust speed: tweak
run_time values for each self.play.
- Add voice‑over: export the MP4 and combine with an audio track using
ffmpeg.
- Reuse components: the
disk helper function can generate any rotating disk galaxy.
- Export frames:
self.add(frame) inside any step to capture PNGs for slides.
Version: 1.0 – first release after successful trial‑and‑error fixing of 2‑D coordinate errors.