Communitygithub.com

igm61799-cloud/음성-애니메이션

내레이션 오디오 파일을 전체 코딩된 2D 애니메이션 동영상(MP4)으로 변환합니다. 비디오 생성 모델이나 스톡 영상 없이, 모든 프레임을 Canvas 2D로 그려서 단어 타이밍에 맞춰 자르고, 헤드리스 Chromium과 ffmpeg로 렌더링합니다.

음성-애니메이션란 무엇인가요?

이 스킬은 음성 파일(내레이션, 팟캐스트 클립 등)을 입력받아 완전히 코딩된 2D 애니메이션 영화로 제작합니다. 소요되는 모든 프레임은 Canvas 2D로 코드에서 직접 그려지며, Faster-Whisper를 이용한 단어 단위 타임스탬프를 통해 영상 컷과 비주얼 비트가 정확한 단어에 맞춰집니다. 전체 파이프라인은 결정론적(deterministic)으로 각 프레임을 독립적으로 렌더링할 수 있어, 병렬 처리와 완벽한 오디오 싱크가 가능합니다. 사용자는 오디오 파일과 대본을 제공하면, AI가 스토리보드 작성, 장면 코딩, 렌더링까지 수행해 완성된 MP4를 얻을 수 있습니다. Claude Code, Cursor, Codex 등 명령줄 기반 AI 어시스턴트에서 실행할 수 있으며, 비디오 생성 모델 없이 2D 코드 애니메이션을 통해 'Kurzgesagt 스타일'의 설명 영상, 모션 그래픽, 애니메이션 단편을 제작하는 데 특화되어 있습니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/igm61799-cloud/voiceover-to-animation/tree/HEAD

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

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

문서

음성 -> 코딩된 2D 애니메이션 단편

당신은 슬라이드쇼가 아닌 단편 영화를 만듭니다. 내레이션은 고정되어 있습니다; 당신의 임무는 매 순간 시청자가 보는 것을 창조하고, 코드로 그려내며, 단어에 맞춰 컷과 비주얼 비트가 정확히 떨어지는 MP4를 제공하는 것입니다.

전체 파이프라인은 결정론적입니다: 각 장면은 시간에 대한 순수 함수이므로 모든 프레임을 독립적으로 렌더링할 수 있습니다. 이 하나의 특성이 다른 모든 것(즉시 검토용 스틸 이미지, 병렬 렌더링, 완벽한 오디오 싱크)을 가능하게 합니다.

시스템 요구사항

시작하기 전에 확인하세요; 이것들이 유일하게 필요한 요소입니다.

python -c "import faster_whisper, playwright, PIL; print('ok')"
ffmpeg -version

없는 구성요소: pip install faster-whisper playwright pillow, 그 다음 python -m playwright install chromium. ffmpeg는 PATH에 있어야 합니다. 설치할 수 없는 것이 있다면, 약한 접근법으로 전환하지 말고 무엇이 누락되었는지 사용자에게 알려주세요.

작업 흐름

사용자 관점을 단순하게 유지하세요: 사용자가 오디오를 주고 영화를 원합니다. 다음 단계를 순서대로 수행하고, 눈에 보이는 작업 목록을 유지하세요.

1. 먼저 듣기: 단어 타이밍

python <skill>/scripts/transcribe.py voiceover.mp3 work

시작/종료 시간이 있는 문장들과 모든 단어를 word@start 형식으로 출력하고 work/words.json에 저장합니다. 이후 모든 작업은 이 숫자들을 기준으로 계획됩니다. 전체 길이(duration)를 기록해 두세요.

2. 오디오 옆에 프로젝트 설정

<skill>/assets/template/을 작업 폴더에 복사하여 다음과 같이 구성되도록 하세요:

voiceover.mp3
render.py
src/index.html  src/engine.js  src/scenes.js  src/main.js

src/engine.js를 한 번 읽고(짧습니다) references/engine-api.md를 훑어보면, 이미 가지고 있는 기능들(이징, 시드 기반 난수, 카메라, 부분 경로 그리기, 판화 스타일 모티프(구름, 비, 번개, 연기, 스카이라인))을 알 수 있습니다.

3. 아트 방향 결정 후 단어에 맞춰 스토리보드 작성

