Communitygithub.com

manim-video-explainer

Renderiza vídeos técnicos explicativos em alta definição 1080p60, walkthroughs passo a passo de sistemas e animações de arquitetura multi-ato com Manim Community (sem LaTeX), conectores esquemáticos ortogonais ancorados por porta, narração neural TTS opcional sincronizada com batida (Piper offline, HTTP TTS compatível com OpenAI /v1/audio/speech ou CLI TTS) com normalização de loudness EBU R128 e legendas WebVTT, além de um player HTML5 opcional para capítulos offline.

O que é manim-video-explainer?

Este skill capacita agentes de IA, como Claude Code, Cursor ou Codex, a gerar vídeos explicativos técnicos de alta qualidade (1080p60) usando Manim Community, sem depender de LaTeX. Ele fornece um sistema completo para criar animações de arquitetura e processos, com ou sem narração narrada. O skill inclui um conjunto de ferramentas que vai desde a elaboração de storyboards multi-ato e componentes visuais pré-definidos (cartões, conectores ortogonais, temporizadores de batida) até a síntese de narração neural com normalização de loudness e geração automática de legendas. O fluxo de trabalho recomendado consiste em planejar a narrativa em beats, codificar a animação no Manim e, opcionalmente, usar o script `mux_narration.py` para sincronizar a narração, normalizar o áudio e gerar o vídeo final com legendas. O resultado pode ser encapsulado em um player HTML5 autônomo para apresentações offline. O skill é compatível com plataformas de desenvolvimento que permitem executar comandos Python e shell, sendo ideal para criar vídeos de alta qualidade para documentação técnica, tutoriais e demonstrações de arquitetura de software, eliminando a complexidade de renderização e sincronização de áudio, mas exigindo que as animações sigam regras estritas de layout (como o uso de conectores ortogonais em vez de setas diagonais) e que o usuário revise o guia de referência para boas práticas de design de cena.

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

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Manim 1080p60 Video Explicativo Técnico & Sistema de Narração Sincronizada

Visão Geral e Recursos Inclusos

Crie vídeos explicativos técnicos nítidos e determinísticos em 1920x1080 @ 60fps e walkthroughs de arquitetura em Python puro usando Manim Community, sem exigir pdflatex ou MacTeX. Emparelhe animações opcionalmente com narração neural precisa (Piper ONNX offline, qualquer endpoint HTTP /v1/audio/speech compatível com OpenAI ou CLI TTS personalizada), normalização de loudness EBU R128, legendas .vtt WebVTT sincronizadas e um player HTML5 offline sem CDN.

  • assets/nord_manim_theme.py: Tokens da paleta Nord Polar/Frost/Aurora, constantes de layout de quadro seguro, auxiliar de sincronização BeatTimer, primitivas livres de LaTeX (make_header_banner, make_node_card, make_badge, animate_blocked_pulse, safe_fit_text) e conectores esquemáticos ortogonais estritos ancorados por porta (connect_h, connect_v, connect_elbow, animate_packet_along, assert_all_orthogonal).
  • assets/video_player_template.html: Player de Cinema HTML5 Nord escuro/claro autônomo e sem CDN com abas de capítulos, marcadores de batida clicáveis, velocidade de reprodução (0.75x..2.0x) e alternância opcional de áudio/legendas bilíngue.
  • check_scene_bounds.py: Verificador de layout pré-renderização que chega cenas do Manim ou pontos de verificação JSON para estouro do quadro [-6.8, 6.8] x [-3.75, 3.75] e colisões de rótulos de texto irmãos.
  • mux_narration.py: CLI Python da stdlib que sintetiza WAVs por batida via Piper, um endpoint HTTP /v1/audio/speech compatível com OpenAI ou um comando TTS personalizado, emite beat_timings.json, monta uma faixa WAV mestre precisa em amostras, preenche quadros de vídeo finais com clone (tpad=stop_mode=clone) para que a fala nunca seja cortada, opcionalmente normaliza o loudness (--loudnorm-lufs -18.0), multiplexa H.264 + AAC (+faststart) e escreve legendas .vtt.
  • lint.py: Verificador do pacote de saída que inspeciona fluxos de áudio e H.264 yuv420p do ffprobe, monotonicidade do cabeçalho/cue WebVTT e invariantes CSP/sem CDN/sem emoji do player HTML5 offline.
  • references/manim-layout-and-choreography.md: Leia ao projetar topologias de múltiplos nós, roteamento ortogonal de barramento/cotovelo, contadores dinâmicos, setas bidirecionais ou concatenação de várias cenas com ffmpeg.
  • references/voiceover-and-commentary.md: Leia ao criar manifestos de narração, instruções de prosódia para narrador natural, glossários de pronúncia fonética, normalização EBU R128 ou faixas de narração bilíngues.

