Opus Visual Motion Engine은(는) 무엇을 하나요?
시각적 결과물을 위한 규율 있는 파이프라인: 계획 → 템플릿에서 구축 → 렌더 → QA → 수정 → 내보내기. 품질은 취향이 아니라 렌더 전후에 적용되는 게이트에서 나옵니다. 순서대로 단계를 따르세요. 작업이 "단순해 보인다"고 단계를 건너뛰지 마세요.
아래의 경로는 이 스킬의 디렉토리(제공된 skill_resources 베이스)를 기준으로 합니다. 이를 $SKILL이라고 부릅니다.
0. 먼저 읽기 (무조건)
코드를 작성하기 전에, 결과물과 일치하는 참고 자료를 읽으십시오. 해당 주제에 대해 권위 있는 자료입니다:
| 결과물 | 읽을 자료 |
|---|---|
| 비디오, 모션 그래픽, 설명 영상, 제품 비디오 | references/video-generation.md, references/motion-design.md, references/javascript-animation.md |
| 키네틱 타이포그래피 / 타이포 중심 작품 | 위의 비디오 세트 + references/typography.md |
| 랜딩 페이지, 대시보드, 목업, UI | references/visual-design.md, references/typography.md |
| 다이어그램, 개략도, 데이터 시각화 | references/visual-design.md §4 |
| 프레이밍 / 카메라 결정 | references/cinematography.md |
| 지속 시간 및 이징 | references/animation-primitives.md |
| 동기화할 음악 또는 음성 | SKILL.md §10 + references/video-generation.md §6 |
작업 예제는 examples/*.md에 있습니다. 브리핑과 가장 가까운 예제를 읽으십시오.
1. 코드 전에 계획 (작성하기)
HTML을 생성하기 전에 계획을 답변(또는 출력물 옆의 PLAN.md)에 작성하십시오. 머릿속에만 있는 계획은 카운트되지 않습니다.
UI / 페이지
- 핵심 메시지: 한 문장. 시청자가 3초 안에 이해해야 하는 것은?
- 명명된 방향: 이름, 4–6개의 명명된 16진수 토큰(의미 할당됨), 역할이 있는 서체, 형태 처리. 주제의 산업, 재료 및 관용구에서 파생하십시오. 장난감 가게와 핀테크 대시보드는 절대 방향을 공유하지 않습니다.
- 계층 구조: 모든 블록에 PRIMARY / SECONDARY / TERTIARY 태그를 지정하십시오. PRIMARY만이 하나의 대담한 동작을 얻습니다.
- 섹션 진행: 섹션당 한 줄로, 해당 섹션의 역할과 다음 섹션으로 전환하는 방법을 설명합니다.
- 렌더 전 수학: 텍스트 너비 ≈
chars × 0.55 × font-size(산세리프) 또는× 0.5(세리프); 상자 너비 =max(title_chars × 8, subtitle_chars × 7) + 24(14px 기준); 행 맞춤 = Σ너비 + 간격 ≤ 컨테이너. 모든 연결선을 모든 상자와 대조하여 추적하십시오. 상자와 교차하면 L자형 경로를 만드십시오.
비디오 / 모션 그래픽
- 핵심 메시지: 한 문장. 두 문장이 필요하다면 두 개의 비디오가 있는 것입니다.
- 비트: 3–7개. 각 비트는 "시청자가 이제 무엇을 알게 되었는가?"에 답합니다.
- 비트당 장면 카드: 시작/종료 (초), 구도, 카메라, 유형 크기, 기본 요소 (2–4), 종료.
references/motion-design.md§2의 템플릿을 사용하십시오. - 시그니처 모멘트: 메커니즘이 표시되는(레이블이 아닌) 하나의 프레임 이름을 지정하십시오(예: 잡음이 신호로 필터링됨). 스토리가 캡션에서만 작동한다면 비트를 재설계하십시오.
- 전체 타임라인: 절대 타임스탬프. 장면의 EXIT는 다음 장면이 공간을 차지하기 전에 완료되어야 합니다.
DURATION을 실제 종료 시간(기본 10–20초, 내레이션이 있는 경우에만 30–45초)으로 설정하십시오.
카드가 없는 장면은 비디오에 속하지 않습니다.
2. 템플릿에서 구축 (비디오)
assets/composition-template.html을 결과물 옆에 복사하고 EDIT 블록을 채우십시오. ENGINE 블록을 다시 작성하지 마십시오. 이미 다음을 제공합니다:
- 1920×1080
#stage(STAGE_W/H를 1080×1920 또는 1080×1080으로 변경 가능), 모든 창에 맞춤; window.DURATION,window.seek(t),window.render(t),window.SCENES: 캡처/내보내기 계약 (QA는SCENES에서 모든 장면을 캡처함);at(),ease.*,lerp,mulberry32(시드 PRNG), 그리고 가시성 및 EXIT를 위한layer();window.AUDIO(§10)를 읽고 없으면 0을 반환하는 오디오 헬퍼audioAt(t, "rms"|"low"),since(t, list),hit(t, list, dur);- 캡처 및 축소 동작 아래에서 자동으로 꺼지는 미리보기 플레이어 (space, ←/→, Home/End);
- 필요한 3개 레이어(밝음,
prefers-color-scheme보호,[data-theme])의 색상 토큰.
페이지의 경우, §6에 따라 일반 단일 파일 HTML을 작성하십시오.
3. 결정론적 시간 (비디오)
render(t)는 유일한 상태 드라이버입니다. 모든 위치, 불투명도, 크기, 숫자 및 dashoffset은 초 단위t에서 계산됩니다.seek(x)는 이전에 렌더링된 것과 관계없이 동일한 프레임을 렌더링합니다.t는 초 단위입니다.t를[0, DURATION]으로 고정하고, 절대[0, 1]로 고정하지 마십시오: 초를 0..1로 고정하면 전체 타임라인이 조용히 멈춥니다.- 금지:
setTimeout,setInterval, 타임라인으로서의 CSS 애니메이션/전환, 시드되지 않은Math.random(), 상태의Date.now().requestAnimationFrame은 재생 시계만 진행하고render(clock)을 호출할 수 있습니다. 템플릿이 이미 이 작업을 수행합니다. - 무작위성:
render내부가 아닌, 빌드 시mulberry32(seed)에서 생성하십시오. - SVG 그리기에는 세 가지 모두 필요합니다:
pathLength="1",stroke-dasharray="1", 그리고 1에서 0까지의stroke-dashoffset. dasharray가 없으면 프레임 0부터 선이 완전히 그려진 것처럼 나타납니다. inline-block단어 범위는 후행 공백을 삼킵니다. 간격은margin-right에 두십시오.
4. 모션 문법
각각 하나의 작업을 가진 다섯 가지 동작. 어떤 동작이 이 중 어느 것에도 해당하지 않으면 삭제하십시오.
- ENTER: 페이지 또는 장면당 하나의 오케스트레이션된 시퀀스, 읽기 순서로, 형제는 60–120ms 간격으로 시작됨. 모든 섹션에서 위로 페이드하는 것은 AI의 전형적인 징후입니다.
- EMPHASIS: 한 번에 한 요소: 스케일 팽창 (≤1.2), 광선 또는 의미를 전달하는 색상 변경.
- TRANSITION: 상태 변경. 변경된 부분만 움직입니다. 나머지는 정지. 0.2–0.6초.
- CAMERA: 무대 수준의 스케일/이동, 장면당 최대 한 번, 0.8–1.2초, 텍스트 읽기 중에는 절대 안 됨.
- EXIT: 0.3–0.5초, 완전히 완료됨. 어떤 요소도 장면 경계를 넘어 중간 상태로 남아 있지 않아야 합니다. 단, 동일하게 유지되거나 변형되는 의도적인 연속성 앵커는 예외입니다(동일한 객체의 상태 변경은 가장 강력한 전환입니다).
전환은 의미를 전달합니다: 하드 컷 = 카테고리 변경, 변형 = 같은 것이 변경됨, 팬 = 횡단. 절대 모든 곳에 기본 크로스페이드를 사용하지 마십시오. 지속 시간과 이징은 references/animation-primitives.md에 있습니다. 함께 움직이는 이웃은 다른 지속 시간 또는 오프셋을 얻습니다.
5. 스택 결정
| 상황 | 스택 |
|---|---|
| 페이지 마이크로 모션 | transform/opacity에 대한 CSS 전환, 0.2–0.6초; @media (prefers-reduced-motion: no-preference) 내부 ≤2초 루프 |
| 비디오 / 모션 그래픽 / 키네틱 타입 | 템플릿 + 바닐라 render(t) (기본값) |
| 6개 이상의 엇갈린 트랙 또는 SVG 변형 오케스트레이션 | 고정된 GSAP UMD, 하나의 일시 중지된 마스터 타임라인, seek(t) → tl.seek(t) |
| 차트 | 인라인 SVG (기본값) 또는 animation:false가 있는 Chart.js UMD, 16진수 색상 (캔버스는 CSS 변수를 읽을 수 없음) |
| 생성 예술 | randomSeed/noiseSeed가 있는 p5.js |
| 3D, 3D가 메시지인 경우에만 | 고정된 Three.js UMD; render(t)에서 스크립트된 카메라 |
목표에 도달하는 가장 간단한 스택을 사용하십시오. 외부 스크립트는 cdnjs.cloudflare.com 또는 cdn.jsdelivr.net의 정확한 고정 버전이어야 합니다. 다른 모든 것은 인라인으로 처리하십시오. 모든 글꼴은 실제 대체 스택을 가져야 합니다.
6. 기술적 바닥 (협상 불가)
- CSS와 JS가 인라인된 단일
.html, 16MB 미만, 이미지/데이터는data:URI 사용. :root토큰으로 색상 지정;:root:not([data-theme="light"])로 보호된@media (prefers-color-scheme: dark)아래 어두운 레이어 및:root[data-theme="dark"]아래 반복; 명시적인body배경색.viewport-fit=cover가 있는meta viewport;env(safe-area-inset-*)패딩; html/body에height:100%,100vh사용 금지; 넓은 블록은 자체overflow-x:auto내에서 스크롤; 그리드는minmax(0,1fr)사용.- 표시된 계산된 숫자는
Math.round,toFixed또는Intl.NumberFormat을 통과해야 합니다. 카운트업은 최종 값에 정확히 도달해야 합니다. - 접근성: 명도 대비 ≥4.5:1 (≥24px 텍스트의 경우 ≥3:1), 눈에 보이는 포커스,
prefers-reduced-motion준수 (템플릿은 최종 프레임을 표시함). - 읽기 쉬운 유형: 1080p 비디오 프레임에서 20px 미만의 텍스트 없음 (H/54). 모바일 페이지에서 14px 미만의 본문 텍스트 없음.
- 브라우저 저장소는 타임라인 상태를 절대 보유하지 않습니다. 저장소를 사용하는 경우 try/catch로 감쌉니다.
7. 일반적인 AI 디자인 피하기
브리핑에 자유가 있을 때, 이러한 기본값에 절대 사용하지 마십시오 (qa.mjs는 여러 가지를 자동으로 플래그 지정합니다):
| 특징 | 수정 |
|---|---|
크림색 #F4F1EA 계열 + 세리프 디스플레이 + 테라코타 #D97757 계열 강조 | 주제 자체 자료에서 가져온 팔레트 |
| 거의 검은색 + 하나의 산성 강조, 주제에 관계없이 사용 | 방향이 서면으로 정당화되는 경우에만 허용 |
| 동일한 둥근 카드, 모든 곳에서 하나의 반경, 동일한 부드러운 그림자 | 계층 구조별 반경 토큰; 카드는 경계가 있는 객체만 |
| 장식으로서의 그라데이션, 보라색 그라데이션, 글래스모피즘 | 평면 채우기; 연속적인 물리적 속성에 대해서만 최대 하나의 그라데이션 |
| 추적된 대문자 눈썹, 중간 점 메타 문자열, 링크의 "→" | 문장 케이스; 내용을 반복하는 레이블 삭제 |
| 기본값 Inter / 시스템 글꼴 | 이 주제에 대해 선택된 서체, 대체 스택 포함 |
| 헤드라인에서 강조된 단일 단어 | 강조 색상이 작품의 다른 곳에서 동일한 의미로 사용되는 의미 토큰인 경우에만 허용 (예: 산호 = 잡음) |
| 일반적인 히어로: 헤드라인 + 부제목 + CTA + 스크린샷 | 주제 세계에서 가장 특징적인 것으로 시작 |
| 모든 것 가운데 정렬, 모든 섹션에서 동일한 그리드 리듬 | 섹션별 정렬 시스템; 리듬 변경 |
| 모든 것이 한 번에 애니메이션, 모든 곳에서 300ms | 지속 시간 맵 + 스태거 (§4) |
한 곳에서 대담함을 투자한 다음, 하나의 액세서리를 제거하십시오.
8. 렌더 + QA (전달 전 필수)
자동화된 QA를 실행하십시오. 파일을 헤드리스로 렌더링하고 증거와 판정을 기록합니다. Node ≥18이 필요합니다. 스킬의 새 복사본에서 첫 번째 실행은 $SKILL/scripts에서 npm install이 필요합니다 (scripts/node_modules가 존재하면 건너뛰십시오).
node "$SKILL/scripts/qa.mjs" path/to/deliverable.html
Windows의 DSH에서는 pwsh 도구를 사용하여 동일한 명령으로 실행하십시오. 옵션: --step 0.25 (빠른 장면에 대한 더 미세한 그리드), --out <dir>, --mode page|video.
비디오 모드 (DURATION + seek가 존재할 때 자동)는 단계마다 프레임을 캡처하고 다음을 확인합니다: 금지된 API, 순서가 잘못된 재방문에 대한 결정론, 고정된 타임라인, 데드 에어, 거의 빈 범위, 해결되지 않은 최종 프레임, 텍스트 충돌, 다른 그래픽 위의 텍스트, 클리핑, 명도 대비, 최소 글꼴 크기, 절대 그려지지 않는 dash-offset 선, 축소 모션 루프, 콘솔/페이지 오류 및 템플릿 특징.
페이지 모드는 데스크톱 1440, 모바일 390 및 어두운 스킴을 렌더링하고, 동일한 레이아웃 규칙과 모바일 가로 오버플로를 확인합니다.
출력은 qa-<name>/에 저장됩니다: report.md (판정 및 결과), report.json (프레임별 잉크 범위 및 변경), contact-sheet.png (모든 프레임을 하나의 이미지에), moments.png (각 장면의 시작, 중간 및 끝, window.SCENES에서 가져옴), key_000..100.png 및 frames/.
- 시그니처 모멘트 및 동기화 지점을 명시적으로 전달하십시오:
--at 5.4,9.1. - 의도적인 레이어링 (의도적으로 쌓은 키네틱 타입, 푸티지 위의 제목)은
data-qa-allow-overlap속성이 있는 요소 내부에 있어야 합니다. 해당 서브트리에 대해서만 충돌 및 텍스트-오버-그래픽 검사를 건너뜁니다. 우발적 충돌을 무음 처리하는 데 절대 사용하지 마십시오. fontWARN은 선택한 패밀리가 로드되지 않았고 텍스트가 대체 글꼴에 있음을 의미합니다. 모든 너비가 잘못되었으므로 레이아웃을 판단하기 전에 수정하십시오.
종료 코드 0 = FAIL 없음. FAIL은 차단입니다. 모든 WARN은 수정되거나 비평에서 한 줄로 정당화되어야 합니다.
9. 비평 루프
- 모델이 이미지를 읽을 수 있는 경우:
contact-sheet.png를 연 다음, 잘못 보이는key_*.png를 여십시오. 의도가 아닌 픽셀을 판단하십시오. - 모델이 텍스트 전용인 경우:
report.md와report.json의 프레임별 테이블을 사용하십시오.coverage< 0.015는 프레임이 비어 있음을 의미합니다. 장면 전체에 걸친diff0은 아무것도 움직이지 않음을 의미합니다.diff의 급증은 컷을 표시합니다. 모든 장면 카드의 타임스탬프를 이러한 숫자와 교차 확인하십시오. - 각 질문에 "✓"가 아닌 구체적인 관찰로 답하십시오:
- 구성: 각 키 프레임에서 눈이 가장 먼저 가는 곳은 어디입니까? 그것이 메시지입니까?
- 계층 구조: PRIMARY가 명확합니까? 프레임당 정확히 하나의 중요한 움직이는 것이 있습니까?
- 메커니즘: 시그니처 모멘트가 캡션을 제거한 상태에서 아이디어를 보여줍니까?
- 연속성: 모든 핸드오프가 깔끔합니까? 경계에서 중간 상태인 것이 없고 앵커가 동일하거나 변형되고 있습니까?
- 타이밍: 오프셋이 있고, 기본 지속 시간이 없으며, 읽기 시간이 8단어당 ≥1.2초입니까?
- 타이포그래피 / 명도 대비 / 밀도: 계획당 크기, 프레임당 ≤5개의 경쟁 요소, 다크 모드에서 읽기 가능?
- 내러티브: 시청자가 한 번 본 후 핵심 메시지를 반복할 수 있습니까?
- 수정 →
qa.mjs재실행 → 재비평. QA가 통과하고 비평에 열린 항목이 없을 때까지 반복하십시오. - 최종 질문, 정직하게 답변: "이것이 의도적으로 디자인된 것처럼 보입니까, 아니면 단지 생성된 것처럼 보입니까?" "생성된" 경우: 무엇이 일반적인지 이름을 지정하고, 계획을 수정하고 (§1), 재구축하십시오. 첫 번째 렌더는 거의 최종 결과물이 아닙니다. 2–3회 생성을 예상하십시오.
10. 오디오 동기화 (음악 또는 음성이 있는 경우)
절대 귀로 타이밍을 추측하지 마십시오. 오디오를 데이터로 변환하고 render(t)가 이를 읽도록 하십시오:
node "$SKILL/scripts/audio-data.mjs" music.mp3 --inject composition.html # window.AUDIO를 HTML에 기록
AUDIO.beats(추정된 템포의 균일한 간격 그리드),AUDIO.onsets(히트, 음표, 음절), 및 프레임별AUDIO.rms/AUDIO.low(0..1;low는 <150 Hz 킥/베이스 밴드).- 템플릿 헬퍼로 모션 구동: 비트 펄스(EMPHASIS)를 위한
hit(t, AUDIO.beats, 0.2), 킥 반응 스케일을 위한audioAt(t, "low"), 히트당 공개를 위한since(t, AUDIO.onsets). 비트에 장면 컷 배치:AUDIO.beats에서 장면 시작 선택. - 동기화는 구성상 프레임 정확함: 펄스는 해당 비트 또는 그 이후의 첫 번째 프레임에 도달합니다 (≤1 프레임, 30fps에서 ≤33ms). 오디오가 t=0보다 늦게 시작하면
--offset <s>를 전달하십시오. - 수준은 누군가 듣기 전에 확인됩니다. 클리핑된 샘플은 종료 코드 1을 반환합니다. 샘플 피크가 -1 dBFS를 초과하면 경고합니다. AAC 인코딩이 클리핑하기 때문입니다. -18..-12 LUFS 밖의 음량은 보고됩니다 (플랫폼은 약 -14로 정규화).
--bpm은 템포 감지를 무시하고 제공된 BPM을 사용합니다.- 미리보기:
$SKILL/scripts/audio-data.mjs를--preview music.mp3없이 실행하여music.html을 만든 다음 브라우저에서 엽니다. 비트, onsets 및 RMS 파형이 타임라인에 표시되며, 음소거/솔로 및 재생/마커가 포함됩니다. - 오프닝에만 적합한 오디오는 0초보다 큰
audioOffset을 설정합니다.DURATION은 여기에 포함되지 않지만 오디오가 재생되는 프레임만 나타냅니다.
11. 내보내기
생성된 HTML을 비디오로 변환합니다:
node "$SKILL/scripts/export.mjs" path/to/deliverable.html --fps 30 --out video.mp4
- FFmpeg가 설치되어 있어야 합니다 (가장 가까운 FFmpeg가 PATH에 있어야 함).
--ffmpeg-binary로 재정의할 수 있습니다. - 기본 fps 30, crf 17, h264. WebP 가져오기가
--fps 60을 초과하지 않습니다. - 이미지 시퀀스의 경우
--format png. --frames N,M은 N에서 M까지의 프레임만 캡처합니다.--audio music.mp3는 출력에 오디오 트랙을 믹스합니다.--audio-delay및--audio-volume도 사용 가능합니다.- 렌더는 QA가 요구 사항을 전달한 후에만 실행하십시오. 미리 내보내지 마십시오.
"