Product video as code (제품 비디오를 코드로)
제품 비디오를 코드로 빌드합니다: 결정론적 타임라인으로 구동되는 HTML 데크, AI 음성 해설, 실제 제품 화면 녹화, 그리고 ffmpeg 오디오 마스터로 구성됩니다. 모든 것이 재현 가능하고, diff 가능하며, 다시 렌더링할 수 있어 수정 시 재구축 대신 몇 분이면 됩니다.
이것은 비디오 편집기가 아닙니다. 파이프라인입니다. 출력물은 MP4입니다.
중요한 한 가지 아이디어
오디오 우선, 그 다음 타이밍, 마지막으로 화면.
화면에 어떤 것이 얼마나 오래 표시될지 결정하기 전에 내레이션을 생성하세요. 각 클립을 측정하고, 해당 측정값에서 장면 지속 시간을 도출한 다음에야 프레임을 렌더링합니다. 대안들(지속 시간을 추측하거나 고정된 스토리보드에 맞추기 위해 내레이션을 작성하는 것)은 음성과 시각 자료가 동기화에서 벗어나고, 문구를 수정할 때마다 다시 렌더링을 강제합니다.
script.json ──► TTS ──► 측정 ──► timings.json ──► 렌더 ──► 컴포지트 ──► 먹스 ──► MP4
▲ ▲ ▲
assemble.py render.py composite.py
내레이션이 없는 비디오(타이틀 스팅, 무음 루프, 음소거된 소셜 클립)의 경우 1단계를 건너뛰고 timings.json을 직접 작성하세요. 이후 모든 단계는 변경되지 않습니다.
파이프라인
| 단계 | 스크립트 | 결과물 |
|---|---|---|
| 1. 내레이션 | 선택한 TTS 도구 | audio/vo_NN.mp3, 장면당 하나씩 |
| 2. 타이밍 + 음성 트랙 | scripts/assemble.py | timings.json, out/voice.wav |
| 3. 화면 | scripts/render.py | out/silent.mp4 |
| 4. 화면 영상 (선택 사항) | scripts/capture/tour.py | capture/clips/*.mp4 |
| 5. 합성 (선택 사항) | scripts/composite.py | out/composited.mp4 |
| 6. 오디오 마스터 | scripts/mux.py | 최종 MP4 |
워크스루는 references/PIPELINE.md, 음성 및 음악은 references/AUDIO.md, 제품 영상은 references/SCREEN-CAPTURE.md, 디버깅 전에 이 파이프라인이 실제로 직면한 실패 사례 목록이 있는 references/PITFALLS.md를 읽으세요.
비디오 종류
동일한 파이프라인이지만, 구조, 페이싱, 프레임 중 실제 제품이 차지하는 비율이 달라집니다. 문장을 작성하기 전에 어떤 종류를 만들지 결정하세요. 그게 다른 모든 것을 결정하기 때문입니다.
| 종류 | 일반적인 길이 | 형태 | 영상 |
|---|---|---|---|
| 제품 설명 영상 (Product explainer) | 1-3분 | 문제 → 전환 → 작동 방식 → 증명 → 마무리 | 약간, 증명용으로 |
| 기능 출시 (Feature launch) | 30-90초 | 변경된 점 → 중요성 → 직접 보기 → 사용 방법 | 중간 |
| 데모 / 워크스루 (Demo / walkthrough) | 2-5분 | 실제 작업 하나, 처음부터 끝까지 | 지배적 |
| 튜토리얼 (Tutorial) | 3-10분 | 단계별, 챕터 구분, 따라 할 수 있는 간격 | 지배적 |
| 릴리스 노트 (Release notes) | 30-90초 | 비트당 하나의 항목, 밀도 높음 | 스크린샷 또는 짧은 클립 |
| 온보딩 (Onboarding) | 1-3분 | 사용자가 처음 만나는 경로 | 지배적 |
| 타이틀 스팅 / 루프 (Title sting / loop) | 5-20초 | 하나의 아이디어, 종종 무음 | 없음 |
| 소셜 컷 (Social cut) | 60초 미만 | 먼저 훅, 긴 컷에서 파생 | 짧고 강렬 |
유형에서 비롯되는 경험 법칙:
- 데모와 튜토리얼은 영상 우선입니다. 데크는 제품 주변의 크롬(타이틀 카드, 콜아웃, 챕터 구분)입니다. 화면에 이미 표시된 내용을 내레이션으로 다시 말하지 마세요.
- 설명 영상과 출시는 내레이션 우선입니다. 영상은 방금 한 주장에 대한 증거로 등장합니다. 그 인접성이 메시지를 와닿게 만듭니다.
- 튜토리얼에는 숨 쉴 공간이 필요합니다. 각 단계 후에 박자를 남겨두세요.
--tail을 충분히 사용하세요. 시청자는 따라 하는 중이지, 그냥 보는 것이 아닙니다. - 릴리스 노트는 밀도가 높습니다. 화면 텍스트가 전달하고, 내레이션이 요약합니다. 비트당 하나의 항목, 머무르지 않음.
- 소셜 컷은 파생되며, 두 번 작성하지 않습니다. 긴 컷의 장면 하위 집합으로 빌드하여 차이가 발생하지 않도록 하세요(PIPELINE.md 참조).
빠른 시작
설정, 한 번만: pip install -r requirements.txt && playwright install chromium
ffmpeg와 ffprobe도 PATH에 있어야 합니다.
먼저 데크가 렌더링되는지 확인하기 위해 무음으로:
cp templates/deck.html .
python3 scripts/render.py --preview 2 8 18 # 스틸 이미지
python3 scripts/render.py # -> out/silent.mp4
그런 다음 내레이션이 준비되면 전체 파이프라인:
cp templates/script.example.json script.json
# 장면당 하나씩 audio/vo_01.mp3 ... 생성, 그 다음:
python3 scripts/assemble.py # timings.json + out/voice.wav
python3 scripts/render.py # out/silent.mp4
python3 scripts/mux.py --final out/final.mp4 # 음성 추가 (+ audio/bed.mp3가 있으면 음악)
assemble.py는 오디오가 필요한 유일한 단계입니다. 오디오가 없으면 render.py는 데크에 선언된 지속 시간을 사용합니다.
이것이 계속 작동하게 하는 규칙
절대 실제 시계 시간(wall-clock time)으로 타임라인을 구동하지 마세요. 렌더러는 명시적인 t로 이동하여 스크린샷을 찍습니다. render.py는 각 프레임에 대해 window.__seek(t)를 호출하므로, 느린 시스템에서도 빠른 시스템과 동일한 파일을 생성합니다. requestAnimationFrame이나 CSS 애니메이션 재생을 사용하는 것은 스크린샷 루프 아래에서 찢어지거나 프레임 드롭을 유발합니다.
모든 애니메이션을 내레이션 비트에 고정하세요. 음성이 4.2초에 "세 가지"라고 말하면 세 번째 카드는 4.2초에 나타납니다. 다시 작성한 후에는 비트를 다시 확인하세요. 3초 더 짧아진 대사는 시각 자료를 고립시킵니다. 이것이 결과물이 연출된 것처럼 보이는지 아니면 일반적으로 보이는지를 결정하는 가장 큰 요인입니다.
재개 가능한 청크로 렌더링하세요. render.py는 out/parts/part_NNN.mp4를 작성하고 재실행 시 완료된 부분을 건너뜁니다. 중단된 스크린샷은 전체 패스가 아닌 하나의 청크만 손실됩니다. 동일한 parts 디렉토리에 대해 두 개의 렌더러를 동시에 실행하지 마세요.
측정하고, 추측하지 마세요. 숫자로 확인하고 느낌으로 확인하지 마세요:
- 빈 프레임 감지: 표준 편차가 아닌 어두운 픽셀 수(
(pixels < 200).mean())를 세세요. 합법적으로 흰색이 많은 UI 페이지도 표준 편차가 낮습니다. - 발음: 생성된 오디오를 음성-텍스트로 다시 실행하고 읽으세요.
- 레벨: 매번 최종 파일에 대해
ffmpeg -af volumedetect를 실행하세요. - 지오메트리: 하드코딩하지 말고
getBoundingClientRect()를 DOM에서 읽으세요.
하나의 소스, 여러 컷. 단일 데크에서 쿼리 매개변수(deck.html?cut=short)로 장면 하위 집합을 선택하여 긴 데모와 40초 소셜 컷이 분리되지 않도록 하세요. references/PIPELINE.md 참조.
제품의 코드에서 브랜드 일치시키기
제품에 코드베이스가 있다면, 16진수 코드를 묻는 대신 거기서 브랜드를 읽으세요:
python3 scripts/detect_brand.py ../their-product --css
디자인 토큰 파일, tailwind 설정, CSS 사용자 정의 속성, SCSS 및 Less 변수, JS/TS 테마 객체, 웹 매니페스트를 읽고 발견한 폰트, 아이콘 라이브러리, 로고 파일을 보고합니다. 출력된 :root 블록을 deck.html에 붙여넣으세요.
dist/ 폴더는 브랜드가 아닌 벤더드 컴포넌트 라이브러리 CSS로 가득 차 있으므로 빌드 출력은 건너뜁니다. 팔레트도 이름 일치만 신뢰하지 않고 일관성을 확인합니다: 밝은 테마를 위해 --surface를, 어두운 테마를 위해 --bg를 정의하는 저장소는 그렇지 않으면 검은 프레임에 흰색 카드를 제공합니다. 거부된 항목이 출력되며, 조용히 적용되지 않습니다.
렌더링 전에 감지된 항목과 거부된 항목을 사용자에게 보여주세요.
디자인 가이드라인
데크는 HTML/CSS이므로 디자인은 전적으로 여러분의 몫입니다. 알아둘 만한 두 가지:
소셜 및 자동 재생 컨텍스트는 음소거됩니다. 화면 헤드라인이 각 비트를 자체적으로 전달해야 하며, 훅은 처음 1-2초 안에 전달되어야 합니다. 음성 해설은 전달 메커니즘이 아닌 향상 요소로 취급하세요.
시각적으로 독특한 것을 요청받았다면, 어두운 배경에 하나의 밝은 강조 색상(일반적인 출력이 모이는 곳)을 찾기보다는 주제 자체의 세계(그 유물, 도구, 용어)에 기반을 두세요. Anthropic의 frontend-design 스킬이 이 부분을 잘 다루며, 이 스킬과 깔끔하게 결합됩니다.