Communitygithub.com

MooT020/kinetic-typography

운동감 타이포그래피 비디오 — 큰 단어들이 시각적 요소가 되어 음성 또는 음악 비트에 맞춰 부딪히고, 늘어나고, 쌓이고, 분할되고, 교체됩니다. 9:16 또는 16:9 MP4로 렌더링됩니다. 텍스트만으로 설명, 후크, 타이틀 시퀀스, 인용문, 가사/시 비디오, 공지사항, 단어별 캡션을 메인 디자인으로 사용하세요.

kinetic-typography란 무엇인가요?

이 스킬은 단어 자체가 화면의 주인공이 되는 운동감 타이포그래피(Kinetic Typography) 비디오를 제작하는 에이전트를 정의합니다. 사용자는 스크립트를 1-4개의 단어로 구성된 구문(phrase)으로 분할하고, 각 구문마다 하나의 히어로 단어를 지정해야 합니다. 에이전트는 HyperFrames + GSAP(기본값) 또는 Remotion을 기반으로 HTML/CSS/JS 템플릿을 생성하며, Node.js와 FFmpeg만으로 실행됩니다. 주요 옵션으로는 포맷(9:16 세로, 16:9 가로, 정사각형), 지속 시간, 톤(펀치, 차분, 혼란), 리듬(고정 템포, 음성, 음악 비트)이 있습니다. 이 스킬은 50-80%의 프레임 너비를 채우는 큰 텍스트, 한 번에 하나의 아이디어, 극적인 명암비, 리듬감 있는 움직임(슬램, 슬라이드 스택, 스트레치, 문자 폭포, 플립 스왑, 스플릿)을 강조합니다. 에이전트는 빌드 전에 종횡비, 단어와 타이밍, 리듬 소스, 히어로 단어와 움직임을 사용자에게 확인하고, 최종 렌더링 전에 스냅샷 검증 및 자기 비평을 수행해야 합니다.

지원 대상✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/MooT020/takhayyal-video-skills/tree/HEAD/plugins/motion-design-styles/skills/kinetic-typography

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

운동감 타이포그래피 (Kinetic Typography)

단어 자체가 그림이 되는 비디오를 만듭니다. 한 구문이 나타나 프레임을 채우고, 강하게 부딪힌 후 다음 구문으로 교체되며 리듬을 탑니다. 마치 움직이는 포스터처럼 대담하고 빠르며 음악적이어야 합니다.

옵션

옵션기본값참고
format세로 1080×1920또는 가로 1920×1080, 정사각형 1080×1080
duration10–20 초음성 또는 노래 구간에 맞춤
tonepunchycalm (0.4초 슬라이드 및 페이드, 적은 뒤집기), punchy (기본값), chaotic (모두 대문자, 부딪힘, 흔들림, 매 두 번째 구문마다 뒤집기)
rhythm고정 템포또는 음성 단어 시간, 또는 음악 비트

브리핑 (계획 전에 답변)

  1. 시청자가 기억해야 할 한 문장은 무엇인가요?
  2. 스크립트를 1–4개 단어의 구문으로 나누고, 각 구문마다 하나의 히어로 단어를 표시하세요.
  3. 에너지가 최고조에 달하는 지점(색상 뒤집기가 발생하는 구문)은 어디인가요?
  4. 마무리 잠금 장면(타이틀, 행동 유도 문구, 핸들)은 무엇인가요?

읽기 시트 (비트 목록과 함께 작성)

각 비트마다 시청자가 반드시 이해해야 할 한 가지("로봇은 날 수 없다", "캐시가 누락되었다")와 그것이 읽을 수 있는 상태로 유지되는 시간을 작성하세요. 한 번에 하나의 읽기입니다. 텍스트 바닥: 레이블은 최소 0.8초, 문장은 화면에 완전히 나타난 후 단어당 약 0.3초입니다. 텍스트가 시간에 맞지 않으면 속도를 높이지 말고 단어를 줄이세요.

