motionkit은(는) 무엇을 하나요?
영화는 하나의 페이지와 하나의 스크립트로 구성되며, renderFrame(f)가 프레임 f(초당 30, 1920x1080)를 그리고 그 외에는 아무것도 하지 않습니다. 숨겨진 Chrome이 프레임을 단계별로 처리하고, ffmpeg가 인코딩하며(mp4는 초당 60프레임으로 고품질로 두 배 확장, 영화 1초당 약 4~5MB), 사운드트랙은 합성됩니다.
파일 위치
- 엔진: 이 파일 옆에 있는
engine/폴더(동작, 렌더러, 사운드, 검사, 폰트)입니다.$E로 부르고 전체 경로를 사용하세요. 읽기만 하고 절대 영화를 넣지 마세요(설치 시 자체node_modules를 추가하는데, 이는 예상된 동작입니다). - 작업 폴더: 사용자가 작업 중인 폴더입니다. 영화(
<name>.html,<name>.js),assets/,ref/,out-<name>/파일이 모두 여기에 위치하며, 모든 명령어는 여기에서 실행됩니다. 페이지는 엔진에서lib.js,moves.js,fonts/를 자동으로 찾습니다. 작업 폴더가 git 저장소라면out-*/와ref/를.gitignore에 추가하세요.
첫 번째: 설정 (머신당 한 번)
sh $E/setup.sh를 실행하세요. 다음만 확인합니다: bun, ffmpeg with ffprobe, python3, uv, Chromium 브라우저(Chrome, Chromium 또는 Edge;CHROME_PATH로 재정의 가능), 그리고 엔진의 하나의 패키지.Ready라고 출력되면 계속 진행하세요.engine package만 누락된 경우(새 설치 시 정상),sh $E/setup.sh --install을 실행하세요. 그러면 스킬 자체 폴더에 하나의 패키지만 가져옵니다. 도구나 브라우저가MISSING이면, 목록과 출력된 설치 명령어를 사용자에게 표시하고, 컴퓨터에 소프트웨어를 설치하기 전에 한 번 물어보세요.- 동의하면
sh $E/setup.sh --install을 실행하세요(macOS는 Homebrew, bun과 uv는 공식 설치 프로그램), 그런 다음 다시 확인하세요.직접 실행이라고 표시된 줄은 사용자의 비밀번호(macOS의sudo) 또는 수동 다운로드가 필요합니다. 절대 실행하지 말고 사용자에게 정확한 명령어를 제공하고 기다리세요. 설치가 실패하면 사용자에게 직접 실행할 명령어 하나를 알려주세요. - 작동하는지 확인: 작업 폴더에서
bun $E/build.ts hello를 실행하고out-hello/hello.mp4가 존재하는지 확인하세요.
macOS에서만 테스트되었습니다. 사용자가 Linux 또는 Windows를 사용 중이면 그렇게 알리세요.
복사할 작업 예제: $E/hello.js(비트 그리드 위의 영화, 모든 사운드는 클립에서, 풍경은 코드로 그림), $E/title.js(영상 위에 겹칠 투명 타이틀), $E/moves-demo.js(모든 동작을 한 번씩).
영화 제작: 클립 먼저
각 동작을 클립으로 한 번 선언하세요. 그림, 사운드, 읽기 시간 확인이 모두 그 한 줄에서 비롯됩니다.
const M = window.MOVES, B = M.brand, G = M.grid(120); // 120 bpm: 비트는 15프레임, 마디는 60프레임
const K = { line: M.clip("words", G.bar(1) + 2, 10, { text: "움직임을 복사하세요.", x: 960, y: 590, size: 110, fam: B.display, stretch: B.wide, weight: 600, color: B.paper, until: G.bar(2) }) };
// 씬에서: K.line.draw(X, f) K.line.on(f)는 표시되어야 할 때 true입니다.
window.timeline = () => ({ fps: 30, total: TOTAL, bpm: G.bpm, sections: [[0, "intro"], [4, "build"], [8, "drop"], [24, "outro"]], cues: M.cues(), captions: M.captions() });
출시 또는 제품 영화의 경우 먼저 아래 "출시 및 제품 영화"를 읽으세요. bun $E/new.ts <name>으로 영화를 시작하세요. 작업 폴더에 <name>.html과 <name>.js를 쓰며, hello 예제에서 복사됩니다. 클립, 씬, 카피를 다시 작성하세요. 그런 다음 bun $E/build.ts <name>은 순서대로: 타임라인, 사운드트랙(sound.py), 사운드 체크, 읽기 시간 체크(lint.py), 렌더링, 결합, 동기화 체크(sync.py)를 수행하고 out-<name>/<name>.mp4를 씁니다. bpm이 없는 타임라인은 audio.py에서 더 간단한 사운드트랙(컷만)을 얻습니다. 타임라인 스위치: sound: "fx"(동작 사운드만, 음악 없음), sound: "none", alpha: true(투명 배경, .mov 출력; EditMax 섹션 참조).
영화 스크립트의 구조
$E/hello.js가 모델입니다. 한 번 읽어보세요. 각 부분이 무엇인지:
lib.js의 전역 변수:W,H(1920, 1080),X(캔버스 컨텍스트),clamp,lerp,TAU,hash,noise,txt(ctx, text, x, y, size, weight, color, fam, italic, align),rr(둥근 사각형),vignette(strength, innerRadius).M.clip(move, start, length, options):start와length는 프레임 단위입니다.length는 동작이 재생되는 시간(얼마나 오래 머무르는지가 아님)입니다.{ draw(ctx, f), t(f) (0..1, 동작 전체), on(f), o (옵션) }을 반환합니다. 길이가 지나면 클립은 완료된 상태를 계속 그립니다. 숨기지 않습니다. 씬을 끝내거나if (K.x.on(f))로 숨기세요.on은start부터until까지 true입니다(until없으면 영원히). 전환의from내부에서 완료된 텍스트 동작을 표시하려면t = 1에서 그리세요:M.words(ctx, 1, K.line.o). 일회성 효과(burst,glitchCut)는K.x.t(f) < 1도 필요합니다.- 클립 내부의 호출 모양: 전환은 옵션에서
from과to를 사용합니다. 래퍼(punch,shake,sweep,smear)는draw를 사용합니다. 그 외 모든 것(pathDraw,burst,bars,chart,callout,logoDraw,bounce,ticker)은 옵션만 사용합니다.mesh와parallax는 배경입니다. 직접 호출하세요:M.mesh(ctx, f / 240, { colors }), 여기서t는 천천히 증가하는 숫자일 뿐입니다. - 텍스트 옵션:
x는 줄의 중앙(align: "center"가 기본값),y는 기준선입니다.stretch: B.wide는 넓은 컷을 제공하고,stretch: null은 끕니다. 아무것도 텍스트를 측정하거나 줄 바꿈하지 않습니다. 크기 150 넓게에서 13자는 약 1340픽셀을 차지하므로, 정지 프레임을 렌더링하고 모든 줄이 여백과 함께 프레임 안에 들어가는지 확인하세요. - 씬 테이블:
[첫 번째 프레임, draw(f), drawAfterFinish(f)].renderFrame은 컨텍스트 상태를 재설정하고(그 줄을 유지), 현재 씬을 그린 다음, 그레인과 비네트, 그리고 깨끗하게 유지해야 하는 항목(로고)을 위한 세 번째 슬롯을 그립니다. window.ready: 렌더러가 기다리는 프로미스:Promise.all([M.fonts(), M.levels("out-<name>/levels.json"), M.load({ ... })]).M.levels는M.eq에만 필요하며, 파일이 존재하기 전에 안전합니다.- 타임라인:
sections는[beat, name]입니다. 짧은 영화는[[0, "intro"], [8, "drop"], [16, "outro"]]만 사용할 수 있으며, 각drop시작은 묵음 하프 비트를 얻습니다(클립을mute:로 하거나 유지).sync는 컷과 베이스 히트가 일치해야 하는 프레임을 나열합니다. 드롭 이후 각 씬의 첫 번째 프레임을 사용하세요. "변경"은 영화의 일반적인 프레임 간 차이로 판단되므로, 동기화 프레임에서 정확히 끝나는 전환은 그곳에서 변경할 것이 없으며 체크에서NO CHANGE를 출력합니다. 짧은 플래시를 추가하거나 다음 동작을 그 프레임에서 시작하세요.cuts는 모든 씬 시작입니다. - 색상:
M.brand(B)에서 모든 것을 읽으세요.M.rgba(B.ink, 0.7)는 불투명도로 색상을 제공하고,M.mix(a, b, t)는 두 색상 사이의 색상을 제공합니다.M.setBrand는M.brand를 제자리에서 변경합니다. 클립이 선언되기 전에 호출하고accentOnDark도 설정하세요(어두운 배경에서 사용되는 강조 색상;accent를 따르지 않음). 넓은 컷이 없는 글꼴의 경우wide: null을 설정하세요. - 길이: 읽기 시간이 최소를 설정합니다. 3단어 줄은 나타난 후 약 1.3초가 필요하므로, 120bpm에서 마디당 한 줄입니다. 사운드트랙은 마지막 프레임 이후 약 0.5초 더 실행되고 거기서 잘립니다.
outro섹션에서 끝나도록 하여 컷이 그 꼬리에 떨어지도록 하세요. - 빌드 결과:
build.ts는 검사가 실패해도 0으로 종료됩니다. 실패는 위에 세부 정보와 함께WARNING:줄로 출력됩니다. 모든 경고를 실패로 처리하세요. 출력을 읽고(frame n/total진행률 줄은 필터링), 수정하고, 경고가 없을 때까지 다시 빌드하세요. 정지 프레임은out-<name>/fNNNN.jpg로 기록됩니다. 읽으세요.bun $E/pure.ts <name>는 프레임이 단독으로 그리고 다른 프레임 후에 동일하게 렌더링되는지 확인합니다. sound.py는 numpy가 필요하며 항상uv run을 통해 실행됩니다. 다른 Python 스크립트는 표준 라이브러리만 사용합니다.
출시 및 제품 영화: 제품 보여주기
배경에 큰 단어만 있는 영화는 기본적으로 읽힙니다. 효과적인 출시 영화는 깊이와 천천히 움직이는 카메라로 하나의 시각적 세계에 제품을 화면에 유지하고, 작고 간결한 텍스트를 사용하며, 컷당 2~6초(몇 마디) 동안 유지하고, 비트당 한 컷을 사용하지 않습니다. 출시 또는 제품 영화의 경우 hello에서 시작하지 마세요:
- 먼저 룩을 결정하세요: 하나의 배경 세계, 하나의 강조 색상, 하나의 서체 패밀리. 두 줄로 사용자에게 말하세요.
- 실제 사물의 6~8개 샷을 스토리보드로 작성하세요: 제품의 실제 화면이나 출력, 실제 명령어, 실제 코드, 실제 결과. 제품에 없는 인터페이스를 그리고, 숫자를 발명하거나, 다른 회사의 로고를 사용하지 마세요. 캡션은 작은 레이블(
txt, 모노, 왼쪽 아래)이며 헤드라인이 아닙니다. - 먼저 하나의 히어로 샷을 빌드하고, 정지 프레임을 렌더링한 후, 나머지를 빌드하기 전에 실제 출시 영화와 비교하세요.
페이지는 moves.js 다음에 stage.js를 로드해야 합니다(new.ts로 만든 페이지는 로드합니다). renderFrame을 M.frame(); M.reset(X);로 시작하세요. 스테이지 조각(모두 MOVES에 있음):
| 조각 | 설명 |
|---|---|
backdrop(ctx, f, { base, lights[], streak }) | 느린 빛의 웅덩이가 있는 거의 검은색: 모든 것이 놓이는 세계 |
glass(ctx, x, y, w, h, { r, top, bottom, rim, shadow }) | 빛의 테두리와 부드러운 그림자가 있는 어두운 둥근 패널 |
camera(ctx, cam, draw) with cam(a, b, t, ease) | 더 큰 씬의 한 지점을 봅니다: { x, y, zoom, rot }; 매크로 클로즈업에서 시작하여 뒤로 당기거나 천천히 표류 |
tilt(ctx, draw, { depth, focus, blur, fog, top, height, width, cx, roll }) | 전체 프레임 씬을 기울어진 평면으로 그리며, 먼 가장자리는 더 작고, 어둡고, 초점이 흐려짐 |
tiles(ctx, f, { cells: [{ draw(ctx, t), label, bg, len, hold }], cols, x, y, w, gap }) | 각각 동작을 실시간으로 재생하는 작은 화면의 그리드(타일 내부에 전환을 절대 넣지 마세요) |
term(ctx, f, { x, y, size, lines: [{ text, at, speed, prompt, mark: "ok" or "run", color }] }) | 프레임에서 타이핑하거나 나타나는 터미널 라인 |
code(ctx, { lines[], x, y, size, lead, hot, shown }) | 조용한 구문 색상과 하나의 강조된 줄이 있는 JavaScript |
wave(ctx, { peaks, x, y, w, h, played, beats[], blocks[] }) | 재생 헤드, 비트 틱, 레인의 클립이 있는 실제 사운드트랙의 모양 |
스테이지 조각은 자체 사운드를 만들지 않습니다. 타임라인에 큐를 수동으로 추가하세요: { f, kind, gain }, kind는 type, tick, pop, ding 등.
실제 노래에 맞추기
- 사용자는 음악 파일을 제공해야 하며 사용할 권리가 있어야 합니다. 그렇게 말하고, 라이센스가 요구하는 크레딧을 엔드 카드에 넣으세요. 스트리밍 사이트에서 노래를 절대 긁어오지 마세요.
uv run $E/beats.py <file>는 템포와 각 마디의 음량을 출력합니다. 마디 변경이 몇 마디 후에 있는 시작 부분에서 발췌를 선택한 다음,uv run $E/beats.py <file> --from <seconds> --len <seconds> --out beats.json는beats.json과beats.js를 씁니다.- 영화 스크립트 전에 페이지에서
beats.js를 로드하세요. 모든 씬 시작을 비트 번호로 작성하세요:const b = (n) => window.BEATS.frames[n]. 트랙이 변경되는 4비트(마디)마다 컷하세요. - 타임라인에서
music: { file, from },sound: "fx"(동작 자체 사운드는 음악 아래에 조용히 유지됨) 또는"none",bpm,sync: [실제 컷의 프레임]을 설정하세요. 빌드는 -16 LUFS로 트랙을 믹싱하고, 끝을 페이드 아웃하며, 각 컷이 프레임 내에 베이스 히트가 있는지 확인합니다. 그 검사를 통과하기 위해 플래시를 추가하지 마세요.
EditMax용 모션(비디오 편집기 타임라인의 클립)
사용자가 EditMax에서 편집 중이고 컷에 모션을 원할 때 사용하세요: 영상 위의 애니메이션 타이틀, 인트로 또는 아웃트로 카드, 샷 사이의 운동학적 유형 비트. 동일한 에이전트 세션에 두 가지가 필요합니다: 셸(렌더링용)과 EditMax의 MCP 도구(inspect_timeline, import_media, ...; EditMax의 에이전트 탭에서 설정). EditMax 자체 앱 내 채팅은 셸 명령어를 거부하므로 이 스킬을 실행할 수 없습니다. 그렇게 말하고 사용자에게 Claude Code와 같은 에이전트를 연결하라고 알리세요. EditMax 도구가 없거나 응답하지 않으면 어쨌든 클립을 렌더링하고 사용자에게 드래그 앤 드롭할 파일 경로를 제공하세요.
1. 경로를 선택하세요. EditMax 자체 도구가 모션을 커버할 때 선호하세요. 텍스트가 앱 내에서 편집 가능하게 유지되기 때문입니다:
| 원하는 것 | 사용 |
|---|---|
| 페이드, 슬라이드, 팝, 타자 또는 와이프로 나타나는 제목 또는 하단 3분의 1 | add_text / add_lower_third + set_text_animation |
| 두 클립 사이의 디졸브, 푸시, 슬라이드, 와이프 또는 아이리스 | set_transition |
| 이동, 크기 조절 또는 페이드되는 클립 | set_keyframe, set_clip_transform, set_clip_fades |
| 그 외 모든 것: 슬램, 스크램블, 스플릿-플랩, 워드 휠, 크로스아웃, 하이라이트, 경로 또는 로고 그리기, 차트, 카운터, 버스트, 다중 동작 시퀀스, 전체 인트로 또는 아웃트로 | 이 스킬로 클립 렌더링 |
2. 프로젝트를 읽으세요. inspect_timeline: 프레임 크기, 프레임 속도, 트랙, 모션이 배치될 시간을 기록하세요. 이 엔진은 1920x1080, 초당 30프레임으로 렌더링합니다. 다른 크기의 16:9 프로젝트는 깔끔하게 크기를 조정합니다. 세로 또는 정사각형 프로젝트의 경우, 클립이 레터박스되거나 set_clip_transform으로 확대 및 자르기되어야 하며, 기본 타이틀이 더 나을 수 있다고 명확히 말하세요.
3. 렌더링.
- 오버레이(영상 위에 위치):
bun $E/new.ts <name> --overlay,$E/title.js를 모델로 합니다. 타임라인에는alpha: true와sound: "fx"가 있습니다. 빌드는out-<name>/<name>.mov(투명 배경이 있는 ProRes 4444)를 씁니다. 규칙: 프레임 지우기(X.clearRect), 절대 채우지 마세요.grain,vignette,mesh,parallax또는 전체 프레임 전환 없음. 모든 영상에서 읽을 수 있도록 유형 아래에 부드러운 그림자 또는 플레이트를 넣으세요. 유형을 프레임의 중간 90% 안에 유지하세요. 끝에서 페이드 아웃하세요. - 전체 프레임(인트로, 아웃트로, 샷 사이의 카드):
bun $E/new.ts <name>,$E/hello.js를 모델로 합니다. 빌드는out-<name>/<name>.mp4를 씁니다. - 사운드(타임라인에서
sound:설정): 전체 사운드트랙(독립적인 인트로 또는 아웃트로)에 대해 생략하세요. 동작이 만드는 사운드만(영상, 음성 또는 사용자 음악 위에 재생되는 모든 것)에 대해"fx". 무음에 대해"none". 편집에 이미 음악이 있는 경우 사용자가 원하는 것을 물어보세요. - 빌드하고, 모든
WARNING을 수정하고, 진행하기 전에 정지 프레임을 읽으세요.
4. 파일을 지속되는 곳에 두세요. EditMax는 가져온 파일을 참조하고 복사하지 않습니다. 완성된 파일을 out-<name>/에서 사용자가 유지하는 폴더(한 번 묻고 ~/Movies/Motions/ 제안)로 복사하고 거기서 가져오세요.
5. 사용자의 컷을 움직이지 않고 배치하세요. 시간으로 insert_clip하는 것은 리핑입니다.
[이하 생략됨 – 원본 SKILL.md가 잘렸습니다]