Communitygithub.com

Manim 1080p60 기술 영상 설명 & 동기화 음성 해설 시스템

LaTeX 없이 Manim Community로 1080p60 고화질 기술 설명 영상, 단계별 시스템 워크스루, 다중 액트 아키텍처 애니메이션을 렌더링합니다. 선택적으로 비트 동기화된 신경망 TTS 음성 해설(Piper 오프라인, OpenAI 호환 HTTP TTS, CLI TTS), EBU R128 음량 정규화 및 WebVTT 자막, 독립형 HTML5 챕터 플레이어를 지원합니다.

Manim 1080p60 기술 영상 설명 & 동기화 음성 해설 시스템란 무엇인가요?

이 스킬은 복잡한 시스템 아키텍처, 데이터 흐름, 또는 프로세스를 시각적으로 설명하는 고품질 애니메이션 비디오를 제작하는 기술 전문가와 AI 에이전트를 위해 설계되었습니다. LaTeX가 필요 없는 Manim Community 라이브러리를 활용하여 1920x1080 해상도, 초당 60프레임의 깔끔하고 결정론적인 애니메이션을 Python 코드로 직접 생성합니다. 사용자가 '애니메이션 비디오 워크스루', 'Manim 아키텍처 애니메이션', '내레이션이 포함된 비디오 튜토리얼', 또는 '단계별 시각적 설명'을 요청할 때 사용하세요. Claude Code, Cursor, Codex 등 Manim을 실행할 수 있는 모든 AI 코딩 에이전트에서 작동합니다. 이 스킬의 핵심은 엄격한 LaTeX-Free 원칙과 포트 앵커 기반 직교 도식 커넥터(orthogonal schematic connectors)에 있습니다. 일반 Manim의 `Arrow()`가 상자 중심 간 대각선 화살표를 생성하는 문제를 해결하기 위해, 이 스킬은 `connect_h`, `connect_v`, `connect_elbow`와 같은 함수를 제공하여 항상 직각으로 꺾이는 깔끔한 와이어를 그리도록 강제합니다. 또한 비트 동기화 음성 해설 시스템(`mux_narration.py`)을 통해 각 애니메이션 단계(비트)에 샘플 정확도의 음성 해설(WAV)을 합성하고, EBU R128 표준으로 음량을 정규화하며, WebVTT 자막(.vtt)을 생성합니다. 최종 결과물은 독립형 HTML5 플레이어로 제공되어 오프라인에서도 챕터 탐색과 자막 전환이 가능한 완전한 패키지를 제공합니다.

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

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

Manim 1080p60 기술 비디오 설명 & 동기화 음성 해설 시스템

개요 및 번들 리소스

