100% 코드로 만드는 비디오 (Remotion + 로컬 오디오)
작업 순서 (순서 변경 금지)
- 스토리 먼저 (HyperFrames 메서드
story.md): 유일한 작업, 3개의 스토리라인, 처음 2초 안에 후크, 백분율로 비트 표시, 금지된 생성 비디오 "tells" (모든 것이 동일하게 들어감, 이유 없는 부유, 일렬 카드, 떠다니는 로고). 타임코드와 함께SCRIPT.md작성. - 음성 → 실제 지속 시간 →
timeline.json(씬, 각 줄 시작, 이벤트, 음악 섹션). 오디오와 비디오가 동일한 JSON을 읽음. - 마스터 오디오 (음악 + SFX + 덕킹 적용 음성, −15.5 LUFS, 피크 ≤ −1 dBTP).
- 씬 in Remotion, 각 씬에 대해
<Sequence>가 있는Film컴포지션 하나; 렌더링 전에 컨택 시트(contact sheet)로 제어 프레임 검토. - 씬별 렌더링 (
--frames=a-b --muted) → 연결 → ffmpeg로 먹싱 → 추출된 프레임으로 QC.
엔진
npx create-video@latest --yes --blank --no-tailwind <dir>;npx remotion add @remotion/transitions @remotion/media @remotion/effects @remotion/noise @remotion/paths @remotion/shapes @remotion/motion-blur @remotion/fonts.- GPU 없는 샌드박스:
remotion.config.ts에서 →Config.setBrowserExecutable(<chrome-headless-shell>),Config.setChromiumOpenGlRenderer("swangle"),setConcurrency(2),setCrf(18),setPixelFormat("yuv420p"). - 폰트:
@fontsource[-variable]/*패키지 (npm은 도착함; Google Fonts는 안 됨). 캡처 전에delayRender와 함께document.fonts.load()대기 (글리프 áéíóúñ¿¡). - Remotion 규칙: 모든 것은
useCurrentFrame()에서 파생;interpolate에 clamp 사용; Remotion의random(seed)사용; CSStransition/animation,Math.random,Date, 타이머,useFrame금지. - 비용 측정 (2 CPU, 1080p): 빈 씬 ≈0.1 초/프레임; 2000 스프라이트 캔버스 ≈0.7 초/프레임; 전체 화면
filter: blur()≈2.5 초/프레임. 파티클: 닫힌 형태 (위치 = f(시드, i, t)), 사전 블러 처리된 방사형 스프라이트,globalCompositeOperation: "lighter". - 3분짜리 단편 (5400 프레임, 1080×1920)은 2 CPU에서 약 1시간 30분 소요. 번들 저장 (
npx remotion bundle) 후 번들에서 렌더링.
재사용 가능한 키트 (이 저장소에는 포함되지 않음; 구축할 부품 목록 역할)
anim.ts (역할별 이징, 닫힌 스프링, keyedState, 깜빡임, 드리프트), Camera2D.tsx (시차/돌리가 있는 레이어), Particles.tsx (비, 반딧불, 먼지, 별, 그레인), City.tsx (절차적 네온 시티 + 반사), Text.tsx (키네틱, 네온, 단어별 자막, 가변 타이틀), Post.tsx (비네트, 플래시, 레터박스, 번개), character/Mia.tsx (리그: 포즈 + 표정 + 비슴 A–H/X + 깜빡임 + 호흡 + 림 라이트 + 소품), character/Lucero.tsx, Flat.tsx (보일 효과가 있는 연필 평면 세계), ChatScreen.tsx, lipsync.ts. tools/의 스크립트: voice.py, make_voices.py, asr.py, make_timeline.py, make_audio.py, render_all.sh, assemble.sh, sheet.py.
캐릭터
- 리그 = 프레임별로 혼합 가능한 숫자 매개변수 (포즈, 표정, 비슴); 표정 프리셋;
keyedState로 전환 (예측 → 동작 → 안정). 머리가 이끌고 몸이 따름; 2–5초마다 시드된 깜빡임; 같은 곡선으로 모든 것을 동시에 움직이지 않음. - 립싱크: WAV 22kHz 모노 16비트에 대해 Rhubarb
-r phonetic -f json→ 큐 A–H/X → 큐 간 입 매개변수 혼합 (블렌드 50ms). 속삭임에 대해 열림 강도 ×0.7. - 애니메이션 전에 스틸로 캐릭터 시트 (표정, 비슴, 포즈) 생성.
음성 (스페인어, 로컬)
- Kokoro-82M ONNX (
kokoro-onnx, 음성ef_dora,em_alex,em_santa, 언어es-419) 및 Piper (piper-tts,es_MX-claude-high,es_AR-daniela-high). 모델은 GitHub 릴리스에서 (thewh1teagle/kokoro-onnx, k2-fsa/sherpa-onnx). - 어린이:
rubberband -p N -F로 +4…+6 반음 (포먼트 유지). 저음: −4. "많은 목소리로 만든 목소리": −2 st 레이어 (26 %) + +0.3 st 지연 12ms 레이어 (18 %) + 큰 리버브. - 항상 ASR로 확인 (Parakeet-TDT 0.6B v3 via sherpa-onnx ≥ 2.0): 텍스트에 대한 WER; 5% 초과 시 재생성 또는 문장 다시 쓰기. 효과 적용 전에 확인 (리버브가 ASR의 언어 감지기를 혼동시킴). ASR의 단어별 타임스탬프가 자막에 공급됨.
- 사용자가 자신의 음성 (예: ElevenLabs)을 가지고 있다면, 각 줄의 WAV를 대체하고 씬을 건드리지 않고 큐/타임라인을 재생성.
음악 및 SFX
pretty_midi로 MIDI 작곡 (bpm, 진행, GM 악기별 섹션) →fluidsynth -ni -g 0.6 -r 48000 -F out.wav MuseScore_General_Full.sf3 in.mid→ pedalboard (HPF, 컴프레서, 리버브) → pyloudnorm.- 시각적 동기화 = 동일한 비트 산술 (프레임/비트 = fps·60/bpm, 인덱스에서 각 비트 계산).
- 화면상 원인이 있는 SFX; 이미지 다음 1프레임에 피크 배치; 테이크 순환; 존재하지 않는 것은 numpy로 합성 (허밍, 비, 연필, 샤이머, 서브 임팩트).
- 믹스: 음성 −20 dBFS RMS, 음성 아래 음악 −8 dB (덕킹 120ms / 700ms), SFX −4 dB, 마스터 −15.5 LUFS, 피크 0.6. 스펙트로그램 (
ffmpeg showspectrumpic)으로 침대 (비)가 음성을 가리지 않는지 확인.
연출 (읽은 스킬에서: Remotion, HyperFrames, iart-ai motion/kinetic/webgl, nolanx, anatomy, isometric-ops, taste-skill)
- 비트당 한 동작; 도착은 긴 꼬리 (
bezier(0.16,1,0.3,1)), 출발은 40–50% 더 짧음; 카메라inOut; 일단 착지하면 타이틀 정지; 원인이 있는 3–5가지 전환 유형; 가장 빠른 프레임에서 컷. - 시차: 배경 0.1–0.3배, 중간 0.5–0.7배, 앞 1–1.5배 + 블러. 드론 샷 = 로그 스케일로 중첩된 평면 스택 (창문 → 파사드 → 블록 → 지도 → 구름 → 행성).
- 등각: 단일 투영; 평평한 그림자 15–30%; 상단 더 밝게.
- 타이포그래피: 최대 2개 패밀리; 타이틀 높이의 4–9%; 문자 스태거 20–40ms, 단어 40–70ms; 가변 폰트로 무게/너비 조절.
- 세로: 핵심 콘텐츠는 중앙 80%; 자막은 하단 안전 영역 위 (≥ 110px); 텍스트 ≥ 44px; 소리 없이도 이해할 수 있도록 단어별 자막.