Communitygithub.com

manim-video-explainer

高解像度1080p60の技術解説ビデオ、ステップバイステップのシステムウォークスルー、マルチアクトのアーキテクチャアニメーションをManim Community(LaTeX不要)でレンダリングします。ポートアンカー直交模式図コネクタ、オプションのビート同期ニューラルTTSナレーション(オフラインPiper、OpenAI互換/v1/audio/speech HTTP TTS、またはCLI TTS)とEBU R128ラウドネス正規化、WebVTTキャプション、およびオプションのスタンドアロンHTML5チャプタープレーヤーを備えています。

manim-video-explainer とは?

このスキルは、Manim Communityライブラリを使用して、高品質な1920x1080 60fpsの技術解説ビデオを生成するための包括的なツールです。pdflatexやMacTeXを必要とせず、純粋なPythonで動作するため、環境構築が容易です。主な特徴として、(1) LaTeXフリーのテキストレンダリング(Text, MarkupText等)、(2) ポートアンカー方式の直交コネクタによる正確な模式図描画、(3) オフラインPiper ONNXやOpenAI互換のHTTP TTSを使ったビート同期ナレーション、(4) EBU R128規格に準拠したラウドネス正規化とWebVTT字幕生成、(5) スタンドアロンHTML5プレイヤーによるオフライン再生、が含まれます。このスキルは、システムアーキテクチャの説明、プロトコルフローのアニメーション、段階的なチュートリアルビデオなどに最適です。使用時は、まずストーリーボードとビートテーブルを設計し、各シーンをActごとに分割してレンダリング効率を高めます。ナレーションが必要な場合は、narration_manifest.jsonを作成し、mux_narration.pyを使って音声合成と動画へのマルチプレクスを行います。

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

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Manim 1080p60 技術解説ビデオ&同期ナレーションシステム

概要とバンドルリソース

pdflatexやMacTeXを必要とせず、純粋なPythonとManim Communityを使用して、高精細で決定論的な 1920x1080 @ 60fps の技術解説ビデオとアーキテクチャウォークスルーを作成します。 アニメーションには、オプションでサンプル精度のニューラルナレーション(オフラインPiper ONNX、OpenAI互換の /v1/audio/speech HTTPエンドポイント、またはカスタムCLI TTS)、EBU R128ラウドネス正規化、同期された .vtt WebVTT字幕、およびゼロCDNのオフラインHTML5チャプタープレーヤーを組み合わせることができます。

  • assets/nord_manim_theme.py: Nord Polar/Frost/Auroraパレットトークン、セーフフレームレイアウト定数、BeatTimer 同期ヘルパー、LaTeXフリープリミティブ(make_header_banner、make_node_card、make_badge、animate_blocked_pulse、safe_fit_text)、および厳格なポートアンカー直交模式図コネクタ(connect_h、connect_v、connect_elbow、animate_packet_along、assert_all_orthogonal)を含みます。
  • assets/video_player_template.html: スタンドアロン、ゼロCDNのNordダーク/ライトHTML5シネマプレーヤー。チャプタータブ、クリック可能なビートマーカー、再生速度(0.75x..2.0x)、およびオプションのバイリンガル音声/キャプション切り替えを備えています。
  • check_scene_bounds.py: プリレンダーレイアウト検証ツール。ManimシーンまたはJSONチェックポイントが [-6.8, 6.8] x [-3.75, 3.75] のフレームオーバーフローや兄弟テキストラベルの衝突をチェックします。
  • mux_narration.py: Stdlib Python CLI。Piper、OpenAI互換のHTTP /v1/audio/speech エンドポイント、またはカスタムTTSコマンドを使用してビートごとのWAVを合成し、beat_timings.json を出力し、サンプル精度のマスターWAVトラックを組み立て、末尾のビデオフレームをクローンパディング(tpad=stop_mode=clone)して音声が途切れないようにし、オプションでラウドネス正規化(--loudnorm-lufs -18.0)し、H.264 + AAC(+faststart)をマルチプレクスし、.vtt キャプションを書き込みます。
  • lint.py: 出力バンドル検証ツール。ffprobe によるH.264 yuv420p + 音声ストリーム、WebVTTヘッダー/キューの単調増加性、およびオフラインHTML5プレーヤーのCSP/ゼロCDN/ゼロ絵文字インバリアントをチェックします。
  • references/manim-layout-and-choreography.md: マルチノードトポロジ、直交バス/エルボールーティング、動的カウンター、双方向矢印、またはマルチシーン ffmpeg 連結を設計する際に参照してください。
  • references/voiceover-and-commentary.md: ナレーションマニフェスト、自然なナレーターの韻律指示、発音用語集、EBU R128正規化、またはバイリンガルナレーショントラックを作成する際に参照してください。