모션 규율

  • 모션은 인과적입니다. 모든 움직임에는 화면에 이유(클릭, 낙하, 패킷 도착)가 있으며, 다음 움직임은 그로부터 이어집니다.
  • 전체 비디오의 주요 이동 방향 하나를 선택하고 유지하세요. 다른 방향은 의미에 사용하세요: 위쪽은 성공 또는 결론, 축소는 "여기 전체 그림이 있습니다".
  • 장면의 종료는 다음 항목을 설정합니다: 동일한 축, 동일한 방향, 유사한 속도. 크로스페이드 대신 하나의 오브젝트를 변경점을 가로질러 가져오세요.
  • 다이어그램, 차트 또는 텍스트에 유휴 필러 모션(떠다니기, 숨쉬기, 흔들리기)은 없습니다. 아무것도 움직이지 않아야 하면 멈추고, 의도적인 카메라 드리프트 하나면 충분합니다.
  • 이징: 항목은 부드러운 이즈 아웃(power2.out ~ power4.out), 종료는 더 빠른 이즈 인(항목 시간의 약 75%). 항목은 최대 0.8초. 그룹의 총 지연 시간은 최대 0.5초. 오버슈트(back.out)는 작은 팝에만 사용; 탄성이나 바운스는 사용하지 마세요.
  • 중요할 때는 한 번에 하나의 것만 움직입니다. 눈은 하나만 따라갈 수 있습니다.
  • 결정적 순간 직전에 0.3–0.75초의 정적 비트를 추가하세요.

스타일 규칙 (항상)

이것이 이 스타일을 만듭니다. 색상이나 단어와 관계없이 유지하세요.

  1. 텍스트는 프레임 너비의 50–80%를 채웁니다. 작은 텍스트는 캡션으로 읽히고 시각적 요소로 읽히지 않기 때문입니다.
  2. 한 번에 한 가지 아이디어: 1–4개 단어, 최대 3줄. 다른 모든 것은 사라졌습니다.
  3. 모든 단어는 움직임(슬램, 슬라이드, 스트레치, 마스크 공개, 플립)과 함께 도착합니다. 아무것도 단순히 페이드 인하지 않습니다.
  4. 변화는 리듬에 맞춰 발생합니다: 0.25–0.7초마다 새 단어 또는 움직임, 키워드는 비트 또는 강세 음절에.
  5. 대비는 극단적입니다: 거의 검은색 위에 거의 흰색 또는 그 반대, 핵심 단어에만 하나의 악센트 색상.
  6. 각 구문에는 하나의 히어로 단어가 있습니다: 가장 크거나, 악센트 색상이거나, 나머지와 다르게 움직입니다.

기본값 (자유롭게 변경)

글꼴, 색상, 단어, 타이밍 및 배경. 사용자가 브랜드 글꼴이나 색상을 제공하면 그것이 우선합니다. 우선 순위: 사용자의 브랜드 또는 사양, 명시적 요청, 그 다음 이 기본값.

  • 배경 #111111, 텍스트 #f4f1ea, 악센트 #ff4d2e. 에너지를 위해 구문마다 밝음 위에 어두움 또는 어두움 위에 밝음으로 뒤집습니다.
  • 디스플레이 글꼴: 무겁고 좁은 얼굴(Anton, Bebas Neue 또는 Archivo Black), 히어로 단어는 모두 대문자. 지원 글꼴: 작은 연결 단어를 위한 무거운 산세리프(Inter 800).
  • 크기: 9:16에서 히어로 단어 180–320 px; 작은 단어 60–90 px.

설정 (한 번)

node -v                      # 22+ 필요
ffmpeg -version
npx hyperframes init my-video --non-interactive --resolution portrait && cd my-video   # 16:9는 landscape

기존 빈 폴더 안에서는 npx hyperframes init . --non-interactive --resolution portrait를 사용한 다음, 생성된 index.html을 아래 템플릿으로 교체하세요.

빌드 전 결정

코드를 작성하기 전에 이것들을 한 줄로 사용자에게 다시 알리세요.

  1. 종횡비: 9:16 (1080×1920) 또는 16:9 (1920×1080). 기본값은 9:16입니다.
  2. 단어 및 타이밍: 스크립트를 1–4개 단어의 구문으로 나눕니다. 음성이 있는 경우, 단어 타임스탬프(npx hyperframes transcribe voice.wav)에서 시간을 가져옵니다. 없는 경우, 각 구문에 0.6–1.4초를 주고 히어로 단어에는 더 긴 시간을 줍니다.
  3. 리듬 소스: 음성(강세 음절에 맞춤), 음악(비트에 맞춤; npx hyperframes beats music.mp3), 또는 고정 템포(예: 120 BPM = 0.5초마다 비트).
  4. 구문당 히어로 단어 및 어떤 움직임을 받을지.