영화 전체에 대해 하나의 시각적 언어를 선택하고 유지하세요: 작은 팔레트(5~6가지 색상), 가장자리와 텍스처 처리 방식 하나, 인물 그리는 방식 하나. 템플릿은 기본적으로 리노컷/판화 스타일(본지(뼈 종이), 네이비 잉크, 버밀리언, 골드, 흔들리는 손으로 자른 가장자리)을 제공합니다. 이를 그대로 사용하거나 scenes.js의 팔레트와 POST 스위치를 변경하여 스타일을 재설정할 수 있습니다. 단, 장면 간에 스타일을 혼합하지 마세요. 다양성은 장면마다 어떤 색상이 지배적인지(밤 장면, 종이 장면, 빨간 장면)에서 오는 것이지 규칙을 바꾸는 데서 오는 것이 아닙니다.

그런 다음 코드를 작성하기 전에 스토리보드를 작성하세요: 각 샷의 절대 시작/종료 시간, 해당 샷이 다루는 단어, 화면에 보이는 것, 각 비트가 떨어지는 정확한 단어로 구성된 표입니다. references/storyboarding.md를 읽고 내레이션을 이미지로 변환하는 방법을 익히세요. 이것이 영화의 질을 결정하는 부분입니다.

요약:

  • 구절당 하나의 아이디어. 세 개의 절로 된 문장은 보통 세 개의 샷 또는 한 샷 내의 세 개의 비트입니다.
  • 대상을 보여주고, 자막으로 설명하지 마세요. 내레이션을 반복하는 화면 텍스트는 시청자가 보는 대신 읽게 만듭니다. 기본적으로 텍스트는 없습니다; 이름, 실제 데이터, 차트의 짧은 중립 레이블이 예외입니다. 사용자가 텍스트 선호도를 명시했다면 그것이 우선합니다.
  • 영화에 줄기를 주세요: 반복되는 캐릭터나 모티프, 그리고 시작 부분의 무언가를 마지막에 다시 호출하는 것.
  • 스케일과 카메라를 의도적으로 변경하세요: 와이드, 클로즈업, 다이어그램, 지도, 단면도. 두 개의 연속된 샷이 동일한 구성을 가져서는 안 됩니다.
  • 내레이션이 포인트(펀치라인)로 끝난다면, 마지막 단어 후에 1~2초의 무음 꼬리를 추가하여 마지막 이미지가 여운을 남길 수 있도록 하세요. render.py가 오디오를 패딩합니다.

4. 나머지를 작성하기 전에 처음부터 끝까지 하나의 장면 제작

첫 번째 샷을 src/scenes.js에 작성하고 SHOTS에 등록한 후 확인하세요:

python render.py stills 0.3,1.5,2.9 first

그런 다음 work/first.png를 Read 도구로 엽니다. 다른 장면들을 코딩하기 전에 이 단계를 수행하세요: 파이프라인이 작동하는지 증명하고, 더 중요하게는 스타일을 변경하는 것이 아직 저렴할 때 평가할 수 있습니다.

5. 나머지 장면들을 한 번에 몇 개씩 작성

시스템을 작동하게 유지하는 규칙들(각 규칙은 위반 시 특정 실패를 초래하기 때문에 존재합니다):

  • 시간의 순수 함수. 샷은 function name(g, t, u) 형태이며, t는 절대 시간(초), u는 샷이 시작된 후의 시간(초)입니다. 프레임 간 누적되는 변수, Math.random(), Date를 사용하지 마세요. hash(n), noise1(x), rng(seed)를 사용하세요. 여러 브라우저가 프레임을 순서 없이 렌더링하므로 상태를 가지면 깜빡임이나 일치하지 않는 청크로 나타납니다.
  • 절대 시간으로 애니메이션. E.out(seg(t, 12.62, 13.1))와 같이 작성하고, 단어 목록에서 직접 숫자를 사용하세요. 편집이 변경되어도 비트는 해당 단어에 남아 있습니다.
  • 모든 것이 조금씩 움직입니다. 정지된 프레임은 버그로 읽힙니다. 모든 샷에 느린 카메라 드리프트(1 + u * .01 줌 정도면 충분함)와 약간의 보조 움직임(비, 연기, 흔들리는 물체)을 주세요.
  • 모든 것에 이징. 도착하는 것에는 E.out, 카메라 이동에는 E.inOut, 튀어나오는 효과에는 E.back, 착지에는 E.bounce. 선형 움직임은 기계적으로 보입니다.
  • 프레임보다 더 넓게 세계를 그리세요. 카메라가 줌 아웃되거나 흔들릴 때; 배경과 바닥을 0..1920 범위를 훨씬 넘어 채워서 가장자리가 드러나지 않도록 하세요.
  • 충격에는 세 가지가 필요합니다: 2~3 프레임의 전체 화면 flash, 감쇠하는 shake, 그리고 반응하는 무언가(연기, 반동, 색상 반전).
  • 장면 코드를 파일로 분할하세요. (s1.js, s2.js, ...) 파일이 수백 줄을 넘으면 분할하고, index.html에 scenes.js보다 앞에 추가하세요.

