동영상 설명 제작
당신은 모션 디자이너가 코드로 동영상을 만드는 방식으로 작업합니다: 각 장면은 DOM/SVG를 구축하고 일시정지된 GSAP 타임라인에 트윈을 추가하는 작은 JS 모듈입니다. 렌더러는 해당 타임라인을 프레임 단위로 탐색하고(t = frame / fps), 각 프레임을 스크린샷으로 캡처한 후 프레임을 ffmpeg로 파이프 전송합니다. 출력은 결정론적입니다: 동일한 프로젝트는 항상 동일한 프레임을 렌더링합니다.
SKILL_DIR은 이 스킬의 기본 디렉토리(이 파일이 들어 있는 폴더)를 의미합니다.
설정 (기기당 한 번)
cd SKILL_DIR/scripts && npm install && npx playwright install chromium
또한 ffmpeg가 PATH에 있어야 합니다. 모든 스크립트는 종속성이 누락된 경우 이 명령을 출력합니다.
워크플로우
- 먼저 스토리보드를 작성합니다. 주제, 대상, 목표 길이 및 음성 해설 포함 여부에 동의한 후
video.json에 장면 목록을 작성합니다. 각 장면에는narration(음성 해설 내용)과visual(화면에 표시될 내용)이 포함됩니다.reference/storyboard.md를 읽어보세요. 스토리보드가 자체적으로 이해될 때까지 장면 코드를 작성하지 마십시오. - 스캐폴딩:
node SKILL_DIR/scripts/new.mjs <project-dir> --title "..."를 실행한 후,video.json을 편집하고scenes/<id>.js에 장면당 하나의 모듈을 작성합니다. 레시피는reference/animation-patterns.md를, 레이아웃, 서체 및 색상은reference/design.md를 참조하세요. 음성 해설이 있는 경우 장면 길이를"auto"로 설정하고cue('phrase')로 애니메이션을 시간 동기화합니다. 두 가지 예제(ElevenLabs를 사용한examples/kubernetes-overview/및 Gemini를 사용한examples/how-dns-works/)는 모두 음성 해설이 포함된 완성된 동영상입니다. - 음성 해설(원하는 경우):
reference/voiceover.md를 따르세요. 간단히 말해,video.json에"voice"블록을 추가하고,node SKILL_DIR/scripts/narration.mjs status <project-dir>를 실행한 후, 나열된 각 장면에 대해 정확히 해당 인수로 OpenRouter MCPgenerate-speech도구를 한 번 호출하고,narration.mjs add로 각 저장된 파일을 등록합니다. 실제 오디오는 장면 길이와cue()타이밍을 변경하므로 확인 단계(4단계) 전에 이 작업을 수행하세요. - 확인:
node SKILL_DIR/scripts/lint.mjs <project-dir>를 실행합니다. Chromium에서 모든 장면을 로드하고 로드 오류, 장면보다 긴 애니메이션,tl외부에서 생성된 애니메이션, 프레임 외부 요소, 잘린 텍스트 및 장면에 비해 너무 긴 내레이션을 보고합니다. 의도적인 것이 아니라면 모든 경고를 수정하세요. - 미리보기:
node SKILL_DIR/scripts/render.mjs <project-dir> --contact-sheet를 실행한 후 PNG를 읽습니다(<project-dir>/out/contact-sheet.png). 계층 구조, 겹침, 가독성, 빈 프레임, 타이밍을 확인합니다. 세부 사항은--stills 12.5,30과 같은 특정 순간을 렌더링합니다. 올바르게 보일 때까지 반복하고, 마지막 편집 후 한 번 더 연락처 시트 패스를 수행합니다. - 렌더링:
node SKILL_DIR/scripts/render.mjs <project-dir>→<project-dir>/out/video.mp4. 내레이션 클립이 자동으로 믹스됩니다. 사용자에게 경로, 길이 및 장면 목록을 제공합니다.node SKILL_DIR/scripts/serve.mjs <project-dir>로 대화형 미리보기(스크러버, 재생/일시 정지, 내레이션 재생,c는 캡션 전환,m은 음소거)를 할 수 있습니다.
유용한 렌더링 플래그: --out file.mp4, --from 10 --to 20(부분 렌더링), --scale 0.5(빠른 초안), --workers N, --crf 18, --audio track.mp3(내레이션 대신 자체 오디오 트랙 사용), --no-voice(무음), --audio-codec aac(기본값은 MP3이며 VS Code에서도 재생 가능).
프로젝트 레이아웃
my-video/
video.json # 제목, 너비, 높이, fps, 시드, 전환, 테마, 장면[]
scenes/<id>.js # 장면당 하나의 모듈
assets/ # 이미지/SVG, '/assets/x.svg'로 참조
audio/ # 내레이션 클립 + manifest.json(narration.mjs에서 생성; 비용이 들었으므로 유지)
style.css # 선택 사항, 있으면 자동 로드
out/ # 렌더링 결과(contact-sheet.png, stills/, video.mp4)
video.json 장면 필드: id(필수, 기본 파일 이름 scenes/<id>.js이기도 함), duration(초 단위 또는 "auto": 내레이션 오디오에 맞춤, 오디오가 없을 경우 읽기 속도 추정치, 애니메이션보다 짧지 않음), narration, visual, 선택적 file 및 선택적 transition("fade"(기본값) 또는 "cut"). 최상위 "voice" 블록은 reference/voiceover.md에 설명되어 있습니다.
장면 계약
import { h, reveal, pop } from '/__runtime/helpers.js';
export default function ({ el, tl, gsap, theme, width, height, random, onFrame, cue, voice }) {
const title = h('div', { class: 'h1' }, '안녕하세요');
el.append(h('div', { class: 'center' }, title));
reveal(tl, title, { at: 0.3 }); // tl 시간 0 = 이 장면의 시작
tl.to(title, { color: 'var(--accent)', duration: 0.5 }, cue('핵심 아이디어')); // 해당 문구가 말해질 때
}
el: 이 장면의 컨테이너입니다. 전체 1920×1080 스테이지를 덮으며 사용자 대신 표시/숨김이 처리됩니다.tl: 이 장면의 타임라인입니다. 모든 모션은tl에 있어야 합니다(또는 여기에 추가된 다른 타임라인).gsap.to(...), CSSanimation/transition,setTimeout,setInterval,requestAnimationFrame,<video>등 실제 시간에 의존하는 요소를 직접 사용하지 마십시오. 렌더러는 타임라인 재생 헤드만 이동시키므로, 다른 것은 MP4에서 고정되거나 동기화가 깨집니다.cue(phrase, offset = -0.15)는phrase(대소문자 구분 안 함, 이 장면의 내레이션에 나타나야 함)가 말해지는 장면 시간을 반환합니다. 오디오가 있으면 클립의 측정된 일시 정지와 정렬되므로 절을 시작하는 문구는 실제 단어와 일치합니다. 문구를 찾을 수 없으면 오류를 발생시킵니다. 내레이션과 동기화된 모든at에 사용합니다.voice는 이 장면의 음성에 대해{ start, duration, end, estimated }를 제공합니다(예:voice.end까지 루핑 효과 유지).- 프레임별 그리기(캔버스, 프로시저 모션)의 경우
onFrame((sceneTime) => draw(sceneTime))을 사용합니다. 장면이 표시되는 동안 매 프레임마다 호출됩니다. - 오버레이(선택 사항):
video.json의"overlay": "scenes/_overlay.js"는 전체 동영상의 모든 장면 위에 그려지는 모듈을 추가합니다. 타임코드, 진행률 표시줄, 로고 또는 프레임 테두리에 사용합니다.{ el, tl, onFrame, scenes, duration, ... }을 받으며,onFrame((t) => ...)은 전역 시간을 받고scenes는 각 장면의start/end를 나열합니다. random()은 장면별로 시드되며,Math.random도 시드되므로 임의 레이아웃을 재현할 수 있습니다.- 함수는
async일 수 있습니다(예:fetch('/assets/data.json')). 글꼴은 장면이 실행되기 전에 로드되므로 텍스트 측정이 가능합니다. - 테마 색상은 CSS 변수입니다:
var(--bg) --surface --surface2 --fg --muted --accent --accent2 --accent3 --accent4 --danger, 추가로--font및--mono.video.json→"theme": { "accent": "#ff3366" }에서 재정의할 수 있습니다. - GSAP 플러그인이 전역적으로 등록됩니다: DrawSVG, MotionPath, MorphSVG, SplitText, Text, ScrambleText, CustomEase.
품질 기준
- 장면당 하나의 아이디어. 최소 2~3초마다 화면에서 무언가가 움직이거나 변경되어야 하며, 모든 애니메이션은 설명하는 내레이션 라인과 동기화되어야 합니다(
cue()사용; 내레이션은 약 초당 2.5단어로 진행됨). - 텍스트는 크게: 본문 ≥ 40px, 레이블 ≥ 26px(1920×1080 스테이지 기준). 안전 영역(
.safe, 96/128px 여백) 내에 유지하세요. - 완성된 다이어그램을 한 번에 보여주기보다는 조각별로 구성하세요.
- 각 장면 끝에 페이드 전에 짧은 유지 시간을 남겨 최종 상태를 읽을 수 있도록 하세요.
음성 해설
오디오는 OpenRouter MCP 서버(generate-speech)에서 가져옵니다. 이 스킬은 계획 및 기록만 담당합니다. 해당 도구를 사용할 수 없는 경우, 음성 해설을 위해 OpenRouter MCP 서버 연결이 필요하다고 사용자에게 알리고, 그동안 음성 없이 렌더링하세요. reference/voiceover.md에 전체 절차, 모델 및 음성 선택, 비용이 나와 있습니다.