1. Storyboard e Design Narrativo Multi-Ato

Antes de escrever código Manim, estruture a explicação em atos discretos e uma tabela de storyboard batida por batida. Mantenha uma classe Scene por Ato para que edições no Ato 2 nunca exijam re-renderização do Ato 1 ou Ato 3:

  • Ato 1 — Configuração e Admissão de Solicitação: Apresente a topologia, invariantes e guarda de entrada (ex.: Cliente -> Gateway de API -> Limitador de Taxa Token-Bucket).
  • Ato 2 — Execução em Estado Estacionário: Trace o caminho de dados principal e as transições de estado (ex.: busca single-flight do Cache de Leitura -> hidratação do Banco de Dados Primário -> writeback TTL).
  • Ato 3 — Modos de Falha e Recuperação: Demonstre o que acontece quando os limites são atingidos ou os nós falham (ex.: rejeição de rajada HTTP 429 ou elevação de quorum Bloqueio Distribuído e Replicação de Log Write-Ahead).

Mapeie cada batida explicitamente antes de codificar:

beat_idvisual_actionmin_visual_seccommentary_script (se narrado)
act1_ingressRenderizar 4 cartões de topologia Nord + cabeçalho; animar pacote de solicitação para o Gateway3.2Escritas de entrada passam por um gateway de borda antes de tocar no armazenamento com estado.
act1_bucketDecrementar contador de tokens 100 -> 99; piscar emblema verde ADMITIDO3.8Uma verificação atômica de token-bucket absorve rajadas de tráfego enquanto limita a concorrência downstream.
act1_rejectAcionar animate_blocked_pulse (429 LIMITADO POR TAXA) quando os tokens chegarem a 03.5Quando a capacidade de rajada se esgota, os chamadores em excesso falham rapidamente com HTTP 429 em vez de enfileirar infinitamente.

2. Criação em Manim Community Livre de LaTeX

Configuração de Ambiente Isolado

Provisione o Manim (e o piper-tts opcional) dentro de um ambiente virtual isolado; verifique se ffmpeg e ffprobe estão no PATH:

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

Regras Estritas de Esquemático Livre de LaTeX e Ortogonal

  1. Zero Subprocessos TeX: Nunca use Tex, MathTex, Title ou DecimalNumber (que invocam pdflatex/dvisvgm). Use Text, MarkupText (<b>, <i>, <sub>, <sup>, <span foreground="...">), Code, RoundedRectangle, Arrow, DashedVMobject e MoveAlongPath.
  2. Regra de Ouro Esquemática (Fios Ortogonais Ancorados por Porta): NUNCA chame Arrow(card_a, card_b) diretamente em dois Mobjects ao construir diagramas de blocos de arquitetura — o Manim direciona raios entre os centros das caixas delimitadoras e produz setas diagonais tortas sempre que as larguras dos cartões ou as posições Y diferem. Roteie cada aresta via connect_h(card_from, card_to, y=...), connect_v(card_from, card_to, x=...) ou connect_elbow(points), anime pacotes com animate_packet_along(self, edge, ...) e valide a geometria dos fios com assert_all_orthogonal(connectors, cards).
  3. Grade de Coordenadas de Quadro Seguro: A tela 16:9 do Manim se estende por [-7.11, 7.11] x [-4.0, 4.0]. Mantenha todo o conteúdo criado dentro de [-6.6, 6.6] x [-3.5, 3.5] e mantenha y < -3.05 vazio sempre que as legendas WebVTT forem exibidas.
  4. Importe assets/nord_manim_theme.py: Defina self.camera.background_color = BG_DARK e construa cartões, conectores ortogonais, animações de pacotes e pausas de batida de forma consistente:
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(
            "Ato 1 / Admissão de Solicitação",
            "Gateway de API e Limitador de Taxa Token-Bucket",
        )
        gw = make_node_card("Gateway de API", "TLS + Correspondência de Rota", width=2.5, accent=ACCENT_CYAN).move_to([-4.5, 0.2, 0])
        limiter = make_node_card("Limitador de Taxa", "Bucket: 100 tok", width=2.5, accent=ACCENT_BLUE).move_to([-1.5, 0.2, 0])
        cache = make_node_card("Cache de Leitura", "TTL Single-Flight", width=2.5, accent=ACCENT_CYAN).move_to([1.5, 0.2, 0])
        db = make_node_card("Banco Primário", "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="token=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 LIMITADO POR TAXA", 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")