필요 항목HyperFrames + GSAP (기본값)Remotion
단어 및 문자 움직임, 마스크, 스트레치예예
음성 또는 비트 데이터 기반 타이밍예 (레이블)예 (프레임)
Node와 FFmpeg만으로 실행예예
라이선스무료최대 3명까지 무료, 그 이상은 유료

움직임 어휘

움직임방법 (GSAP)용도
Slamfrom {scale: 2.6, opacity: 0}, expo.out, 0.22 초, 6px 화면 흔들림 0.1 초 추가히어로 단어
Slide stack줄 from {yPercent: 110} 내부 overflow:hidden 마스크, 지연 0.06 초, power4.out 0.35 초목록, 두 줄 구문
StretchscaleY: 1.6 → 1, transformOrigin: bottom, back.out(3) 0.3 초강조, 동사
Letter cascade문자 from {y: -120, rotation: -20, opacity: 0}, 지연 0.025 초, back.out(2)이름, 제목
Flip swap이전 단어 rotationX: 90 out (0.12 초), 새 단어 rotationX: -90 → 0 in (0.18 초)X가 Y가 됨
Split단어 반쪽이 x: ±140로 미끄러져 작은 단어를 그 사이에 드러냄대조
ExityPercent: -110 또는 scale: 0.7, opacity: 0, 0.15 초, power2.in모든 구문이 떠남

종료는 빠르고(0.12–0.18초), 입장은 생각보다 더 빠릅니다(0.18–0.35초). 각 구문이 도착한 후 최소 0.35초 동안 유지하여 읽을 수 있게 하세요.

시선을 사로잡는 방법

  • 프레임을 채우세요. 단어를 글꼴 크기가 아닌 프레임 너비에 맞게 크기를 조정하세요. 250+ px의 좁은 글꼴이 룩입니다.
  • 색상 뒤집기: 배경이 2–3개의 핵심 구문에서 어두움과 밝음(또는 악센트) 사이를 비트의 하드 컷으로 전환합니다.
  • 질감과 깊이: 미세한 그레인 오버레이, 히어로 단어 뒤의 부드러운 그림자 또는 오프셋 복제본(악센트 색상, 오프셋 10–14 px), 그리고 배경에 가끔 거대하고 흐릿한 단어.
  • 카메라 에너지: 전체 스테이지에 느린 2–4% 스케일 드리프트, 히어로 히트에서 펀치인(스케일 1.06, 0.1 초 동안), 슬램에서 6–10 px 흔들림.
  • 단어와 함께하는 도형: 그려지는 밑줄 막대, 단어 뒤에 나타나는 원, 화살표. 악센트 색상, 평면.
  • 잠금 장면으로 끝내기: 완전히 구성된 제목 또는 행동 유도 문구를 1–1.5초 유지, 시작 프레임과 일치시켜 루프되도록 합니다.
  • 품질 게이트: 스냅샷에 평범한 배경에 작은 중앙 정렬 텍스트가 보이면 완료된 것이 아닙니다.

빌드: 템플릿