1. ストーリーボードとマルチアクトナラティブデザイン

Manimコードを書く前に、説明を個別のアクトとビート単位のストーリーボードテーブルに構造化します。 1つの Scene クラスをアクトごとに保持することで、アクト2の編集がアクト1やアクト3の再レンダリングを必要としなくなります。

  • アクト1 — セットアップとリクエスト受付: トポロジ、インバリアント、およびエントリーガードを紹介します(例:Client -> API Gateway -> Token-Bucket Rate Limiter)。
  • アクト2 — 定常状態の実行: プライマリデータパスと状態遷移をトレースします(例:Read-Through Cache シングルフライトルックアップ -> Primary DB ハイドレーション -> TTLライトバック)。
  • アクト3 — 障害モードとリカバリ: 制限に達したときやノードが障害を起こしたときの動作をデモします(例:HTTP 429 バースト拒否または Distributed Lock & Write-Ahead Log Replication クォーラム昇格)。

コーディング前にすべてのビートを明示的にマッピングします。

beat_idvisual_actionmin_visual_seccommentary_script (ナレーションありの場合)
act1_ingress4つのNordトポロジカードとヘッダーをレンダリング。リクエストパケットをゲートウェイにアニメーション。3.2受信書き込みは、ステートフルストレージに触れる前にエッジゲートウェイを通過します。
act1_bucketトークンカウンターを 100 -> 99 にデクリメント。緑色の ADMITTED バッジをフラッシュ。3.8アトミックトークンバケットチェックは、下流の並行性を制限しながらトラフィックバーストを吸収します。
act1_rejectトークンが 0 になったときに animate_blocked_pulse(429 RATE LIMITED)をトリガー。3.5バースト容量を使い果たすと、超過した呼び出し元は無限にキューイングする代わりにHTTP 429で即座に失敗します。

2. LaTeXフリーのManim Communityオーサリング

分離環境のセットアップ

Manim(およびオプションの piper-tts)を分離された仮想環境内にプロビジョニングし、ffmpeg と ffprobe が PATH 上にあることを確認します。

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

厳格なLaTeXフリーおよび直交模式図のルール

  1. ゼロTeXサブプロセス: Tex、MathTex、Title、または DecimalNumber(pdflatex/dvisvgm を呼び出す)は決して使用しないでください。代わりに Text、MarkupText(<b>、<i>、<sub>、<sup>、<span foreground="...">)、Code、RoundedRectangle、Arrow、DashedVMobject、MoveAlongPath を使用します。
  2. 黄金の模式図ルール(ポートアンカー直交配線): アーキテクチャブロック図を作成する際に、2つの Mobject に対して Arrow(card_a, card_b) を直接決して呼び出さないでください。Manimはバウンディングボックスの中心間の光線を狙い、カードの幅やY位置が異なる場合は常に歪んだ斜めの矢印を生成します。すべてのエッジは connect_h(card_from, card_to, y=...)、connect_v(card_from, card_to, x=...)、または connect_elbow(points) を介してルーティングし、animate_packet_along(self, edge, ...) でパケットをアニメーション化し、assert_all_orthogonal(connectors, cards) でワイヤージオメトリを検証します。
  3. セーフフレーム座標グリッド: Manimの16:9キャンバスは [-7.11, 7.11] x [-4.0, 4.0] に及びます。すべてのオーサリングコンテンツは [-6.6, 6.6] x [-3.5, 3.5] 内に収め、WebVTT字幕が表示される場合は常に y < -3.05 を空に保ちます。
  4. assets/nord_manim_theme.py のインポート: self.camera.background_color = BG_DARK を設定し、カード、直交コネクタ、パケットアニメーション、ビートホールドを一貫して構築します。
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(
            "Act 1 / Request Admission",
            "API Gateway & Token-Bucket Rate Limiter",
        )
        gw = make_node_card("API Gateway", "TLS + Route Match", width=2.5, accent=ACCENT_CYAN).move_to([-4.5, 0.2, 0])
        limiter = make_node_card("Rate Limiter", "Bucket: 100 tok", width=2.5, accent=ACCENT_BLUE).move_to([-1.5, 0.2, 0])
        cache = make_node_card("Read Cache", "Single-Flight TTL", width=2.5, accent=ACCENT_CYAN).move_to([1.5, 0.2, 0])
        db = make_node_card("Primary DB", "WAL Quorum", 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 RATE LIMITED", 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")

