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çãoBeatTimer, 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/speechcompatível com OpenAI ou um comando TTS personalizado, emitebeat_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.264yuv420pdoffprobe, 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 comffmpeg.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 doBanco 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 429ou elevação de quorumBloqueio Distribuído e Replicação de Log Write-Ahead).
Mapeie cada batida explicitamente antes de codificar:
beat_id | visual_action | min_visual_sec | commentary_script (se narrado) |
|---|---|---|---|
act1_ingress | Renderizar 4 cartões de topologia Nord + cabeçalho; animar pacote de solicitação para o Gateway | 3.2 | Escritas de entrada passam por um gateway de borda antes de tocar no armazenamento com estado. |
act1_bucket | Decrementar contador de tokens 100 -> 99; piscar emblema verde ADMITIDO | 3.8 | Uma verificação atômica de token-bucket absorve rajadas de tráfego enquanto limita a concorrência downstream. |
act1_reject | Acionar animate_blocked_pulse (429 LIMITADO POR TAXA) quando os tokens chegarem a 0 | 3.5 | Quando 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
- Zero Subprocessos TeX: Nunca use
Tex,MathTex,TitleouDecimalNumber(que invocampdflatex/dvisvgm). UseText,MarkupText(<b>,<i>,<sub>,<sup>,<span foreground="...">),Code,RoundedRectangle,Arrow,DashedVMobjecteMoveAlongPath. - Regra de Ouro Esquemática (Fios Ortogonais Ancorados por Porta): NUNCA chame
Arrow(card_a, card_b)diretamente em doisMobjects 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 viaconnect_h(card_from, card_to, y=...),connect_v(card_from, card_to, x=...)ouconnect_elbow(points), anime pacotes comanimate_packet_along(self, edge, ...)e valide a geometria dos fios comassert_all_orthogonal(connectors, cards). - 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 mantenhay < -3.05vazio sempre que as legendas WebVTT forem exibidas. - Importe
assets/nord_manim_theme.py: Definaself.camera.background_color = BG_DARKe 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
- Escreva
narration_manifest.jsonseparando otextolimpo da legenda das substituições fonéticas (tts_phonetic_textouphonetic_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."
}
]
}
]
}
- 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.)
- Renderize o Vídeo Manim 1080p60 Orientado por
tmp/beat_timings.json:
./venv/bin/manim -qh --fps 60 scenes.py Act1RateLimiter
- 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:
- Copie
assets/video_player_template.htmlparaoutput/index.html. - Personalize
PLAYER_CONFIG(chapters,sourcesemarkerscorrespondentes aos timestamps das batidas debeat_timings.json). Se estiver entregando em um único idioma, oculte ou omita o botão de idioma secundário em#langGroup. - Mantenha todos os estilos, ícones SVG monocromáticos e scripts 100% inline para que o pacote funcione offline sem CDNs externas.
- 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>.vttcorretamente (file://bloqueia faixas WebVTT via CORS, e ohttp.serverbásico do Python não suporta intervalos de bytes). Sirva o pacoteoutput/com um servidor que suporte intervalos de bytes (ex.:npx serve output/,python3 -m http.servernão funciona. O scriptlint.pyfalhará 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, átomomoovmovido (+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
| Problema | Sintoma / Verificação | Solução |
|---|---|---|
ModuleNotFoundError: No module named 'manim' | Falha de importação do Manim no ambiente | Certifique-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íntese | Instale piper-tts no venv (pip install piper-tts) ou use --http-tts-url com uma chave de API válida |
Legendas .vtt dessincronizadas | As palavras aparecem antes/depois das animações correspondentes | Execute 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 final | A última palavra é cortada abruptamente | mux_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/CDN | O HTML do player contém URLs de CDN ou scripts inline | Use 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
- Storyboard offline: Escreva uma tabela de batida por batida com
visual_action/min_visual_sec/commentary_scriptem um arquivo.md. - Esboço de layout (segundos): Implemente a cena com esboços de caixa e documentos
dump_checkpointscomcheck_scene_bounds.pysem renderização completa. - Pré-visualização -ql: Execute
manim -ql --fps 15 scenes.py MinhaCenapara um MP4 de resolução média de 15 fps em < 30s para verificar a cadência da animação. - Renderização -qh final: Execute
manim -qh --fps 60para MP4 1080p60 para o ato final. - 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.