<!doctype html><html><head><meta charset="UTF-8">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Anton&family=Inter:wght@800&display=swap">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<style>
html,body{margin:0;width:1080px;height:1920px;overflow:hidden;background:#111}
#root{width:100%;height:100%;position:relative;overflow:hidden}
#stage{position:absolute;inset:0;display:flex;align-items:center;justify-content:center}
.phrase{position:absolute;left:60px;right:120px;top:270px;bottom:670px;display:flex;flex-direction:column;justify-content:center;align-items:center;gap:0}
.line{overflow:hidden;line-height:.92;padding:0 8px}
.w{display:inline-block;font-family:Anton,sans-serif;text-transform:uppercase;color:#f4f1ea;white-space:nowrap}
.hero{color:#ff4d2e}
.flip .w{color:#fff}.flip .hero{color:#111}
.small{font-family:Inter,system-ui,sans-serif;font-weight:800;text-transform:none}
#bg{position:absolute;inset:0;background:#111}
</style></head>
<body><div id="root" data-composition-id="main" data-start="0" data-duration="8" data-width="1080" data-height="1920">
<div id="bg"></div><div id="stage"></div></div>
<script>
// [time, lines, moveName, heroWordIndex, flipBackground?]  -- 이 목록을 편집하세요; 시간(초)
const PHRASES = [
  [0.2, ["WORDS"], "slam", 0],
  [1.0, ["can", "MOVE"], "stack", 1],
  [2.0, ["LIKE", "MUSIC"], "stretch", 1, true],
  [3.2, ["one", "idea"], "stack", 1],
  [4.2, ["AT A", "TIME"], "slam", 1],
  [5.4, ["KINETIC", "TYPE"], "cascade", 1],
];
const END = 8, W = 1080 - 180;
// 웹 폰트가 로드된 후에만 텍스트를 측정한 다음 타임라인을 빌드하고 등록합니다.
document.fonts.ready.then(() => {
const tl = gsap.timeline({ paused: true });
const stage = document.getElementById("stage");
PHRASES.forEach(([t, lines, move, hero, flip], i) => {
  const p = document.createElement("div"); p.className = "phrase" + (flip ? " flip" : ""); stage.appendChild(p);
  const words = lines.map((txt, k) => {
    const line = document.createElement("div"); line.className = "line";
    const w = document.createElement("span"); w.className = "w" + (k === hero ? " hero" : "") + (txt === txt.toLowerCase() && k !== hero ? " small" : "");
    w.textContent = txt; line.appendChild(w); p.appendChild(line);
    w.style.fontSize = "100px"; const fit = Math.min(420, (100 * W) / w.getBoundingClientRect().width); // 너비 채우기
    w.style.fontSize = (w.classList.contains("small") ? Math.min(fit, 110) : fit) + "px";
    return w;
  });
  gsap.set(p, { autoAlpha: 0 });
  tl.set(p, { autoAlpha: 1 }, t);
  if (move === "slam") tl.from(words, { scale: 2.6, opacity: 0, duration: 0.22, ease: "expo.out", stagger: 0.08 }, t)
                         .fromTo("#stage", { x: -10 }, { x: 10, duration: 0.04, yoyo: true, repeat: 3, ease: "none" }, t + 0.18).set("#stage", { x: 0 }, t + 0.34);
  if (move === "stack") tl.from(words, { yPercent: 110, duration: 0.35, ease: "power4.out", stagger: 0.07 }, t);
  if (move === "stretch") tl.from(words, { scaleY: 1.7, opacity: 0, transformOrigin: "50% 100%", duration: 0.3, ease: "back.out(3)", stagger: 0.08 }, t);
  if (move === "cascade") words.forEach((w, k) => {
    const letters = Array.from(w.textContent); w.textContent = "";
    letters.forEach((c) => { const l = document.createElement("span"); l.style.display = "inline-block"; l.textContent = c; w.appendChild(l); });
    tl.from(w.children, { y: -140, rotation: -20, opacity: 0, duration: 0.4, ease: "back.out(2)", stagger: 0.03 }, t + k * 0.15); });
  const next = PHRASES[i + 1] ? PHRASES[i + 1][0] : null;
  if (flip) tl.set("#bg", { background: "#ff4d2e" }, t).set("#bg", { background: "#111" }, next || END);   // 키 구문에서 색상 뒤집기
  if (next) tl.to(words, { yPercent: -110, opacity: 0, duration: 0.15, ease: "power2.in", stagger: 0.03 }, next - 0.17)
               .set(p, { autoAlpha: 0 }, next);
});
tl.fromTo("#root", { scale: 1 }, { scale: 1.04, duration: END, ease: "none" }, 0);         // 느린 드리프트
tl.set({}, {}, END);
window.__timelines["main"] = tl;
});
</script></body></html>

PHRASES를 편집하여 단어와 시간을 변경하세요. 움직임 어휘에서 if (move === ...) 분기를 추가하여 움직임을 추가하세요. 음성이 있는 경우, 루트에 <audio> 요소를 넣고 구문 시간을 트랜스크립트에서 가져오세요.

사용자에게 노출할 매개변수

aspect · phrases (시간, 줄, 움직임, 히어로) · fontDisplay · fontSupport · colors (bg, text, accent) · tempo or audio · flipPhrases (배경이 뒤집히는 위치) · endLockup.

피해야 할 것

  • 페이드 인과 느린 드리프트를 주요 모션으로 사용하는 것. 슬라이드쇼로 읽힙니다.
  • 한 번에 4개 이상의 단어 또는 3줄을 화면에 표시하는 것.
  • 여러 개의 악센트 색상. 하나의 악센트, 히어로 단어에만 사용.
  • 히어로 단어에 얇거나 가벼운 글꼴 두께 또는 스크립트 글꼴.
  • 9:16 프레임의 하단 670px 또는 상단 270px 내의 텍스트 (플랫폼 UI가 덮음).
  • 무작위 또는 렌더링별 변형. 모든 값은 타임라인에서 가져와야 합니다.

실패 모드

모양원인수정
슬라이드쇼처럼 보임페이드, 긴 유지, 매번 같은 움직임어휘에서 움직임을 다양화; 입장 0.35초 미만; 종료 0.15초
디자인이 아닌 캡션처럼 보임단어가 너무 작고 빈 공간에 중앙 정렬됨단어를 프레임 너비에 맞게 크기 조정; 좁은 글꼴; 250+ px
지저분하고 읽을 수 없음한 번에 너무 많은 단어 또는 움직임화면에 하나의 구문; 하나의 히어로 단어; 도착 후 0.35초 유지
박자에서 벗어남구문 시간을 추측함트랜스크립트 또는 비트 시간 사용; 히트를 2–3프레임 일찍 착지
마스크에 의해 단어 잘림글꼴에 비해 line-height가 너무 좁음line-height 0.92–1.0, .line에 패딩 추가; 스냅샷 확인
렌더링에 잘못된 글꼴폰트가 로드되지 않음head에 Google Fonts <link>; 스냅샷에 좁은 얼굴이 표시되는지 확인

최종 렌더링 전에 확인

npx hyperframes check
npx hyperframes snapshot --at <구문이 도착한 후 0.3초 시점 하나당 한 번, 그리고 종료 중간에 한 번>
npx hyperframes render -o renders/<name>.mp4
ffprobe -v error -show_entries stream=width,height,r_frame_rate:format=duration -of csv=p=0 renders/<name>.mp4

모든 스냅샷과 snapshots/contact-sheet.jpg를 보고 다음을 확인하세요:

  • 모든 구문이 너비의 50–80%를 채우고 휴대폰 크기에서 읽을 수 있음
  • 구문당 하나의 히어로 단어, 악센트 색상 또는 다른 움직임
  • 어떤 프레임에서도 두 구문이 겹치지 않음
  • 모든 텍스트가 종횡비에 대한 안전 영역 내에 있음
  • 마무리 잠금 장면이 최소 1초 유지되고 시작과 일치함
  • 크기와 지속 시간이 요청과 일치함

최종 렌더링 전 자기 비평

스냅샷 시트에 1–10점으로 다음을 평가하세요: 후크(첫 1초), 휴대폰 너비에서의 가독성, 모션 품질, 구성, 정확성, "음소거 테스트"(시청자가 소리 없이 한 문장으로 요약할 수 있는가?). 7점 이하인 항목만 수정하고, 다시 스냅샷을 찍고 모든 항목이 8점 이상이 될 때까지 반복하세요. 가능하다면 별도의 읽기 전용 패스가 점수를 매기도록 하여 자신의 작업을 평가하지 않도록 하세요.

하지 말 것대신
두 개의 혼잡한 레이아웃 사이를 크로스페이드기존 콘텐츠를 종료한 다음 새 콘텐츠를 가져오거나, 하나의 오브젝트를 가로질러 가져오기
여러 개를 동시에 움직이기지연 시간을 두고, 하나의 초점

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

관련 스킬