1080p60レンダリング前に境界と衝突ゼロを確認

フル 1080p60 レンダリングを開始する前に、レイアウトジオメトリを数秒で必ず確認します。

# オプション A: すべての Scene.wait() 呼び出しに対するライブドライランフック(< 3秒、MP4エンコードなし)
./venv/bin/python .agents/skills/manim-video-explainer/check_scene_bounds.py \
  --module scenes.py --scene Act1RateLimiter

# オプション B: 高速 -ql レンダリング後に出力されたチェックポイントJSONを検査
python3 .agents/skills/manim-video-explainer/check_scene_bounds.py \
  --json tmp/act1_bounds.json

マルチティア座標テンプレート、動的な ValueTracker カウンター、および衝突防止クリーンアップルールについては、references/manim-layout-and-choreography.md を参照してください。


3. オプションのビート同期ナレーション解説(mux_narration.py)

黄金のナレーションルール

画面上のボックスタイトル、矢印、バッジ文字列をそのまま読み上げないでください。 視聴者は "API Gateway -> Rate Limiter" を即座に目にします。各遷移が なぜ 発生するのか、どのような不変条件 がシステムを保護するのか、そして 何が 拒否または回復を引き起こすのかを説明する解説的なプロセスコメントを書きましょう。

ビートファーストの2パスワークフロー

  1. narration_manifest.json を書く: クリーンな字幕 text と発音オーバーライド(tts_phonetic_text または phonetic_glossary)を分離します。これにより、技術的な頭字語(WAL、gRPC、TTL、p99、HTTP 429)が自然に発音され、.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. パス1 — ビートごとのWAVを合成し、正確なタイミングを出力:
# オプション A: オフラインPiper ONNXニューラルTTS(ネットワーク呼び出しゼロ)
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

# オプション B: OpenAI互換HTTPニューラルTTS (/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

(カスタムCLIシンセサイザーは --tts-cmd "my-tts --out {wav} --text {text}" で使用することも、事前レンダリングされた .wav ファイルを直接 --wav-dir に配置することもできます。)

  1. tmp/beat_timings.json に基づいて1080p60 Manimビデオをレンダリング:
./venv/bin/manim -qh --fps 60 scenes.py Act1RateLimiter
  1. パス2 — サンプル精度のマスタートラックアセンブリ、ラウドネス正規化、テールガード、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 は、各ビートWAVを16ビットPCMマスタートラックの round(start_s * sample_rate) に配置し、最終的な音声がビデオ時間を超える場合は tpad=stop_mode=clone で最終ビデオフレームを拡張し(単語の途切れゼロ)、オプションでEBU R128ラウドネス正規化(--loudnorm-lufs -18.0)を適用してチャプター間で音量を一定に保ち、-c:a aac -b:a 160k -movflags +faststart でエンコードし、output/act1_en.vtt を書き込みます。

不適切なコメントと適切なコメントの変換例、非英語発音オーバーライド、およびバイリンガル音声ワークフローについては、references/voiceover-and-commentary.md を参照してください。


4. Webエンコーディング、マルチシーン連結、およびオフラインHTML5プレーヤー

Web互換エンコーディングとマスターリール連結

サイレントウォークスルー(mux_narration.py なし)の場合、ユニバーサルなブラウザ/QuickTime互換性(yuv420p + 移動された moov アトム)を確保します。

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

複数のナレーション付きアクトMP4を連続したマスターリールにまとめるには、以下を実行します。

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

オプションのスタンドアロンHTML5シネマプレーヤー