pdflatex나 MacTeX 없이 Manim Community를 사용하여 순수 Python으로 선명하고 결정론적인 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-Free 프리미티브(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: 표준 라이브러리 기반 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 코드를 작성하기 전에 설명을 개별 액트와 비트별 스토리보드 테이블로 구조화하세요. 액트당 하나의 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 토폴로지 카드 + 헤더 렌더링; Gateway로 요청 패킷 애니메이션3.2인바운드 쓰기는 상태 저장소에 닿기 전에 엣지 게이트웨이를 통과합니다.
act1_bucket토큰 카운터 100 -> 99 감소; 녹색 ADMITTED 배지 깜빡임3.8원자적 토큰 버킷 검사는 다운스트림 동시성을 제한하면서 트래픽 버스트를 흡수합니다.
act1_reject토큰이 0이 되었을 때 animate_blocked_pulse (429 RATE LIMITED) 트리거3.5버스트 용량이 소진되면 초과 호출자는 무한 대기 대신 HTTP 429로 빠르게 실패합니다.

2. LaTeX-Free Manim Community 저작

격리된 환경 설정

격리된 가상 환경 내에서 Manim (및 선택적 piper-tts)을 설치하고 ffmpeg와 ffprobe가 PATH에 있는지 확인하세요:

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

엄격한 LaTeX-Free 및 직교 도식 규칙

  1. Zero TeX 하위 프로세스: Tex, MathTex, Title 또는 DecimalNumber (pdflatex/dvisvgm 호출)를 절대 사용하지 마세요. Text, MarkupText (<b>, <i>, <sub>, <sup>, <span foreground="...">), Code, RoundedRectangle, Arrow, DashedVMobject, MoveAlongPath를 사용하세요.
  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"를 즉시 봅니다. 각 전환이 왜 발생하는지, 어떤 불변 조건이 시스템을 보호하는지, 거절 또는 복구의 원인이 무엇인지 설명하는 설명적인 프로세스 코멘터리를 작성하세요.

비트-퍼스트 투-패스 워크플로우

  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, 표준 라이브러리 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를 round(start_s * sample_rate) 위치의 16비트 PCM 마스터 트랙에 배치하고, 최종 음성이 비디오 지속 시간을 초과할 때마다 tpad=stop_mode=clone을 통해 최종 비디오 프레임을 확장하며(단어 잘림 없음), 선택적으로 EBU R128 음량 정규화(--loudnorm-lufs -18.0)를 적용하여 챕터 간 볼륨 일관성을 유지하고, -c:a aac -b:a 160k -movflags +faststart로 인코딩하며, output/act1_en.vtt를 작성합니다.

Bad-vs-Good 코멘터리 변환, 비영어 발음 오버라이드, 이중 언어 오디오 워크플로우는 references/voiceover-and-commentary.md를 참조하세요.


4. 웹 인코딩, 다중 장면 연결 및 오프라인 HTML5 플레이어

웹 호환 인코딩 및 마스터 릴 연결

무음 워크스루의 경우 (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 트랙을 차단하며, Python의 기본 http.server는 바이트-레인지를 지원하지 않습니다 — python3 -m http.server 8080과 같은 적절한 HTTP 서버를 사용하세요).

5. 린트 및 검증

출력 패키지를 제공하기 전에 항상 lint.py를 실행하세요:

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

lint.py는 다음을 확인합니다:

  • 비디오 스트림: H.264 메인/하이 프로파일, yuv420p, 1920x1080.
  • 오디오 스트림: AAC-LC, 존재 여부.
  • WebVTT: 올바른 WEBVTT 헤더, 단조로운 00:00:00.000 --> 00:00:03.200 큐 타임라인.
  • HTML5 플레이어: CDN URL 없음(/cdn/, https://, http:// 없음), 이모지 없음, 인라인 style/script 태그, 콘텐츠 보안 정책 호환. 에러는 stderr로, 패스는 stdout으로 출력합니다.

6. 저장 및 ".gitignore" 고려 사항

  • tmp/ 디렉토리: gitignore에 추가하세요. mux_narration.py가 비트를 해시하는 cache/ 디렉토리도 마찬가지입니다.
  • media/ 디렉토리: Manim의 기본 비디오/부분 렌더 출력. 일반적으로 gitignore에 추가합니다.
  • output/ 디렉토리: 최종 MP4, VTT, HTML5 플레이어. git lfs로 추적하거나 배포 아티팩트로 간주합니다.
  • scenes.py 및 narration_manifest.json: Git에 커밋합니다. 이것들은 재현 가능한 소스입니다.

일반적인 워크플로우 요약

  1. 스토리보드: 설명을 액트와 비트로 나눕니다. narration_manifest.json을 작성합니다.
  2. Pass 1 (오디오): TTS를 사용하여 비트 WAV를 합성합니다. beat_timings.json을 생성합니다.
  3. 코드: nord_manim_theme.py의 프리미티브를 사용하여 Manim Scene을 작성합니다. 경계를 확인합니다 (check_scene_bounds.py).
  4. 렌더: Manim을 -qh --fps 60으로 실행합니다.
  5. Pass 2 (먹싱): mux_narration.py로 오디오, 비디오, 자막을 결합합니다. 필요시 음량 정규화(--loudnorm-lufs)를 적용합니다.
  6. 연결: 여러 액트를 최종 마스터 릴로 결합합니다.
  7. 패키징: HTML5 플레이어 템플릿을 복사하고 PLAYER_CONFIG를 링크합니다.
  8. 린트: lint.py로 출력의 완전성과 호환성을 확인합니다.

관련 스킬