Spatial Film
60초 제품 영화(7개 장면, 단일 웨이브에 5명의 작업자, 모든 이음매가 프레임 단위로 정확, 확인 깔끔, 4분 19초 만에 1080p60)로 엔드투엔드 검증되었습니다. 템플릿의 film.json에는 해당 영화의 실제 장면 막대와 큐 시트가 작업 예제로 포함되어 있습니다.
<skill> = 이 기술의 폴더(이 SKILL.md가 있는 디렉터리). 모든 스크립트는 영화 프로젝트 루트에서 실행하세요.
아래 python = explainer-kit의 설정 확인이 ~/.talking-explainer/config.json에 저장한 Python 명령어(Windows에서는 py -3.12, macOS에서는 python3).
필요한 것
- Node 20+, PATH에
ffmpeg+ffprobe,numpy와Pillow가 있는 Python 3.10+. - HyperFrames 기술:
npx hyperframes skills update general-video hyperframes-animation media-use(패킷 빌더, 청사진 + 애니메이션 맵, 음악 도우미). CLI 자체는 프로젝트별로 고정되며npx를 통해 실행됩니다. - 표면 사전 렌더링을 위한 Playwright: 영화 폴더에서
npm i -D playwright실행 (또는PLAYWRIGHT_PATH설정). - 선택 사항, 소스 음악의 경우: HeyGen 계정 - 자신의 터미널에서
npx hyperframes auth login을 한 번 실행하세요. 계정이 없으면 직접 트랙을 가져와 4단계를music-analyze.py부터 시작하세요. - 최종 렌더링을 위한 GPU 지원 머신 (소프트웨어 경로로도 렌더링되지만 약 15배 느립니다).
컨텍스트 낭비 없이 이 기술을 사용하는 방법
- 한 번에 하나의 단계 참조만 로드하세요. 해당 단계가 시작될 때만 로드합니다. 절대 미리 모두 로드하지 마세요.
- 스크립트는 검증된 코드입니다. 실행하세요. 로직을 인라인으로 재생성하지 마세요.
- 메인 세션은 전체 장면 파일(20-60KB)을 읽지 않습니다. 연락처 시트로 장면을 판단하고
grep -n과 정확한 패치로 수정하세요. - 작업자에게는 내용을 붙여넣지 말고 파일 경로를 제공하세요.
storyboard.html(60KB+)은 셀 ID로 grep하며, 전체를 읽지 않습니다. - 먼저 연락처 시트를 읽고 세부 사항을 확인해야 할 때만 단일 스냅샷 PNG를 여세요.
단계 (각 단계는 게이트에서 종료됩니다 - 게이트가 통과될 때까지 다음 단계로 넘어가지 마세요)
| # | 단계 | 로드 | 게이트 |
|---|---|---|---|
| 0 | 스캐폴드: node <skill>/scripts/new-film.mjs <dir> --name <slug> | - | 폴더에 film.json, frame.md, kit, 템플릿 문서가 있어야 함 |
| 1 | 인테이크, BRIEF, STORYBOARD, 스케치 시트 | references/storyboard.md | ## Locked 아래에 사용자의 명시적인 "go"가 기록되어야 함 |
| 2 | frame.md, kit, 사전 렌더링된 표면 | references/surfaces.md (+ GHL/n8n/Zapier가 나타나는 경우에만 platform-canvases.md) | frame.md에 {{가 없어야 함, 모든 캡처 PNG를 확인해야 함 |
| 3 | 하네스 스모크 테스트, 패킷, 병렬 작업자 | references/workers.md | 모든 장면: lint 오류 0, 연락처 시트가 해당 블록과 일치해야 함 |
| 4 | 음악 + SFX (작업자가 빌드하는 동안 실행) | references/audio.md | BGM이 네이티브 BPM, 다운비트에 0번째 마디, film.json에 큐가 있어야 함 |
| 5 | 조립, 확인, 이음매, 렌더링, 배송 | references/verify-ship.md | 확인 통과, seam-check 모두 PASS, hardware-gpu 렌더링 확인 |
의도 및 스케치 검토 단계를 위해 /hyperframes -> /general-video를 경유하세요 (이 기술은 프로젝트 형태, 계약, 스크립트 및 게이트를 제공하며, general-video는 패킷 빌더와 작업자 역할을 제공합니다).
파일
| 경로 | 설명 |
|---|---|
template/ | 프로젝트 스켈레톤: film.json (장면 막대, BPM, BGM, SFX, 큐 - 유일한 타이밍 소스), frame.md (설계 진실 계약, 채워넣기 포함), BRIEF.md, STORYBOARD.md, assets/kit.css (기본 영화 키트), assets/fonts/ (Poppins 500/700 + OFL), references/worker-brief.md, references/scene-starter.html (lint-클린 장면 스켈레톤), references/snippets.html, package.json (고정된 hyperframes 0.8.117). |
surfaces/ | GHL, n8n 및 Zapier 캔버스의 준비된 HTML 재구축 + 긴 마케팅 페이지(가상의 Northwind Dental 데이터), platform.css, sprite.svg, jobs.json. |
scripts/new-film.mjs | 템플릿에서 프로젝트 스캐폴드. |
scripts/capture-surfaces.cjs | src/captures/*.html을 2배로 assets/captures/*.png로 렌더링 (jobs.json). |
scripts/measure-surface.cjs | 표면의 요소에 대한 1x 상자, 정확한 주석/자르기 대상용. |
scripts/make-harness.py, scripts/test-scene.mjs | 장면별 격리된 테스트 프로젝트; 하나의 장면 lint + 스냅샷. |
scripts/music-search.mjs, sfx-search.mjs | media-use의 REST 헬퍼를 통한 HeyGen 카탈로그 (Windows 안전). |
scripts/music-analyze.py, music-bars.py | numpy 전용 템포 + 마디당 에너지 (librosa 필요 없음). |
scripts/bgm-cut.py | 마디선에서 BGM 자르기, 트랙 자체 아웃트로 연결, 페이드; --b는 더 긴 영화 아래에서 BGM을 루프하도록 반복합니다. |
scripts/assemble.py | film.json -> index.html (마디 그리드의 장면 슬롯, BGM, 지속 시간 및 레인이 포함된 SFX). |
scripts/seam-check.py | 모든 이음매(컷 전/컷 시점/컷 후)와 루프(프레임 0 대 마지막)를 픽셀 단위로 증명합니다. |
scripts/render-final.mjs | --browser-gpu를 사용한 CLI 렌더링, 파일에 기록, gpu 모드 + ffprobe 확인 후 flash-scan.py 실행. |
scripts/flash-scan.py | 렌더링의 모든 프레임: 양쪽 이웃과 다른 프레임(1/60초 동안 표시된 요소) = 버그. |
scripts/encode-web.mjs | 웹 인코딩 (H.264 CRF 21, faststart, AAC 또는 --silent) + 포스터 프레임. |
scripts/blink-fit.py, scripts/build-presenter.py | 나레이션 영화: 시트 포즈용 깜빡임 오버레이(--band = 눈 띠); mascot에 mouth-fit.json이 있을 때 립싱크와 함께 자체 트랙에서 프레젠터 오버레이를 만듭니다. |
scripts/el-tts.py, el-words.py, el-music.py | ElevenLabs를 통한 나레이션 영화 (env EL의 키, 디스크에 절대 저장 안 함): SCRIPT.md의 음성 후보, Scribe 단어 타이밍, 생성된 BGM. |
scripts/seam-diff.py | 프레젠터 상자가 마스킹된 이음매 diff (나레이션 영화). |
scripts/fromto-scan.py | 사전 렌더링: 렌더링 작업자(장면 내 탐색)가 절대 적용하지 않는 fromTo FROM 전용 속성을 플래그합니다. 모든 렌더링 전에 0 히트. |
scripts/patch_exact.py | 스크립트된 장면 수정을 위한 정확한 CRLF-안전 찾기/바꾸기 (각각 하나의 일치를 주장). |
scripts/selftest.mjs | 이 기술이 이 머신에서 작동한다는 엔드투엔드 증명. 반드시 PASS를 출력해야 합니다. |
나레이션 영화 (음성 해설 + 화면상 프레젠터)
나레이션 "API vs Webhook" 설명 영화(83초, 세로 및 가로, 복제된 음성, 마스코트 프레젠터, 5명의 작업자가 작업한 10개 장면)로 검증되었습니다. 위의 음악 영화 규칙은 다음 규칙으로 대체되는 경우를 제외하고 여전히 유효합니다:
- 먼저 음성 후보.
EL=<key> python <skill>/scripts/el-tts.py . eric=<voice_id> michelle=<voice_id>는SCRIPT.md의 들여쓰기된 줄을 읽고 음성별로 하나의 MP3를src/audio/voice-candidates/에 씁니다 (env의 키만, 디스크에 절대 저장 안 함). 사용자가 귀로 선택하게 한 다음 선택한 것만 전사합니다. - 나레이션이 시계입니다. 단어 시간:
EL=<key> python <skill>/scripts/el-words.py <voice.mp3> src/timing/voice-words.json(ElevenLabs Scribe - 로컬 모델 없음, 키 필요), 또는word_timestamps가 있는 faster-whisper (언어 설정, VAD 켬; explainer-kit의transcribe.py, 무료 및 로컬). 단어 JSON을src/timing/에 보관하세요.film.json에서"bpm": 60, "beats_per_bar": 1로 설정하면 "마디"가 1초가 되고 장면 길이는 문장 쉼표에서 자른 일반 초가 됩니다. 스토리보드 블록은 장면 로컬 단어 시간을 제공합니다. 도착은 ~0.1초 일찍 시작하여 단어에 맞춥니다. 제목은 다음 컷 전에 화면에 최소 ~1초 동안 표시되도록 하세요 (컷을 표시하는 줄의 마지막 단어와 비교하여 확인). film.json추가 사항 (assemble.py + make-harness.py가 읽음): 음성을 위한"audio": [{id, src, dur, volume}], 모든 장면 위에 있는 전체 영화 레이어(프레젠터)를 위한"overlays": [{id, track, z}]- 따라서 이음매에서 팝되지 않고 장면은 프레젠터가 없는 상태로 유지됩니다.- 포즈 PNG의 프레젠터: 포즈당 하나의 투명 PNG (explainer-kit의
prep-mascot.py가 트림, 스케일링 및 매니페스트 작성), 그런 다음scripts/blink-fit.py <mascot_dir>(선택 사항, torch 필요; BLINK 눈 띠를 NCC로 각 포즈에 맞춤; 0.70 미만 포즈는 깜빡임 없음) 및scripts/build-presenter.py(src/presenter-track.json에서 오버레이 작성: 큐 단어의 포즈, 헤드 고정으로 전환이 절대 점프하지 않음, 음성 봉투 밥, 호흡, 깜빡임, fx). 출력물을 수동으로 편집하지 마세요. - 오디오: 음성을 -16 LUFS / -1.5 dBTP로 loudnorm (4.6 dBTP에서 클리핑된 일반 게인), 그 아래에서 BGM을 오프라인으로 사이드체인 덕킹 (~17 dB 이하), SFX는 조용하게 (0.1-0.45). 음성 키를 패딩하거나 덕이 BGM을 음성 길이로 자르지 않도록 하세요 ("bgm 110.25s vs slot 113s" 경고 확인):
[1:a]apad=whole_dur=<TOTAL>[k];[0:a][k]sidechaincompress=threshold=0.03:ratio=6:attack=25:release=450,atrim=end=<TOTAL>. 카탈로그 BGM 없음(HeyGen 검색 500초 또는 로그인 없음):EL=<key> python <skill>/scripts/el-music.py .media/candidates/bed.mp3 <ms> "<프롬프트: 악기, BPM, 내레이터 아래에 위치, 드롭 없음, 해결된 코드로 끝남>"- 영화 길이에 맞춰 스스로 끝나는 트랙은 스플라이스가 필요 없습니다. - 이음매 확인 노이즈: 프레젠터가 모든 이음매를 가로질러 움직이므로
seam-check.py는 이로 인해 실패합니다. 플래그된 각 쌍을scripts/seam-diff.py a.png b.png out.png x0,y0,x1,y1으로 판단하세요 (프레젠터 상자 마스킹) - 외부 0.00%는 통과입니다. - 세로(1080x1920) 크기 규칙: 모든 장면의 상단 띠는 실제 제목(60-84px), 본문 텍스트 >= 40px을 포함하며, 객체는 전체 너비와 프레젠터 위의 높이를 사용합니다. 첫 번째 웨이브는 프레임 상단 45%에 ~33px 텍스트를 배송했습니다. dispatch 전에 frame.md에 이 숫자를 넣으세요.
- 루프가 아님: 나레이션 영화는 고정된 잠금 화면에서 끝납니다. seam-check의 루프 라인은 무시하세요.
하드 규칙 (프리미엄과 부드러움을 만드는 규칙)
- 소스의 진실. 제품 UI는 제품 자체의 HTML/CSS에서 이식됩니다. 서드파티 캔버스는 사용자의 실제 캡처에서 재구축되며, 기억에 의존하지 않습니다. 가상 데이터만 사용합니다. 제품이 실제로 가지고 있는 기능만 사용합니다.
- 카메라 1대. 모든 장면에서 원근 1866px(35mm); 카메라 레이어 아래 드리프트 레이어로 아무것도 정지하지 않음; 먼 잎사귀에 시차 + DOF 흐림. 3D 부모에 opacity/filter/overflow/clip-path 없음 (평탄화됨).
- 1080p+에서 선명하게. CSS
zoom(.kit-zoom)으로 UI 스케일, 절대transform: scale(>1)사용 금지; 각 조각을 화면상 가장 가까운 크기로 작성; 캡처는 네이티브 이하로 표시되는 2x PNG입니다. - 60fps 부드러움 규칙. Transform/opacity만 사용; 착지에는 spring, 도착에는 expo/power3, 카메라 다리에는 inOut; linear, steps, snap 또는 반올림 금지; 카메라 다리는 0.15-0.3초 겹치고 0.6초 이상 지속됩니다.
- 결정론적.
window.__timelines[id]에 마지막으로 등록된 장면당 하나의 일시 중지된 타임라인, 명시적 시작이 있는fromTo, random/clock/CSS 전환 없음. - 이음매는 공유 프레임입니다. 각 이음매는 두 이웃이 ~0.1초 동안 유지하는 하나의 정확한 그림이며, 리그 외부의 2D 플레이트에 의해 잠깁니다. 하드 컷, 빈 프레임, 사라짐으로의 페이드 없음. 마지막 프레임 = 첫 번째 프레임.
- 그리드 위에. 장면은 전체/반 마디; 클릭, 틱, 스탬프 및 스왑은 비트에 맞춤; 네이티브 템포의 음악은 마디선에서 잘림.
- 계약 수정, 장면 수정 아님. 루트 상대
assets/...경로, 숫자로 시작하지 않는 id, 장면 id로 접두사가 붙은 id 및 클래스. 모든 장면이 위반할 규칙은 frame.md / worker-brief에서 한 번 수정됩니다. - 금지 사항. 이모지, 네온 글로우, 보라색-파란색 그라데이션, 색상이 있는 왼쪽 테두리 카드, 원형 스피너(쉬머), 스톡 인물, "John Doe", 슬라이드쇼 페이싱 또는 아무것도 말하지 않는 모션.
- CLI와 하드웨어 GPU로 최종 제작. Studio 내보내기(소프트웨어 경로, ~15배 느림) 사용 금지.
문제 발생 시
| 증상 | 원인 | 수정 |
|---|---|---|
| Lint: 자산 경로 오류 | 하위 구성에서 ../assets/ | 루트 상대 assets/... (계약에서 수정) |
| Lint: 잘못된 구성 id | id가 숫자로 시작 | s01-click, 절대 01-click 아님 (film.py가 거부함) |
| 패킷 빌더 오류 발생 / 패킷 > 48KB | 두 개의 청사진 id 또는 너무 많은 rules: | 하나의 id (Adapt); 블록당 2-3개의 규칙 |
| UI가 흐릿해 보임 | 변환 스케일링 또는 작성된 크기보다 카메라 쪽으로 밀림 | .kit-zoom, 가장 가까운 크기로 작성 |
| 3D가 갑자기 평평해짐 | 3D 조상에 opacity/filter/overflow | 잎사귀로 이동 |
| 이음매 팝 | 한 쪽이 공유 프레임을 유지하지 않음 | seam-check.py, 표류하는 쪽의 플레이트/포즈 수정 |
| 이음매가 몇 % 실패하지만 프레임이 동일하게 보임 | 플레이트 아래 리그 사본이 그림자를 두 배로 만듦 | 유지하는 동안 플레이트만 표시됨 (frame.md 이음매) |
| 제품 UI가 실제 앱과 다름 | 기억 또는 부분적인 CSS에서 재구축됨 | 실제 앱을 2배로 캡처 (surfaces.md 첫 번째 섹션) |
duplicate_audio_track 경고 800개+ | data-duration 없는 SFX | assemble.py 재실행 |
music-search가 자격 증명이 없다고 표시 | 로그인하지 않음 | 사용자가 터미널에서 npx hyperframes auth login 실행 |
| 렌더링이 ~1 프레임/초로 느림 | 소프트웨어 GPU (Studio 내보내기) | render-final.mjs (CLI, --browser-gpu --workers 6) |
| 중지 후에도 렌더링이 계속 실행됨 | 셸 중지가 node를 중지하지 않음 | 프로세스 트리 종료 (verify-ship.md) |
| 렌더링 진행률이 표시되지 않음 | 출력이 ` | tail`로 파이프됨 |
자체 테스트
node <skill>/scripts/selftest.mjs
임시 폴더에 2개 장면 영화를 스캐폴드한 다음, 하네스, 조립, check, seam-check, 하드웨어 GPU 렌더링(1920x1080, 60fps) 및 웹 인코딩을 실행합니다. 반드시 PASS를 출력해야 합니다. 새 머신에서 그리고 편집 후에 실행하세요.