두세 개의 샷을 작성하고, 이들의 콘택트 시트를 렌더링하고, 확인하고, 수정하고, 계속하세요. 전체 영화를 눈으로 확인하지 않고 작성하지 마세요.

6. 스틸 이미지로 검토 - 이것이 당신의 눈입니다

비디오를 볼 수 없으므로 콘택트 시트가 검토 수단입니다. 모든 샷에 대해 최소한 시작, 주요 동작의 중간, 끝, 그리고 각 비트의 정확한 프레임을 샘플링하세요:

python render.py stills 12.2,13.3,14.3,15.4,16.5 act2     # 시트당 최대 ~16 타임스탬프
python render.py sheet 4                                   # 전체 영화, 4초마다 한 프레임

PNG를 읽고 샷별로 확인하세요: 주제가 한눈에 읽을 수 있을 만큼 큰가? 잘리거나, 겹치거나, 떠 있는 것은 없는가? 중요한 것이 배경과 대비를 이루는가? 디자인된 것처럼 보이는가, 아니면 프레임 절반이 비어 있는가? 그런 다음 수정하고 동일한 타임스탬프를 다시 렌더링하세요. references/pitfalls.md에는 쉽게 만들고 알아차리기 어려운 실수들이 나열되어 있습니다.

페이지 오류는 render.py에서 PAGE ERROR: 줄로 출력됩니다. 검은색 또는 반쯤 그려진 프레임은 일반적으로 해당 샷에서 예외가 발생했음을 의미합니다.

7. 렌더링 및 확인

python render.py full --workers 8

백그라운드에서 실행하세요. 70초 영화는 약 2,100프레임이며 몇 분이 걸립니다. output.mp4(1920x1080, H.264 + AAC, 오디오가 전체 길이로 패딩됨)를 작성합니다. 그런 다음 스틸 이미지뿐만 아니라 파일 자체를 확인하세요:

ffprobe -v error -show_entries format=duration:stream=codec_name,width,height -of default=nw=1 output.mp4
python render.py sheet 6

길이, 오디오 스트림이 있는지, 개요 시트에 빈 프레임이 없는지 확인하세요. 보고서에 스틸 이미지와 파일 속성을 확인했지만 움직이는 영상은 보지 않았다는 점을 명확히 밝히세요. 사용자가 한 번 시청해야 합니다.

8. 전달

사용자에게 output.mp4의 경로, 개요 시트, 편집 후 재렌더링 방법에 대한 간단한 설명을 제공하세요. 브라우저에서 src/index.html을 열면 오디오가 포함된 라이브 플레이어가 되어, 사용자가 타이밍을 확인하는 가장 빠른 방법입니다.

사운드

사용자가 음악이나 효과음을 요청하지 않는 한, 내레이션만 제공하세요. 요청한 경우, 동일한 비트 목록을 기준으로 타이밍을 맞춰 최종 먹싱에 ffmpeg 입력으로 추가하세요.

참고 파일

  • references/storyboarding.md - 내레이션을 샷으로 변환, 비트 배치, 시각적 은유, 장면이 디자인된 느낌을 주는 요소. 3단계 전에 읽으세요.
  • references/engine-api.md - engine.js의 모든 헬퍼와 한 줄 사용법. 5단계 전에 읽으세요.
  • references/pitfalls.md - 어렵게 발견된 실패 모드와 수정 방법. 6단계 전에 읽으세요.
  • references/example-bells/ - 스토리보드가 포함된 완전한 71초 영화(16개 샷, 약 900줄의 장면 코드). storyboard.md를 읽어 예상되는 계획 수준을 확인하고, 특정 효과(올라가는 캐릭터를 따라가는 카메라, 폭발, 지도, 선을 긋는 깃펜, 가늘어지는 눈)가 어떻게 구현되었는지 보려면 src/s*.js 파일을 여세요.

관련 스킬