マルチチャプターのウォークスルーやバイリンガル解説パッケージを配信する場合:

  1. assets/video_player_template.html を output/index.html にコピーします。
  2. PLAYER_CONFIG(chapters、sources、および beat_timings.json のビートタイムスタンプに一致する markers)をカスタマイズします。単一言語で配信する場合は、#langGroup のセカンダリ言語ボタンを非表示にするか省略します。
  3. すべてのスタイル、モノクロSVGアイコン、スクリプトが100%インラインになるようにして、バンドルが外部CDNなしでオフラインで動作するようにします。
  4. HTTP 206 Partial Content 提供に関する注意: ブラウザが <video> タイムラインをシークしたり <track> .vtt ファイルを正常に読み込んだりするには、HTTPバイトレンジ(206)レスポンスが必要です(file:// はCORSを介してWebVTTトラックをブロックします)。output/ ディレクトリは、バイトレンジをサポートする任意のHTTPサーバー(nginx、Caddy、Node.js http-server、Python http.server は不完全)で提供するか、統合するアプリケーションに静的ファイルとしてバンドルしてください。

5. 検証とCIパイプライン

バンドル検証(lint.py)

チェックするには以下を実行:

python3 .agents/skills/manim-video-explainer/lint.py --bundle output/

これは以下を検証します:

  • ffprobe を使用したH.264 yuv420p ビデオストリームの存在。
  • AACまたはオプションのマルチプレクスされた音声ストリーム。
  • WebVTTヘッダーとキューのタイムスタンプが単調増加であること。
  • オンラインリソース(CDN、フォント、トラッカー)への参照がないこと。
  • オフラインモードで壊れる可能性のある絵文字やその他のUnicode文字が含まれていないこと。
  • 出力ディレクトリへの安全なパス。

CI用クイックサニティチェック

完全なレンダリング前にジオメトリを確認するには、-ql(低品質、854x480 @ 15fps)Manimプリセットを使用して、mux_narration.py と check_scene_bounds.py のロジックを検証します。

./venv/bin/manim -ql scenes.py Act1RateLimiter
./venv/bin/python .agents/skills/manim-video-explainer/check_scene_bounds.py --json tmp/act1_bounds.json

ルール違反の自動修正

直交コネクタが assert_all_orthogonal の検証に失敗した場合、違反したエッジは再ルーティングが必要です。connect_h または connect_v を使用し、開始/終了ノードのポートのY/X位置が一致していることを確認し、複雑な配線パスには connect_elbow を使用してください。


トラブルシューティングガイド

LaTeXの欠落エラー

RuntimeError: Latex error converting to dvi. See log file.

原因: Tex または MathTex が誤って使用され、システムにpdflatexがインストールされていません。 修正: すべての Tex() / MathTex() 呼び出しを Text() または MarkupText() に置き換え、DecimalNumber の使用を確認してください。

斜め矢印 / 直交違反

症状: connect_h/connect_v の代わりに Arrow(card_a, card_b) が使用された場合、Manimはボックスセンター間に斜めの矢印を生成します。 修正: assert_all_orthogonal(connectors, cards) を呼び出してすべてのワイヤーを検証し、違反したエッジを connect_h、connect_v、または connect_elbow を使用するように書き換えてください。

音声の途切れ / クリッピング

症状: 最終的なナレーション付き動画で最後の単語が途切れる。 修正: mux_narration.py の --loudnorm-lufs パラメータが設定されていることを確認し、最終的な音声が動画時間を超える場合は、mux_narration.py が tpad フィルタを自動的に追加することを確認してください。narration_manifest.json の min_visual_s 値を見直してください。

WebVTTが表示されない(file://)

症状: HTML5プレーヤーを file:// で開いた場合、字幕トラックが読み込まれない。 修正: ブラウザのCORSポリシーにより、file:// からのWebVTT読み込みがブロックされます。ファイルはローカルのHTTPサーバー(npx http-server output/)またはプロダクション環境で提供してください。

レンダリングが遅い

  • フル 1080p60 出力の前に -ql フラグを使用してプレビューします。
  • ValueTracker カウンターのフレームレートを制限します。毎フレーム更新する必要はありません。
  • 大きなテクスチャがある場合は、--write_to_movie の代わりに --format=png を検討してください。

追記事項

環境変数

変数名説明デフォルト
OPENAI_API_KEY--http-tts-key-env で使用するOpenAI APIキーなし
PIPER_VOICE_CACHEPiper ONNXモデルのキャッシュディレクトリ~/.cache/piper-voices/

ファイルパス

  • コアアセット: .agents/skills/manim-video-explainer/assets/
  • 参照ドキュメント: .agents/skills/manim-video-explainer/references/
  • 出力ディレクトリ: output/(mux_narration.py によって作成されます)
  • 一時ファイル: tmp/(beat_timings.json、チェックポイント)

関連スキル