Communitygithub.com

manim-video-explainer

Générez des vidéos explicatives techniques en 1080p60, des présentations pas à pas de systèmes et des animations architecturales multi-actes avec Manim Community (sans LaTeX).

Qu'est-ce que manim-video-explainer ?

Ce skill permet de créer des vidéos explicatives techniques animées de haute qualité (1080p, 60 ips) en utilisant Manim Community sans dépendance à LaTeX. Il est idéal lorsque l'utilisateur demande une présentation vidéo animée, une animation d'architecture ou de processus, un tutoriel vidéo narré ou un explicateur visuel pas à pas. Le skill fournit des ressources clé en main : un thème Nord avec des connecteurs orthogonaux à ancrage de port (évitant les flèches diagonales), un vérificateur de mise en page, un outil de mixage vocal (Piper offline, HTTP compatible OpenAI, ou CLI TTS) avec normalisation de volume EBU R128, et un lecteur HTML5 autonome zéro-CDN. La narration est synchronisée échantillon par échantillon et produit des sous-titres WebVTT. Son architecture en « actes » (Setup, Exécution, Échec) permet de rendre chaque scène indépendamment. Avant de lancer un rendu complet, il est recommandé d'utiliser le script de vérification des limites pour garantir que le contenu reste dans le cadre de sécurité et éviter les collisions de texte. Le skill fonctionne avec les outils d'IA comme Claude Code, Cursor et Codex pour générer le code Python et orchestrer le workflow de rendu.

Compatible avec~Claude Code✓Codex CLI~Cursor
npx skills add https://github.com/iuriimedvedev-dev/manim-video-explainer-skill/tree/HEAD

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Système de vidéo explicative technique Manim 1080p60 et voix off synchronisée

Présentation et ressources intégrées

Créez des vidéos explicatives techniques nettes et déterministes en 1920x1080 @ 60 ips en Python pur avec Manim Community, sans nécessiter pdflatex ou MacTeX. Associez optionnellement les animations à une voix off neuronale précise à l'échantillon (Piper ONNX hors ligne, tout point d'accès HTTP /v1/audio/speech compatible OpenAI, ou TTS CLI personnalisé), une normalisation de volume EBU R128, des sous-titres .vtt WebVTT synchronisés, et un lecteur HTML5 hors ligne sans CDN.

  • assets/nord_manim_theme.py : Jetons de palette Nord Polar/Frost/Aurora, constantes de mise en page du cadre de sécurité, assistant de synchronisation BeatTimer, primitives sans LaTeX (make_header_banner, make_node_card, make_badge, animate_blocked_pulse, safe_fit_text), et connecteurs schématiques orthogonaux stricts à ancrage de port (connect_h, connect_v, connect_elbow, animate_packet_along, assert_all_orthogonal).
  • assets/video_player_template.html : Lecteur cinéma HTML5 autonome, sans CDN, thème Nord clair/sombre, avec onglets de chapitres, marqueurs de temps cliquables, vitesse de lecture (0.75x..2.0x), et commutation optionnelle de l'audio/sous-titres bilingue.
  • check_scene_bounds.py : Vérificateur de mise en page avant rendu qui contrôle les scènes Manim ou les points de contrôle JSON pour les débordements du cadre [-6.8, 6.8] x [-3.75, 3.75] et les collisions de texte entre éléments frères.
  • mux_narration.py : CLI Python avec la bibliothèque standard qui synthétise des WAV par temps via Piper, un point d'accès HTTP /v1/audio/speech compatible OpenAI, ou une commande TTS personnalisée, génère beat_timings.json, assemble une piste WAV maître précise à l'échantillon, clone les images vidéo de fin (tpad=stop_mode=clone) pour que la parole ne soit jamais coupée, normalise optionnellement le volume (--loudnorm-lufs -18.0), multiplexe H.264 + AAC (+faststart), et écrit des sous-titres .vtt.
  • lint.py : Vérificateur du bundle de sortie qui contrôle les flux H.264 yuv420p + audio par ffprobe, la monotonie des en-têtes/timbres WebVTT, et les invariants CSP/sans CDN/sans émoji du lecteur HTML5 hors ligne.
  • references/manim-layout-and-choreography.md : À lire lors de la conception de topologies multi-nœuds, de routage orthogonal bus/coude, de compteurs dynamiques, de flèches bidirectionnelles, ou de concaténation multi-scènes avec ffmpeg.
  • references/voiceover-and-commentary.md : À lire lors de la rédaction de manifestes de narration, d'instructions de prosodie pour narrateur naturel, de glossaires de prononciation phonétique, de normalisation EBU R128, ou de pistes vocales bilingues.