Verifique Limites e Zero Colisões Antes da Renderização 1080p60

Sempre verifique a geometria do layout em segundos antes de iniciar uma renderização 1080p60 completa:

# Opção A: Gancho dry-run ao vivo sobre cada chamada Scene.wait() (< 3s, zero codificação MP4)
./venv/bin/python .agents/skills/manim-video-explainer/check_scene_bounds.py \
  --module scenes.py --scene Act1RateLimiter

# Opção B: Inspecione o JSON de ponto de verificação emitido após uma renderização -ql rápida
python3 .agents/skills/manim-video-explainer/check_scene_bounds.py \
  --json tmp/act1_bounds.json

Leia references/manim-layout-and-choreography.md para modelos de coordenadas de várias camadas, contadores ValueTracker dinâmicos e regras de limpeza anti-colisão.


3. Comentário de Narração Sincronizada com Batida Opcional (mux_narration.py)

A Regra de Ouro da Narração

NUNCA leia títulos de caixas na tela, setas ou textos de emblemas literalmente. Os espectadores já veem "Gateway de API -> Limitador de Taxa" imediatamente. Escreva comentários de processo explicativos que declarem por que cada transição acontece, qual invariante protege o sistema e o que causa a rejeição ou recuperação.

Fluxo de Trabalho em Duas Passagens com Batida Primeiro

  1. Escreva narration_manifest.json separando o texto limpo da legenda das substituições fonéticas (tts_phonetic_text ou phonetic_glossary) para que acrônimos técnicos (WAL, gRPC, TTL, p99, HTTP 429) sejam pronunciados naturalmente sem poluir as legendas .vtt:
{
  "lead_in_s": 0.20,
  "inter_beat_gap_s": 0.25,
  "phonetic_glossary": {
    "WAL": "write-ahead log",
    "TTL": "T T L",
    "HTTP 429": "H T T P four twenty-nine"
  },
  "scenes": [
    {
      "id": "act1",
      "beats": [
        {
          "id": "act1_ingress",
          "min_visual_s": 3.2,
          "text": "Inbound writes terminate at the gateway before touching stateful storage."
        },
        {
          "id": "act1_bucket",
          "min_visual_s": 3.8,
          "text": "An atomic token-bucket check absorbs burst traffic while bounding database concurrency."
        },
        {
          "id": "act1_reject",
          "min_visual_s": 3.5,
          "text": "When burst tokens drain to zero, excess callers fail fast with HTTP 429."
        }
      ]
    }
  ]
}
  1. Passo 1 — Sintetize WAVs por Batida e Emita Tempos Exatos:
# Opção A: TTS neural Piper ONNX offline (zero chamadas de rede)
python3 .agents/skills/manim-video-explainer/mux_narration.py \
  --manifest tmp/narration_manifest.json \
  --scene-id act1 \
  --wav-dir cache/beats_en \
  --piper-bin ./venv/bin/piper \
  --model ~/.cache/piper-voices/en_US-lessac-medium.onnx \
  --emit-timings tmp/beat_timings.json

# Opção B: TTS neural HTTP compatível com OpenAI (/v1/audio/speech, stdlib urllib)
python3 .agents/skills/manim-video-explainer/mux_narration.py \
  --manifest tmp/narration_manifest.json \
  --scene-id act1 \
  --wav-dir cache/beats_en \
  --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 "Calm, clear technical documentary narrator; brief pauses at clauses." \
  --emit-timings tmp/beat_timings.json

