Oui vous pouvez créer des vidéos avec Claude, cependant...
Depuis dix ans, un outil secret produit les animations les plus regardées sur YouTube. Presque personne ne sait qu'il existe. Claude peut maintenant l'écrire pour vous, en français.
Oui, vous avez bien entendu. Claude peut créer des animations mathématiques en vidéo. Pas directement.
Pour y arriver, vous passez par un outil que le grand public ignore presque entièrement. Voici ce qu'il produit :
Vous décrivez. Claude écrit le code. La vidéo se génère.
Python / Manimmanim -ql scene.py IntroSansManim
from manim import *
class IntroSansManim(Scene):
def construct(self):
t1 = Text("Vous décrivez.", font_size=48, color="#E8EDF5")
t2 = Text("Claude écrit le code.", font_size=38, color="#22D3EE")
t3 = Text("La vidéo se génère.", font_size=38, color="#7C5CFF")
VGroup(t1, t2, t3).arrange(DOWN, buff=0.6)
self.play(Write(t1), run_time=0.9)
self.play(FadeIn(t2, shift=UP*0.2), run_time=0.7)
self.play(FadeIn(t3, shift=UP*0.2), run_time=0.7)
self.wait(1.2)
Cet outil s'appelle Manim.
Grant Sanderson, mathématicien et auteur de la chaîne 3Blue1Brown, l'a créé en 2015 pour produire ses propres vidéos. Manim prend du code Python et le convertit en animations mathématiques : des équations qui se transforment, des courbes qui naissent, des formes géométriques construites image par image. Ce n'est pas un éditeur vidéo. C'est un moteur de rendu qui obéit à des instructions. La communauté open source en maintient aujourd'hui une version publique, Manim Community Edition, compatible Python 3.9 à 3.13.
Nous l'avons découvert il y a 4 ans. À l'époque, utiliser Manim demandait de maîtriser des dizaines de classes Python et la syntaxe LaTeX, réservé aux développeurs confirmés. Avec les modèles de langage, l'usage a changé. Vous entrez avec Claude.
Ce que Claude change
Manim n'a pas toujours été accessible. Pour produire la moindre animation, il fallait maîtriser simultanément cinq choses : Python, l'API Manim (plus de 60 classes), la syntaxe LaTeX pour les équations, les systèmes de coordonnées de la bibliothèque, et les mathématiques que vous vouliez visualiser. Cinq domaines en même temps, avant de voir la première image bouger.
La plupart des gens abandonnaient après deux heures de documentation.
Ce changement de rythme change tout. Quand une itération prend 30 secondes au lieu de 30 minutes, vous pouvez tester des idées, explorer des variantes, affiner le résultat. La créativité n'attend plus l'apprentissage technique.
- Classe TracedPath et ses paramètres stroke_color, stroke_opacity
- Fonction color_gradient() pour le dégradé de couleurs
- Distribution gaussienne NumPy : np.random.normal(0, σ, 2)
- np.clip() pour les bords de l'écran
- Gestion des animations simultanées avec *anims
- Paramètre rate_func=linear pour éviter les à-coups
La conséquence directe : Manim devient accessible à des profils qui n'avaient aucune chance de l'utiliser avant.
Quand une animation Manim plante, l'erreur Python est précise et lisible. Copiez-collez le traceback dans Claude :
Traceback: line 34, in construct
trace = TracedPath(dot.get_center...)"
Dans 90% des cas, une seule itération suffit. Claude connaît Manim mieux que la plupart des développeurs qui l'utilisent.
Comment demander à Claude pour éviter les erreurs
Le titre de cet article le dit : vous pouvez créer des vidéos avec Claude, cependant... tout dépend de la formulation. Un prompt vague produit un code qui plante. Un prompt structuré produit une animation fonctionnelle au premier essai.
Les 6 règles qui font la différence
Template qui marche :
Prompt à copier
Écris une scène Manim Community Edition 0.18 nommée [NomScene].
Visuel : [décrivez dans l'ordre chronologique ce que vous voulez voir]
Contraintes :
- Fond sombre #070B14
- Couleurs : cyan #22D3EE, violet #7C5CFF, orange #F59E0B
- Durée totale : [X] secondes, rate_func=linear pour les mouvements continus
- N'utilise pas ShowCreation, PiCreature, GraphScene
- Commente chaque bloc en français
Retourne uniquement le code Python, sans explication.
Testez toujours avec manim -ql scene.py NomScene (basse qualité, rapide) avant le rendu final en -qh.
Ce que Manim produit avec Claude
Huit animations générées avec ce template. Le code de chacune est visible sous la vidéo, cliquez pour le masquer.
Mouvement brownien
12 particules en marche gaussienne depuis le centre. Chaque pas suit N(0, σ²). Les traces restent. L'animation boucle.
12 particules · 90 pas · σ = 0.18 · TracedPath
Python / Manim · MouvementBrownienmanim -ql scene.py MouvementBrownien
from manim import *
import numpy as np
config.background_color = "#070B14"
class MouvementBrownien(Scene):
def construct(self):
np.random.seed(42) # reproductibilité
N = 12 # nombre de particules
STEPS = 90 # nombre de pas gaussiens
DT = 0.08 # durée d'un pas (secondes Manim)
SIGMA = 0.18 # écart-type du processus brownien
# Dégradé cyan → violet → orange sur N particules
colors = color_gradient(["#22D3EE", "#7C5CFF", "#F59E0B"], N)
dots = []
for i in range(N):
dot = Dot(ORIGIN, color=colors[i], radius=0.07)
# TracedPath dessine la trajectoire passée de chaque particule
self.add(TracedPath(dot.get_center,
stroke_color=colors[i],
stroke_width=1.4,
stroke_opacity=0.55), dot)
dots.append(dot)
# Marche aléatoire : chaque déplacement ~ N(0, σ²) indépendant
for _ in range(STEPS):
anims = []
for dot in dots:
dx, dy = np.random.normal(0, SIGMA, 2)
nx = np.clip(dot.get_x() + dx, -5.8, 5.8)
ny = np.clip(dot.get_y() + dy, -3.2, 3.2)
anims.append(dot.animate.move_to([nx, ny, 0]))
self.play(*anims, run_time=DT, rate_func=linear)
Double pendule
Trois pendules, 0.01 rad d'écart au départ. En 8 secondes ils divergent complètement. Intégration RK4 de l'équation de Lagrange.
3 pendules · Δθ = 0.01 rad · RK4 · 500 pas · chaos déterministe
Python / Manim · DoublePendulemanim -ql scene.py DoublePendule
from manim import *
import numpy as np
config.background_color = "#070B14"
class DoublePendule(Scene):
def construct(self):
g = 9.81; L1 = L2 = 1.4; m1 = m2 = 1.0
def deriv(state, _):
# Équations de Lagrange pour un double pendule plan
t1, w1, t2, w2 = state
dt = t1 - t2
den = 2*m1 + m2 - m2*np.cos(2*dt)
a1 = (-g*(2*m1+m2)*np.sin(t1) - m2*g*np.sin(t1-2*t2)
- 2*np.sin(dt)*m2*(w2**2*L2 + w1**2*L1*np.cos(dt))) / (L1*den)
a2 = (2*np.sin(dt)*(w1**2*L1*(m1+m2) + g*(m1+m2)*np.cos(t1)
+ w2**2*L2*m2*np.cos(dt))) / (L2*den)
return [w1, a1, w2, a2]
def rk4(state, dt):
# Runge-Kutta 4 : erreur en O(dt⁵), bien plus précis qu'Euler
k1 = np.array(deriv(state, 0))
k2 = np.array(deriv(state + dt/2*k1, 0))
k3 = np.array(deriv(state + dt/2*k2, 0))
k4 = np.array(deriv(state + dt*k3, 0))
return state + (dt/6)*(k1 + 2*k2 + 2*k3 + k4)
# 3 conditions initiales quasi-identiques : divergence visible après ~4 s
inits = [np.array([np.pi/2, 0, np.pi/2, 0]),
np.array([np.pi/2+0.01, 0, np.pi/2, 0]),
np.array([np.pi/2+0.02, 0, np.pi/2, 0])]
colors_p = ["#22D3EE", "#7C5CFF", "#F59E0B"]
NSTEPS, DT = 500, 0.025
# Pré-calcul complet avant animation (pas de lag en live)
trajs = []
for init in inits:
s = init.copy(); traj = [s.copy()]
for _ in range(NSTEPS):
s = rk4(s, DT); traj.append(s.copy())
trajs.append(traj)
PIVOT = np.array([0, 1.5, 0])
rod1s, rod2s, dot2s = [], [], []
for ci, col in enumerate(colors_p):
t1, _, t2, _ = trajs[ci][0]
p1 = PIVOT + np.array([L1*np.sin(t1), -L1*np.cos(t1), 0])
p2 = p1 + np.array([L2*np.sin(t2), -L2*np.cos(t2), 0])
r1 = Line(PIVOT, p1, color="#8A93A6", stroke_width=2)
r2 = Line(p1, p2, color="#8A93A6", stroke_width=2)
d2 = Dot(p2, color=col, radius=0.1)
self.add(TracedPath(d2.get_center, stroke_color=col,
stroke_width=1.8, stroke_opacity=0.7), r1, r2, d2)
rod1s.append(r1); rod2s.append(r2); dot2s.append(d2)
self.add(Dot(PIVOT, color="#E8EDF5", radius=0.07))
# 4 pas RK4 par frame : fluidité sans surcharge CPU
for step in range(0, NSTEPS, 4):
anims = []
for ci in range(3):
t1, _, t2, _ = trajs[ci][step]
p1 = PIVOT + np.array([L1*np.sin(t1), -L1*np.cos(t1), 0])
p2 = p1 + np.array([L2*np.sin(t2), -L2*np.cos(t2), 0])
anims += [rod1s[ci].animate.put_start_and_end_on(PIVOT, p1),
rod2s[ci].animate.put_start_and_end_on(p1, p2),
dot2s[ci].animate.move_to(p2)]
self.play(*anims, run_time=DT*4, rate_func=linear)
Épicycles Fourier
4 cercles rotatifs imbriqués. La pointe du dernier bras trace une courbe. C'est la transformée de Fourier rendue visible : toute forme est une somme de rotations.
Rayons 1.6 / 0.8 / 0.5 / 0.3 · vitesses 1, 2, -3, 5 · boucle en 2π
Python / Manim · Epicyclesmanim -ql scene.py Epicycles
from manim import *
import numpy as np
config.background_color = "#070B14"
class Epicycles(Scene):
def construct(self):
# (rayon, vitesse angulaire) : simule une décomposition de Fourier
epi = [(1.6, 1.0), (0.8, 2.0), (0.5, -3.0), (0.3, 5.0)]
tracker = ValueTracker(0)
def get_circles():
# Redessine cercles et bras à chaque frame
t = tracker.get_value(); cx, cy = 0.0, 0.0
g = VGroup()
for r, w in epi:
a = w * t
g.add(Circle(radius=r, color="#8A93A6",
stroke_width=1.2, stroke_opacity=0.5).move_to([cx,cy,0]),
Line([cx,cy,0], [cx+r*np.cos(a), cy+r*np.sin(a), 0],
color="#7C5CFF", stroke_width=1.5))
cx += r*np.cos(a); cy += r*np.sin(a)
return g
def tip_pos():
# Position de la pointe = somme vectorielle des rotations
t = tracker.get_value(); cx, cy = 0.0, 0.0
for r, w in epi:
cx += r*np.cos(w*t); cy += r*np.sin(w*t)
return np.array([cx, cy, 0])
tip = Dot(color="#22D3EE", radius=0.07)
tip.add_updater(lambda d: d.move_to(tip_pos()))
# TracedPath accumule la courbe tracée par la pointe
self.add(TracedPath(tip.get_center, stroke_color="#22D3EE",
stroke_width=2.5, stroke_opacity=0.9),
always_redraw(get_circles), tip)
# 2 tours complets : la courbe se referme exactement
self.play(tracker.animate.set_value(2*TAU), run_time=8, rate_func=linear)
Résolution pas à pas
x² - 5x + 6 = 0 se résout en direct. Discriminant, formule quadratique, racines sur la parabole. Format conçu pour l'enseignement.
Δ = 1 · x₁ = 3, x₂ = 2 · parabole violette · racines orange
Python / Manim · ResolutionDegre2manim -ql scene.py ResolutionDegre2
from manim import *
config.background_color = "#070B14"
class ResolutionDegre2(Scene):
def construct(self):
title = Text("Résoudre x² - 5x + 6 = 0",
color="#E8EDF5", font_size=34).to_edge(UP, buff=0.55)
self.play(Write(title), run_time=0.9)
# Étape 1 : discriminant
step1 = MathTex(r"\Delta = b^2 - 4ac = 25 - 24 = 1",
color="#22D3EE", font_size=30).shift(UP*0.9)
self.play(Write(step1), run_time=1)
# Étape 2 : formule quadratique
step2 = MathTex(r"\frac{-b \pm \sqrt{\Delta}}{2a} = \frac{5 \pm 1}{2}",
color="#E8EDF5", font_size=30).shift(UP*0.1)
self.play(Write(step2), run_time=1.2)
# Étape 3 : les deux racines en orange
self.play(
FadeIn(MathTex(r"x_1 = 3", color="#F59E0B", font_size=36).shift(DOWN*0.8+LEFT*1.5), shift=UP*0.2),
FadeIn(MathTex(r"x_2 = 2", color="#F59E0B", font_size=36).shift(DOWN*0.8+RIGHT*1.5), shift=UP*0.2),
run_time=0.8)
# Étape 4 : parabole + racines sur l'axe
axes = Axes(x_range=[0,5,1], y_range=[-1.5,3,1],
x_length=6, y_length=3, tips=False,
axis_config={"color": "#8A93A6", "stroke_width": 1.2}
).to_edge(DOWN, buff=0.3)
self.play(Create(axes),
Create(axes.plot(lambda x: x**2-5*x+6, color="#7C5CFF", stroke_width=2.2)),
run_time=1)
self.play(FadeIn(Dot(axes.c2p(3,0), color="#F59E0B", radius=0.1)),
FadeIn(Dot(axes.c2p(2,0), color="#F59E0B", radius=0.1)))
self.wait(0.7)
Descente de gradient
f(x) = x² · 9 étapes · xₙ₊₁ = xₙ - η·∇f
Python / Manim · DescenteGradientmanim -ql scene.py DescenteGradient
from manim import *
config.background_color = "#070B14"
class DescenteGradient(Scene):
def construct(self):
axes = Axes(x_range=[-3,3,1], y_range=[-0.3,7,2],
x_length=9, y_length=5.5, tips=False,
axis_config={"color": "#8A93A6"}).scale(0.82)
self.play(Create(axes),
Create(axes.plot(lambda x: x**2, color="#7C5CFF", stroke_width=2.5)),
run_time=1)
x_val = 2.5; lr = 0.35
dot = Dot(axes.c2p(x_val, x_val**2), color="#22D3EE", radius=0.12)
self.play(FadeIn(dot))
# 9 étapes : x ← x - η·2x (gradient de x² = 2x)
for _ in range(9):
x_new = max(x_val - lr * 2 * x_val, -2.9)
self.play(dot.animate.move_to(axes.c2p(x_new, x_new**2)),
run_time=0.38, rate_func=smooth)
x_val = x_new
self.play(
Write(MathTex(r"\,x_{n+1} = x_n - \eta\,\nabla f(x_n)",
color="#22D3EE", font_size=30).to_corner(DL, buff=0.6)),
Write(MathTex(r"\text{minimum}", color="#F59E0B", font_size=24
).next_to(axes.c2p(0,0), UP, buff=0.25)))
self.wait(0.8)
Série de Fourier
sin(x) + sin(3x)/3 + ... 9 harmoniques · onde carrée idéale en pointillés
Python / Manim · FourierCarremanim -ql scene.py FourierCarre
from manim import *
import numpy as np
config.background_color = "#070B14"
class FourierCarre(Scene):
def construct(self):
axes = Axes(x_range=[0,2*PI,PI/2], y_range=[-1.6,1.6,1],
x_length=10, y_length=5, tips=False,
axis_config={"color": "#8A93A6"}).scale(0.82)
self.play(Create(axes), run_time=0.6)
colors = ["#22D3EE", "#7C5CFF", "#F59E0B", "#E8EDF5", "#22D3EE"]
harmonics = [1, 3, 5, 7, 9]
curves = []
for i, n in enumerate(harmonics):
# Somme des harmoniques impairs : approche de l'onde carrée
terms = harmonics[:i+1]
curve = axes.plot(
lambda x, t=terms: sum(4/(k*PI)*np.sin(k*x) for k in t),
color=colors[i], stroke_width=2.2)
lbl = Text(f"{i+1} harmonique{'s' if i else ''}",
font_size=22, color=colors[i]).to_corner(UR, buff=0.5)
if curves:
self.play(Transform(curves[0], curve), Transform(curves[1], lbl), run_time=0.9)
else:
self.play(Create(curve), Write(lbl), run_time=1)
curves = [curve, lbl]
# Onde carrée idéale en pointillés semi-transparents
self.play(Create(axes.plot(
lambda x: 1.0 if (x%(2*PI)) < PI else -1.0,
color="#E8EDF5", stroke_width=1.5, stroke_opacity=0.4,
discontinuities=[PI, 2*PI], dt=0.01)), run_time=0.8)
self.wait(1)
Théorème de Pythagore
a² + b² = c² · un carré sur chaque côté · preuve visuelle
Python / Manim · PythagoreAnimemanim -ql scene.py PythagoreAnime
from manim import *
import numpy as np
config.background_color = "#070B14"
class PythagoreAnime(Scene):
def construct(self):
a, b = 2.4, 1.8; c = np.sqrt(a**2 + b**2)
A = np.array([-a/2,-b/2,0]); B = np.array([a/2,-b/2,0]); C = np.array([-a/2,b/2,0])
self.play(Create(Polygon(A,B,C, color="#E8EDF5", stroke_width=2.5)))
# Carré cyan sur a
self.play(DrawBorderThenFill(Polygon(A,B,B+[0,-a,0],A+[0,-a,0],
color="#22D3EE", fill_color="#22D3EE", fill_opacity=0.18)))
self.play(Write(MathTex("a^2", color="#22D3EE", font_size=32).move_to(A+[a/2,-a/2,0])))
# Carré violet sur b
self.play(DrawBorderThenFill(Polygon(A,C,C+[-b,0,0],A+[-b,0,0],
color="#7C5CFF", fill_color="#7C5CFF", fill_opacity=0.18)))
self.play(Write(MathTex("b^2", color="#7C5CFF", font_size=32).move_to(A+[-b/2,b/2,0])))
# Carré orange sur c (perpendiculaire à l'hypoténuse)
dc = (B-C)/np.linalg.norm(B-C); pc = np.array([-dc[1],dc[0],0])
self.play(DrawBorderThenFill(Polygon(C,B,B+pc*c,C+pc*c,
color="#F59E0B", fill_color="#F59E0B", fill_opacity=0.18)))
self.play(Write(MathTex(r"a^2+b^2=c^2", color="#E8EDF5", font_size=38).to_corner(DR, buff=0.7)))
self.wait(0.8)
Onde sonore
f(x,t) = A·sin(kx - ωt) · propagation en temps réel · always_redraw
Python / Manim · OndeSonoremanim -ql scene.py OndeSonore
from manim import *
import numpy as np
config.background_color = "#070B14"
class OndeSonore(Scene):
def construct(self):
axes = Axes(x_range=[0,4*PI,PI], y_range=[-1.4,1.4,1],
x_length=10, y_length=4.5, tips=False,
axis_config={"color": "#8A93A6"}).scale(0.82)
self.play(Create(axes), run_time=0.6)
t_tracker = ValueTracker(0)
# always_redraw : recalcule la courbe à chaque frame du tracker
wave = always_redraw(lambda: axes.plot(
lambda x: np.sin(x - t_tracker.get_value()),
color="#22D3EE", stroke_width=2.8))
self.add(wave)
self.play(Write(MathTex(r"f(x,t) = A\sin(kx-\omega t)",
color="#22D3EE", font_size=30
).to_corner(UR, buff=0.55)), run_time=0.8)
# t avance de 0 à 4π : deux longueurs d'onde parcourues
self.play(t_tracker.animate.set_value(4*PI), run_time=3.5, rate_func=linear)
self.wait(0.5)
Quelle option choisir ?
| Option | Installation | Qualité max | Idéal pour |
|---|---|---|---|
| claude.ai + copier le code | Aucune | 4K | Tester une idée en 5 min |
| Claude Code CLI | Python + Manim | 4K + MP4 | Projets, itération rapide |
| AnimG (animg.app) | Aucune | 1080p | Partage rapide, navigateur |
Installation
Manim tourne sur Python 3.9 à 3.13. Depuis la version 0.19, FFmpeg n'est plus à installer manuellement : il est embarqué dans le paquet. L'installation se résume à une commande.
Deux méthodes. La méthode B (Chocolatey) installe tout d'un coup, sans configuration manuelle.
Méthode A : pip (simple)
# Python 3.9 à 3.13 requis (python.org)
pip install manim
Méthode B : Chocolatey (recommandée)
# Dans PowerShell en administrateur
Set-ExecutionPolicy Bypass -Scope Process -Force
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
# Installe Manim + toutes ses dépendances en une commande
choco install manimce
LaTeX : requis pour les équations (MathTex)
# MiKTeX : léger (~500 Mo), téléchargeable sur miktex.org
# Cochez "Install missing packages on the fly" lors de l'installation
winget install MiKTeX.MiKTeX
# Homebrew requis : brew.sh
brew install --cask mactex-no-gui
# Installer Manim (FFmpeg inclus depuis v0.19)
pip install manim
Si Homebrew n'est pas installé : /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Ubuntu / Debian : dépendances système
sudo apt install libpango1.0-dev texlive texlive-latex-extra
# Installer Manim
pip install manim
Vérifier l'installation
manim --version
python -c "from manim import *; print('Manim OK')"
# Premier rendu : -ql = basse qualité (rapide), -qh = haute qualité
manim -ql scene.py MouvementBrownien
Erreurs fréquentes
pip uninstall manimgl manimlib manim -y && pip install manim
python -m manim -ql scene.py NomScene
10 templates Manim prêts à copier
Les prompts exacts, les commandes et les résultats attendus. Réservé aux abonnés newsletter.
Télécharger le PDFManim n'est pas un outil de présentation. C'est un moteur de rendu mathématique. Ce qui le rend difficile à prendre en main seul le rend puissant entre les mains d'un modèle de langage. Nous continuons à l'utiliser pour chaque article qui nécessite une animation. Claude gère le code. Nous gérons les idées.
Notez cet article