1. Storyboard et conception narrative multi-acte

Avant d'écrire du code Manim, structurez l'explication en actes distincts et une table de storyboard temps par temps. Gardez une classe Scene par Acte afin que les modifications de l'Acte 2 ne nécessitent jamais de re-rendu de l'Acte 1 ou 3 :

  • Acte 1 — Configuration et admission de la requête : Présentez la topologie, les invariants et le garde d'entrée (par ex., Client -> API Gateway -> Limiteur de Débit à Seau).
  • Acte 2 — Exécution en régime permanent : Tracez le chemin de données principal et les transitions d'état (par ex., lecture Read-Through Cache en simple vol -> hydratation Base de données primaire -> réécriture TTL).
  • Acte 3 — Modes de défaillance et reprise : Montrez ce qui se passe lorsque les limites sont atteintes ou que des nœuds tombent en panne (par ex., rejet de rafale HTTP 429 ou promotion de quorum Verrou distribué et réplication du journal d'écriture anticipée (WAL)).

Planifiez chaque temps explicitement avant de coder :

beat_idvisual_actionmin_visual_seccommentary_script (si narré)
act1_ingressAfficher 4 cartes de topologie Nord + en-tête ; animer le paquet de requête dans la passerelle3.2Les écritures entrantes passent par une passerelle périphérique avant de toucher le stockage avec état.
act1_bucketDécrémenter le compteur de jetons 100 -> 99 ; faire clignoter le badge vert ADMIS3.8Une vérification atomique du seau à jetons absorbe les rafales de trafic tout en limitant la concurrence en aval.
act1_rejectDéclencher animate_blocked_pulse (429 LIMITATION DE DÉBIT) lorsque les jetons atteignent 03.5Une fois la capacité de rafale épuisée, les appelants en excès échouent rapidement avec HTTP 429 au lieu de faire la queue indéfiniment.

2. Création sans LaTeX avec Manim Community

Configuration de l'environnement isolé

Approvisionnez Manim (et optionnellement piper-tts) dans un environnement virtuel isolé ; vérifiez que ffmpeg et ffprobe sont dans le PATH :

uv venv venv
uv pip install --python venv/bin/python manim piper-tts

Règles strictes sans LaTeX et schémas orthogonaux

  1. Zéro sous-processus TeX : N'utilisez jamais Tex, MathTex, Title, ou DecimalNumber (qui invoquent pdflatex/dvisvgm). Utilisez Text, MarkupText (<b>, <i>, <sub>, <sup>, <span foreground="...">), Code, RoundedRectangle, Arrow, DashedVMobject, et MoveAlongPath.
  2. Règle d'or des schémas (Fils orthogonaux ancrés aux ports) : N'appelez JAMAIS Arrow(card_a, card_b) directement sur deux Mobject lors de la construction de diagrammes d'architecture en blocs — Manim vise des rayons entre les centres des boîtes englobantes et produit des flèches diagonales tordues chaque fois que les largeurs de carte ou les positions Y diffèrent. Routez chaque arête via connect_h(card_from, card_to, y=...), connect_v(card_from, card_to, x=...), ou connect_elbow(points), animez les paquets avec animate_packet_along(self, edge, ...), et validez la géométrie des fils avec assert_all_orthogonal(connectors, cards).
  3. Grille de coordonnées du cadre de sécurité : Le canevas 16:9 de Manim s'étend sur [-7.11, 7.11] x [-4.0, 4.0]. Gardez tout le contenu créé à l'intérieur de [-6.6, 6.6] x [-3.5, 3.5], et laissez y < -3.05 vide à chaque fois que des sous-titres WebVTT sont affichés.
  4. Importez assets/nord_manim_theme.py : Définissez self.camera.background_color = BG_DARK et construisez les cartes, connecteurs orthogonaux, animations de paquets, et temps de pause de manière cohérente :
from pathlib import Path
import sys

SKILL_DIR = Path(__file__).resolve().parent
sys.path.insert(0, str(SKILL_DIR))
sys.path.insert(0, str(SKILL_DIR / "assets"))

from manim import Create, FadeIn, Scene, VGroup
from check_scene_bounds import dump_checkpoints
from nord_manim_theme import (
    ACCENT_BLUE, ACCENT_CYAN, BG_DARK, STATUS_ERR, STATUS_OK,
    BeatTimer, animate_blocked_pulse, animate_packet_along,
    assert_all_orthogonal, connect_h, make_header_banner, make_node_card,
)


class Act1RateLimiter(Scene):
    def construct(self) -> None:
        self.camera.background_color = BG_DARK
        timer = BeatTimer("tmp/beat_timings.json")
        checkpoints: list[dict] = []

        header = make_header_banner(
            "Acte 1 / Admission des requêtes",
            "Passerelle API & Limiteur de débit à seau",
        )
        gw = make_node_card("Passerelle API", "TLS + Routage", width=2.5, accent=ACCENT_CYAN).move_to([-4.5, 0.2, 0])
        limiter = make_node_card("Limiteur", "Seau : 100 jetons", width=2.5, accent=ACCENT_BLUE).move_to([-1.5, 0.2, 0])
        cache = make_node_card("Cache de lecture", "TTL simple vol", width=2.5, accent=ACCENT_CYAN).move_to([1.5, 0.2, 0])
        db = make_node_card("BD primaire", "Quorum WAL", width=2.5, accent=STATUS_OK).move_to([4.5, 0.2, 0])

        e_gw_lim = connect_h(gw, limiter)
        e_lim_cache = connect_h(limiter, cache)
        e_cache_db = connect_h(cache, db, dashed=True)
        assert_all_orthogonal([e_gw_lim, e_lim_cache, e_cache_db], [gw, limiter, cache, db])
        edges = VGroup(e_gw_lim, e_lim_cache, e_cache_db)

        self.play(FadeIn(header), FadeIn(gw), FadeIn(limiter), FadeIn(cache), FadeIn(db), Create(edges), run_time=1.1)
        spent = animate_packet_along(self, e_gw_lim, label="POST /orders", run_time=0.7)
        timer.hold(self, "act1_ingress", consumed_anim_s=1.1 + spent, fallback_s=3.2, checkpoints=checkpoints)

        spent = animate_packet_along(self, e_lim_cache, label="jeton=99", color=STATUS_OK, run_time=0.7)
        timer.hold(self, "act1_bucket", consumed_anim_s=spent, fallback_s=3.8, checkpoints=checkpoints)

        spent = animate_blocked_pulse(self, limiter, reason_label="429 LIMITATION DE DÉBIT", color=STATUS_ERR, run_time=0.95)
        timer.hold(self, "act1_reject", consumed_anim_s=spent, fallback_s=3.5, checkpoints=checkpoints)

        dump_checkpoints(checkpoints, "tmp/act1_bounds.json")

Vérifiez les limites et collisions nulles avant le rendu 1080p60

Vérifiez toujours la géométrie de la mise en page en quelques secondes avant de lancer un rendu complet 1080p60 :

# Option A : Accrochage de simulation en direct sur chaque appel Scene.wait() (< 3s, zéro encodage MP4)
./venv/bin/python .agents/skills/manim-video-explainer/check_scene_bounds.py \
  --module scenes.py --scene Act1RateLimiter

# Option B : Inspectez le JSON de points de contrôle émis après un rendu rapide -ql
python3 .agents/skills/manim-video-explainer/check_scene_bounds.py \
  --json tmp/act1_bounds.json

Lisez references/manim-layout-and-choreography.md pour les modèles de coordonnées multi-niveaux, les compteurs dynamiques ValueTracker, et les règles de nettoyage anti-collision.


3. Commentaire vocal optionnel synchronisé au temps (mux_narration.py)

La règle d'or de la narration

Ne lisez JAMAIS textuellement les titres des boîtes, flèches ou badges à l'écran. Les spectateurs voient déjà "Passerelle API -> Limiteur de débit" immédiatement. Écrivez un commentaire de processus explicatif qui indique pourquoi chaque transition se produit, quel invariant protège le système, et ce qui cause le rejet ou la reprise.

Workflow en deux passes avec priorité au temps

  1. Écrivez narration_manifest.json en séparant le text propre des sous-titres des remplacements phonétiques (tts_phonetic_text ou phonetic_glossary) afin que les acronymes techniques (WAL, gRPC, TTL, p99, HTTP 429) soient prononcés naturellement sans polluer les sous-titres .vtt :
{
  "lead_in_s": 0.20,
  "inter_beat_gap_s": 0.25,
  "phonetic_glossary": {
    "WAL": "journal d'écriture anticipée",
    "TTL": "T T L",
    "HTTP 429": "H T T P quatre vingt vingt-neuf"
  },
  "scenes": [
    {
      "id": "act1",
      "beats": [
        {
          "id": "act1_ingress",
          "min_visual_s": 3.2,
          "text": "Les écritures entrantes se terminent à la passerelle avant de toucher le stockage avec état."
        },
        {
          "id": "act1_bucket",
          "min_visual_s": 3.8,
          "text": "Une vérification atomique du seau à jetons absorbe le trafic de rafale tout en limitant la concurrence de la base de données."
        },
        {
          "id": "act1_reject",
          "min_visual_s": 3.5,
          "text": "Lorsque les jetons de rafale tombent à zéro, les appelants en excès échouent rapidement avec HTTP 429."
        }
      ]
    }
  ]
}
  1. Passe 1 — Synthétisez les WAV par temps et émettez les timings exacts :
# Option A : TTS neuronal Piper ONNX hors ligne (zéro appel réseau)
python3 .agents/skills/manim-video-explainer/mux_narration.py \
  --manifest tmp/narration_manifest.json \
  --scene-id act1 \
  --wav-dir cache/beats_fr \
  --piper-bin ./venv/bin/piper \
  --model ~/.cache/piper-voices/fr_FR-mls-medium.onnx \
  --emit-timings tmp/beat_timings.json

# Option B : TTS HTTP neuronal compatible OpenAI (/v1/audio/speech, urllib stdlib)
python3 .agents/skills/manim-video-explainer/mux_narration.py \
  --manifest tmp/narration_manifest.json \
  --scene-id act1 \
  --wav-dir cache/beats_fr \
  --http-tts-url https://api.openai.com/v1/audio/speech \
  --http-tts-key-env OPENAI_API_KEY \
  --http-tts-model gpt-4o-mini-tts \
  --http-tts-voice cedar \
  --http-tts-instructions "Narrateur calme et clair de documentaire technique ; brèves pauses aux propositions." \
  --emit-timings tmp/beat_timings.json

(Tout synthétiseur CLI personnalisé peut également être utilisé via --tts-cmd "my-tts --out {wav} --text {text}", ou des fichiers .wav pré-rendus peuvent être placés directement dans --wav-dir.)

  1. Rendu de la vidéo Manim 1080p60 piloté par tmp/beat_timings.json :
./venv/bin/manim -qh --fps 60 scenes.py Act1RateLimiter
  1. Passe 2 — Assemblage de la piste maître précise à l'échantillon, normalisation du volume, garde de fin et multiplexage AAC :
python3 .agents/skills/manim-video-explainer/mux_narration.py \
  --manifest tmp/narration_manifest.json \
  --scene-id act1 \
  --wav-dir cache/beats_fr \
  --video media/videos/scenes/1080p60/Act1RateLimiter.mp4 \
  --loudnorm-lufs -18.0 \
  --output output/act1_fr.mp4 \
  --vtt output/act1_fr.vtt

mux_narration.py place chaque WAV de temps à round(start_s * sample_rate) sur une piste maître PCM 16 bits, étend la dernière image vidéo via tpad=stop_mode=clone chaque fois que la parole finale dépasse la durée de la vidéo (zéro mot coupé), applique optionnellement la normalisation du volume EBU R128 (--loudnorm-lufs -18.0) pour que le volume reste cohérent entre les chapitres, encode -c:a aac -b:a 160k -movflags +faststart, et écrit output/act1_fr.vtt.

Lisez references/voiceover-and-commentary.md pour les transformations de commentaire Mauvais vs Bon, les remplacements phonétiques en langues non anglaises, et les workflows audio bilingues.


4. Encodage Web, concaténation multi-scène et lecteur HTML5 hors ligne

Encodage compatible Web et concaténation de bobine maître

Pour les présentations silencieuses (sans mux_narration.py), assurez la compatibilité universelle navigateur/QuickTime (yuv420p + atome moov déplacé) :

ffmpeg -y -i media/videos/scenes/1080p60/Act1RateLimiter.mp4 \
  -c:v libx264 -pix_fmt yuv420p -crf 23 -movflags +faststart \
  output/act1_silent.mp4

Pour assembler plusieurs MP4 d'actes narrés en une bobine maître continue :

printf "file 'act1_fr.mp4'\nfile 'act2_fr.mp4'\nfile 'act3_fr.mp4'\n" > output/concat.txt
ffmpeg -y -f concat -safe 0 -i output/concat.txt \
  -c:v libx264 -pix_fmt yuv420p -crf 21 -c:a aac -b:a 160k -movflags +faststart \
  output/master_fr.mp4

Lecteur HTML5 autonome optionnel

Lors de la livraison d'une présentation multi-chapitres ou d'un pack explicatif bilingue :

  1. Copiez assets/video_player_template.html vers output/index.html.
  2. Personnalisez PLAYER_CONFIG (chapters, sources, et markers correspondant aux timestamps des temps de beat_timings.json). Si vous livrez une seule langue, masquez ou omettez le bouton de langue secondaire dans #langGroup.
  3. Gardez tous les styles, icônes SVG monochromes et scripts 100% en ligne afin que le bundle fonctionne hors ligne sans CDN externe.
  4. Note sur le service HTTP 206 Partial Content : Les navigateurs nécessitent des réponses HTTP Byte-Range (206) pour parcourir les timelines <video> et charger les fichiers <track> .vtt proprement (file:// bloque les pistes WebVTT via CORS, et le serveur http.server de base de Python ne gère que 200 OK). Pour les tests locaux, utilisez un serveur léger comme nginx ou python3 -m http.server 8080.

5. Vérification du bundle de sortie (lint.py)

Exécutez lint.py avant de livrer pour vous assurer que la sortie est prête pour le web :

python3 .agents/skills/manim-video-explainer/lint.py output/master_fr.mp4 --vtt output/act1_fr.vtt --player output/index.html

lint.py vérifie :

  • Format vidéo – ffprobe confirme libx264 avec yuv420p, et un flux audio AAC stéréo (silencieux ignoré).
  • Sous-titres WebVTT – L'en-tête WEBVTT est présent ; les timbres des temps sont strictement croissants ; les timestamps --> ne dépassent pas le min_visual_s correspondant + lead_in_s + inter_beat_gap_s.
  • Contenu HTML5 hors ligne – Aucune source CDN (http://, https://) ; aucun émoji ou caractère hors plan Unicode (\u200B..\uFFFF) en dehors du code ; la Content-Security-Policy en ligne interdit connect-src sauf blob: et data:.

Skills associés