(Qualquer sintetizador CLI personalizado também pode ser usado via --tts-cmd "my-tts --out {wav} --text {text}", ou arquivos .wav pré-renderizados podem ser colocados diretamente em --wav-dir.)

  1. Renderize o Vídeo Manim 1080p60 Orientado por tmp/beat_timings.json:
./venv/bin/manim -qh --fps 60 scenes.py Act1RateLimiter
  1. Passo 2 — Montagem da Faixa Mestre Precisão de Amostra, Normalização de Loudness, Proteção de Cauda e Multiplexação AAC:
python3 .agents/skills/manim-video-explainer/mux_narration.py \
  --manifest tmp/narration_manifest.json \
  --scene-id act1 \
  --wav-dir cache/beats_en \
  --video media/videos/scenes/1080p60/Act1RateLimiter.mp4 \
  --loudnorm-lufs -18.0 \
  --output output/act1_en.mp4 \
  --vtt output/act1_en.vtt

mux_narration.py coloca cada WAV de batida em round(start_s * sample_rate) em uma faixa mestre PCM de 16 bits, estende o quadro de vídeo final via tpad=stop_mode=clone sempre que a fala final excede a duração do vídeo (zero palavras cortadas), opcionalmente aplica normalização de loudness EBU R128 (--loudnorm-lufs -18.0) para que o volume permaneça consistente entre os capítulos, codifica -c:a aac -b:a 160k -movflags +faststart e escreve output/act1_en.vtt.

Leia references/voiceover-and-commentary.md para transformações de comentários Ruim vs Bom, substituições fonéticas para outros idiomas e fluxos de trabalho de áudio bilíngues.


4. Codificação Web, Concatenação de Várias Cenas e Player HTML5 Offline

Codificação Compatível com a Web e Concatenação de Reel Mestre

Para walkthroughs silenciosos (sem mux_narration.py), garanta compatibilidade universal com navegador/QuickTime (yuv420p + átomo moov movido):

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

Para montar vários MP4s de atos narrados em um reel mestre contínuo:

printf "file 'act1_en.mp4'\nfile 'act2_en.mp4'\nfile 'act3_en.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_en.mp4

Player HTML5 de Cinema Autônomo Opcional

Ao entregar um walkthrough de vários capítulos ou um pacote explicativo bilíngue:

  1. Copie assets/video_player_template.html para output/index.html.
  2. Personalize PLAYER_CONFIG (chapters, sources e markers correspondentes aos timestamps das batidas de beat_timings.json). Se estiver entregando em um único idioma, oculte ou omita o botão de idioma secundário em #langGroup.
  3. Mantenha todos os estilos, ícones SVG monocromáticos e scripts 100% inline para que o pacote funcione offline sem CDNs externas.
  4. Nota sobre Serviço HTTP 206 Partial Content: Os navegadores exigem respostas HTTP Byte-Range (206) para percorrer as linhas do tempo <video> e carregar arquivos <track> .vtt corretamente (file:// bloqueia faixas WebVTT via CORS, e o http.server básico do Python não suporta intervalos de bytes). Sirva o pacote output/ com um servidor que suporte intervalos de bytes (ex.: npx serve output/, python3 -m http.server não funciona. O script lint.py falhará se detectar dependências CDN ou emojis inline no player.

5. Verificação de Pacote e Garantia de Qualidade (lint.py)

Execute as seguintes verificações antes de enviar um pacote explicativo:

python3 .agents/skills/manim-video-explainer/lint.py \
  --mp4 output/act1_en.mp4 \
  --vtt output/act1_en.vtt \
  --html output/index.html

lint.py inspeciona:

  • Fluxos de vídeo/áudio ffprobe: codec H.264 (yuv420p), taxa de bits AAC (>= 128k), sem fluxos de dados, átomo moov movido (+faststart).
  • Monotonicidade WebVTT: marcas temporais de cue estritamente crescentes, sem sobreposições, sem linhas em branco perdidas entre blocos cue.
  • Invariantes do player HTML5: zero URLs CDN (cdn., cloudfront, unpkg, jsdelivr, googleapis), zero caracteres emoji inline em elemento não <code>, marcação CSP incluída.

Saída de verificação de amostra:

✓ act1_en.mp4          H.264 yuv420p  @ 1080p60   moov OK
✓ act1_en.vtt          7 cues monotônicos
✓ index.html           CSP OK   zero CDN   zero emoji
Passou em todas as verificações sem erros.

6. Exemplo de Fluxo de Trabalho Completo (3 Atos Narrados)

# 1. (Crie scenes.py com 3 classes de Ato e o manifesto de narração)
# 2. Sintetize narração e emita tempos de batida
python3 .agents/skills/manim-video-explainer/mux_narration.py \
  --manifest tmp/narration_manifest.json --scene-id act1 \
  --wav-dir cache/beats_act1 --piper-bin ./venv/bin/piper \
  --model ~/.cache/piper-voices/en_US-lessac-medium.onnx \
  --emit-timings tmp/beat_timings_act1.json
# (repita para act2, act3)

# 3. Renderize vídeos Manim 1080p60
./venv/bin/manim -qh --fps 60 scenes.py Act1RateLimiter Act2Cache Act3Recovery

# 4. Multiplexar áudio + vídeo + legendas para cada ato
for act in act1 act2 act3; do
  python3 .agents/skills/manim-video-explainer/mux_narration.py \
    --manifest tmp/narration_manifest.json --scene-id $act \
    --wav-dir cache/beats_$act \
    --video media/videos/scenes/1080p60/${act^}.mp4 \
    --loudnorm-lufs -18.0 --output output/${act}_en.mp4 \
    --vtt output/${act}_en.vtt
done

# 5. Concatene atos em um reel mestre e gere player HTML5 de capítulos
printf "file 'act1_en.mp4'\nfile 'act2_en.mp4'\nfile 'act3_en.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/multi_act_master.mp4
cp assets/video_player_template.html output/index.html
# Edite output/index.html PLAYER_CONFIG com metadados de capítulo

# 6. Verifique o pacote final
python3 .agents/skills/manim-video-explainer/lint.py \
  --mp4 output/multi_act_master.mp4 --html output/index.html

# 7. Sirva (requer servidor HTTP compatível com intervalos de bytes)
npx serve output/

Problemas Comuns e Soluções

ProblemaSintoma / VerificaçãoSolução
ModuleNotFoundError: No module named 'manim'Falha de importação do Manim no ambienteCertifique-se de que uv pip install --python venv/bin/python manim foi executado e que ./venv/bin/python está sendo usado para chamar manim
Erro de sintetizador TTS (piper não encontrado)mux_narration.py falha no passo de sínteseInstale piper-tts no venv (pip install piper-tts) ou use --http-tts-url com uma chave de API válida
Legendas .vtt dessincronizadasAs palavras aparecem antes/depois das animações correspondentesExecute mux_narration.py com --emit-timings ANTES de renderizar o vídeo e passe o mesmo arquivo de tempos para timer.hold() no código da cena
Quadros de vídeo cortam a fala finalA última palavra é cortada abruptamentemux_narration.py gerencia isso automaticamente com tpad=stop_mode=clone; verifique se --wav-dir contém WAVs de batida completos
lint.py relata falha de CSP/CDNO HTML do player contém URLs de CDN ou scripts inlineUse o modelo video_player_template.html fornecido; não adicione CDNs; todos os ativos devem estar inline

Fluxo de Trabalho de Desenvolvimento e Ciclo de Feedback

  1. Storyboard offline: Escreva uma tabela de batida por batida com visual_action / min_visual_sec / commentary_script em um arquivo .md.
  2. Esboço de layout (segundos): Implemente a cena com esboços de caixa e documentos dump_checkpoints com check_scene_bounds.py sem renderização completa.
  3. Pré-visualização -ql: Execute manim -ql --fps 15 scenes.py MinhaCena para um MP4 de resolução média de 15 fps em < 30s para verificar a cadência da animação.
  4. Renderização -qh final: Execute manim -qh --fps 60 para MP4 1080p60 para o ato final.
  5. Sincronização de narração: Passe 1 para TTS, renderize vídeo, passe 2 para multiplexação.

Leia references/manim-layout-and-choreography.md e references/voiceover-and-commentary.md antes de escrever storyboards e código de cena para evitar retrabalho.


Histórico de Mudanças

  • v1.0 — Lançamento inicial: conectores ortogonais, narração Piper/OpenAI, normalização de loudness EBU R128, legendas WebVTT, verificação de layout, player HTML5 autônomo.

Habilidades Relacionadas