HyperFrames — HTML을 소스로 삼아 AI 에이전트가 직접 영상을 만드는 프레임워크
HeyGen이 만든 HyperFrames로 HTML·CSS·GSAP만으로 비디오를 작성하고 Puppeteer+FFmpeg로 렌더링하는 실전 가이드
2026-08-12 · 최초 발행 2026-04-21
HyperFrames는 HeyGen이 만든 HTML 기반 비디오 렌더링 프레임워크로, AI 에이전트가 직접 코드로 영상을 제작할 수 있도록 설계된 도구다. 기존 비디오 편집 GUI 대신 HTML, CSS, GSAP 애니메이션으로 영상을 작성하고, Puppeteer + FFmpeg가 이를 MP4로 렌더링한다. Claude Code, Cursor, Gemini CLI와 네이티브 통합을 지원해 에이전트 우선(Agent-first) 워크플로우를 구현한다.
개요
HyperFrames의 핵심 아이디어는 단순하다. HTML이 비디오의 소스가 되고, 별도의 편집 툴이나 독점 DSL 없이 data-* 속성으로 타이밍을 정의하며 GSAP으로 애니메이션을 작성한다. 이렇게 만든 HTML을 Puppeteer + FFmpeg가 프레임별로 캡처해 MP4로 인코딩하는 구조다.
기존 방식과 비교하면 차이가 뚜렷하다.
| 기존 방식 | HyperFrames |
|---|---|
| 비디오 편집 GUI 사용 | HTML + CSS + JS로 작성 |
| 독점 포맷으로 재현 어려움 | 동일 입력 = 동일 출력 (결정론적) |
| 자동화 파이프라인 구축 어려움 | CLI 완전 지원, 에이전트 친화적 |
| 코드로 관리 불가 | Git으로 버전 관리 가능 |
패키지는 모노레포로 구성되며 각 패키지가 역할을 나눠 갖는다.
설치 및 환경 설정
시스템 요구사항은 Node.js 22 이상, 그리고 로컬 렌더링 시 FFmpeg다.
FFmpeg는 OS별로 이렇게 설치한다.
# macOS
brew install ffmpeg
# Ubuntu / Debian
sudo apt-get install ffmpeg
# Windows (Chocolatey)
choco install ffmpeg
새 프로젝트는 init 명령 하나로 시작한다.
npx hyperframes init my-video
cd my-video
이 명령은 다음 구조를 생성한다.
my-video/
├── index.html # 메인 컴포지션
├── assets/ # 미디어 파일 (영상, 이미지, 오디오)
└── compositions/ # 서브 컴포지션 HTML 파일
환경이 제대로 갖춰졌는지는 doctor 명령으로 확인한다.
npx hyperframes doctor
Node.js, FFmpeg, 의존성 설치 상태를 자동으로 진단한다.
핵심 개념
**컴포지션(Composition)**은 data-composition-id를 가진 <div> 요소로, 하나의 비디오 장면 또는 전체 영상에 해당한다.
<div data-composition-id="my-video" data-width="1920" data-height="1080">
<!-- 클립들 -->
</div>
**클립(Clip)**은 컴포지션 안에 배치되는 개별 요소다. <video>, <audio>, <img>, <div> 모두 클립이 될 수 있다.
<video
id="clip-1"
data-start="0"
data-duration="5"
data-track-index="0"
src="hero.mp4"
muted
playsinline
></video>
<div id="title-1" data-start="1" data-duration="4" data-track-index="1">
안녕하세요
</div>
**타임라인(Timeline)**은 GSAP 타임라인으로 애니메이션을 정의한다. 항상 { paused: true }로 시작하고, window.__timelines에 등록해야 한다.
const tl = gsap.timeline({ paused: true });
tl.from(
"#title-1",
{ y: 50, opacity: 0, duration: 0.6, ease: "power3.out" },
0.3,
);
window.__timelines["my-video"] = tl;
**트랙(Track)**의 data-track-index는 클립의 레인(lane)을 지정한다. 같은 트랙 인덱스의 클립은 시간이 겹치면 안 되며, 시각적 레이어링은 CSS z-index로 별도 제어한다.
프로젝트 구조
기본 index.html은 이런 형태를 갖춘다.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #000;
}
</style>
</head>
<body>
<!-- 루트 컴포지션: <template> 없이 직접 <body>에 배치 -->
<div data-composition-id="main" data-width="1920" data-height="1080">
<!-- 비디오 클립 -->
<video
id="bg-video"
data-start="0"
data-duration="10"
data-track-index="0"
src="assets/background.mp4"
muted
playsinline
></video>
<!-- 텍스트 오버레이 -->
<div id="headline" data-start="1" data-duration="9" data-track-index="1">
<div class="scene-content">
<h1 class="title">HyperFrames로 만드는 비디오</h1>
<p class="subtitle">HTML이 비디오의 소스입니다</p>
</div>
</div>
<style>
[data-composition-id="main"] {
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
font-family: "Inter", sans-serif;
}
.scene-content {
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
height: 100%;
padding: 120px 160px;
gap: 24px;
box-sizing: border-box;
}
.title {
font-size: 96px;
font-weight: 700;
color: #ffffff;
line-height: 1.1;
}
.subtitle {
font-size: 40px;
color: #a0a0a0;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<script>
window.__timelines = window.__timelines || {};
const tl = gsap.timeline({ paused: true });
tl.from(
"#headline .title",
{ y: 60, opacity: 0, duration: 0.7, ease: "power3.out" },
1.2,
);
tl.from(
"#headline .subtitle",
{ y: 40, opacity: 0, duration: 0.5, ease: "power2.out" },
1.5,
);
window.__timelines["main"] = tl;
</script>
</div>
</body>
</html>
데이터 속성 레퍼런스
모든 클립에 공통으로 쓰이는 속성은 다음과 같다.
| 속성 | 필수 | 설명 | 예시 |
|---|---|---|---|
id |
필수 | 고유 식별자 | "clip-1" |
data-start |
필수 | 시작 시간(초) 또는 클립 ID 참조 | "0", "clip-1", "clip-1 + 2" |
data-duration |
img/div/컴포지션은 필수 | 지속 시간(초). 비디오/오디오는 미디어 길이가 기본값 | "5" |
data-track-index |
필수 | 트랙 레인 번호 | "0", "1", "2" |
data-media-start |
선택 | 소스 미디어의 시작 오프셋(초) | "3.5" |
data-volume |
선택 | 볼륨 (0~1, 기본값 1) | "0.7" |
컴포지션 요소에만 붙는 속성은 별도다.
| 속성 | 필수 | 설명 |
|---|---|---|
data-composition-id |
필수 | 고유 컴포지션 ID |
data-width |
필수 | 픽셀 너비 |
data-height |
필수 | 픽셀 높이 |
data-composition-src |
선택 | 외부 HTML 파일 경로 (서브 컴포지션) |
data-start는 절대 시간뿐 아니라 다른 클립을 기준으로도 지정할 수 있다.
<!-- 절대 시간 -->
<div id="a" data-start="3" ...></div>
<!-- 다른 클립 ID 참조 (해당 클립이 끝나는 시점에 시작) -->
<div id="b" data-start="a" ...></div>
<!-- 오프셋 포함 참조 -->
<div id="c" data-start="a + 1.5" ...></div>
<div id="d" data-start="b - 0.5" ...></div>
컴포지션 구조 작성법
해상도는 용도에 따라 셋 중 하나를 고른다.
| 용도 | 크기 |
|---|---|
| 가로형 (유튜브, 발표용) | 1920 × 1080 |
| 세로형 (쇼츠, 릴스, TikTok) | 1080 × 1920 |
| 정방형 (인스타그램) | 1080 × 1080 |
하나의 컴포지션 안에서 여러 씬은 시간 순서로 배치한다.
<!-- 씬 1: 0~7초 -->
<div id="scene-1" data-start="0" data-duration="7" data-track-index="1">
<div class="scene-content">
<h2 class="s1-title">첫 번째 씬</h2>
</div>
</div>
<!-- 씬 2: 7~14초 -->
<div id="scene-2" data-start="7" data-duration="7" data-track-index="1">
<div class="scene-content">
<h2 class="s2-title">두 번째 씬</h2>
</div>
</div>
미디어 (비디오 / 오디오)
비디오는 반드시 muted playsinline을 포함해야 한다. 오디오는 별도의 <audio> 요소로 분리한다.
<!-- 영상 (무음) -->
<video
id="el-v"
data-start="0"
data-duration="30"
data-track-index="0"
src="assets/clip.mp4"
muted
playsinline
></video>
<!-- 오디오 (같은 소스 파일에서 분리) -->
<audio
id="el-a"
data-start="0"
data-duration="30"
data-track-index="2"
src="assets/clip.mp4"
data-volume="1"
></audio>
이렇게 분리하는 이유는 프레임워크가 미디어 재생을 완전히 제어해야 하기 때문이다. 비디오 play()/pause()를 직접 호출하는 것은 금지된다.
미디어 트리밍은 data-media-start로 소스의 시작 오프셋을 지정한다.
<!-- 소스 영상의 5초 지점부터 10초간 재생 -->
<video
id="trimmed"
data-start="0"
data-duration="10"
data-media-start="5"
data-track-index="0"
src="assets/long-video.mp4"
muted
playsinline
></video>
이미지 클립은 다음과 같이 배치한다.
<img
id="logo"
data-start="2"
data-duration="6"
data-track-index="2"
src="assets/logo.png"
style="position: absolute; top: 40px; right: 80px; width: 200px;"
/>
GSAP 애니메이션
기본 규칙은 다섯 가지다.
- 타임라인은 항상
{ paused: true }— 프레임워크가 재생을 제어 - 반드시
window.__timelines에 등록 — 프레임워크가 이 객체를 읽음 - 시각적 속성만 애니메이션 —
opacity,x,y,scale,rotation,color,backgroundColor visibility,display애니메이션 금지repeat: -1금지 — 무한 반복은 캡처 엔진을 망가뜨림
gsap.from()은 등장 애니메이션에 쓴다. CSS 위치가 최종 위치이고, from()으로 그 위치까지 오는 여정을 정의한다.
// 아래에서 올라오며 등장
tl.from(
"#title",
{ y: 60, opacity: 0, duration: 0.7, ease: "power3.out" },
0.3,
);
// 왼쪽에서 슬라이드인
tl.from("#card", { x: -80, opacity: 0, duration: 0.5, ease: "expo.out" }, 0.5);
// 확대되며 등장
tl.from(
"#badge",
{ scale: 0.7, opacity: 0, duration: 0.4, ease: "back.out(1.7)" },
0.6,
);
gsap.to()는 퇴장 애니메이션이며, 마지막 씬에서만 허용한다.
// 마지막 씬에서만 퇴장 허용
tl.to(
"#final-title",
{ opacity: 0, y: -30, duration: 0.5, ease: "power2.in" },
9.5,
);
반복 애니메이션이 필요하면 repeat: -1 대신 컴포지션 지속 시간을 기반으로 반복 횟수를 계산한다.
const compositionDuration = 15; // 초
const cycleDuration = 2; // 애니메이션 1사이클 길이
const repeatCount = Math.ceil(compositionDuration / cycleDuration) - 1;
tl.to(
"#pulse",
{
scale: 1.1,
duration: 1,
ease: "sine.inOut",
yoyo: true,
repeat: repeatCount,
},
0,
);
여러 씬을 하나의 타임라인에 담으면 이런 구조가 된다.
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<script>
window.__timelines = window.__timelines || {};
const tl = gsap.timeline({ paused: true });
// 씬 1 등장
tl.from(
".s1-title",
{ y: 50, opacity: 0, duration: 0.7, ease: "power3.out" },
0.3,
);
tl.from(
".s1-subtitle",
{ y: 30, opacity: 0, duration: 0.5, ease: "power2.out" },
0.6,
);
tl.from(
".s1-stat",
{ scale: 0.8, opacity: 0, duration: 0.4, ease: "back.out(1.7)" },
0.8,
);
// 씬 2 등장 (씬 1이 끝난 후)
tl.from(
".s2-heading",
{ x: -60, opacity: 0, duration: 0.6, ease: "expo.out" },
7.3,
);
tl.from(
".s2-body",
{ x: -40, opacity: 0, duration: 0.5, ease: "power2.out" },
7.6,
);
window.__timelines["main"] = tl;
</script>
씬 전환 (Scene Transitions)
전환에는 3가지 절대 규칙이 있다.
- 모든 씬 사이에 전환 효과 — 점프컷 금지
- 모든 씬에 등장 애니메이션 — 요소가 갑자기 나타나는 것 금지
- 전환 전 퇴장 애니메이션 금지 — 전환 효과 자체가 퇴장 역할을 함
전환 전에 퇴장 애니메이션을 넣으면 전환 시점에 빈 화면이 생기는 잘못된 패턴이 된다.
// ❌ 전환 전에 퇴장 애니메이션 → 전환 시점에 빈 화면
tl.to("#s1-title", { opacity: 0, y: -40, duration: 0.4 }, 6.5);
tl.to("#s1-subtitle", { opacity: 0, duration: 0.3 }, 6.7);
// 전환이 빈 프레임에서 시작됨
올바른 방식은 등장 애니메이션만 정의하고 전환 효과가 퇴장을 대신 처리하도록 두는 것이다.
// ✅ 씬 1 등장만 정의 — 전환이 퇴장을 처리
tl.from(
"#s1-title",
{ y: 50, opacity: 0, duration: 0.7, ease: "power3.out" },
0.3,
);
tl.from(
"#s1-subtitle",
{ y: 30, opacity: 0, duration: 0.5, ease: "power2.out" },
0.6,
);
// 7.2초에 전환 효과가 씬 전체를 가져감
// ✅ 씬 2 등장 — 전환 후 시작
tl.from(
"#s2-heading",
{ x: -40, opacity: 0, duration: 0.6, ease: "expo.out" },
8.0,
);
크로스페이드 같은 전환 효과는 별도의 오버레이 클립으로 구현한다.
<!-- 전환 오버레이 클립 -->
<div
id="trans-1-2"
data-start="6.5"
data-duration="1.5"
data-track-index="10"
style="position: absolute; inset: 0; background: #000; opacity: 0; z-index: 100;"
></div>
// 페이드 아웃 (씬 1 가리기)
tl.to("#trans-1-2", { opacity: 1, duration: 0.5, ease: "power2.in" }, 6.5);
// 페이드 인 (씬 2 드러내기)
tl.to("#trans-1-2", { opacity: 0, duration: 0.5, ease: "power2.out" }, 7.5);
서브 컴포지션
복잡한 프로젝트는 씬을 별도 HTML 파일로 분리할 수 있다. 서브 컴포지션은 반드시 <template> 태그로 감싸야 한다.
<!-- 서브 컴포지션은 반드시 <template> 태그로 감싸야 한다 -->
<template id="intro-template">
<div data-composition-id="intro" data-width="1920" data-height="1080">
<div id="intro-title" data-start="0" data-duration="8" data-track-index="0">
<div class="scene-content">
<h1 class="main-title">인트로 씬</h1>
</div>
</div>
<style>
[data-composition-id="intro"] {
background: #0a0a0a;
font-family: "Inter", sans-serif;
}
.scene-content {
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
height: 100%;
padding: 120px 160px;
box-sizing: border-box;
}
.main-title {
font-size: 120px;
font-weight: 800;
color: #ffffff;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<script>
window.__timelines = window.__timelines || {};
const tl = gsap.timeline({ paused: true });
tl.from(
"[data-composition-id='intro'] .main-title",
{
y: 80,
opacity: 0,
duration: 0.8,
ease: "power3.out",
},
0.3,
);
window.__timelines["intro"] = tl;
</script>
</div>
</template>
루트 index.html에서는 이 서브 컴포지션을 다음과 같이 불러온다.
<div
id="intro-clip"
data-composition-id="intro"
data-composition-src="compositions/intro.html"
data-start="0"
data-duration="8"
data-track-index="0"
></div>
루트 컴포지션(index.html)은 <template> 없이 <body>에 직접 배치한다. <template>은 서브 컴포지션에서만 사용한다.
CLI 명령어
개발 워크플로우는 미리보기부터 시작한다.
# 새 프로젝트 생성
npx hyperframes init my-project
# 라이브 프리뷰 시작 (브라우저 자동 열림, 핫 리로드)
npx hyperframes preview
# 특정 포트 지정
npx hyperframes preview --port 3001
검증과 린팅은 렌더링 전 필수 단계다.
# 컴포지션 구조 검증 (타이밍, 속성 오류 감지)
npx hyperframes lint
# WCAG 명암비 감사 + 애니메이션 검사
npx hyperframes validate
# 명암비 검사 건너뜀 (빠른 반복 작업 시)
npx hyperframes validate --no-contrast
렌더링은 다음 명령으로 실행한다.
# MP4로 렌더링
npx hyperframes render
# 출력 파일명 지정
npx hyperframes render --output my-video.mp4
# Docker로 렌더링 (로컬 FFmpeg 불필요)
npx hyperframes render --docker
# 해상도 오버라이드
npx hyperframes render --width 1080 --height 1920
미디어 처리(자막 생성, TTS)도 CLI 안에서 해결된다.
# 오디오 파일에서 자막 생성 (Whisper 기반)
npx hyperframes transcribe assets/voiceover.mp3
# 텍스트를 음성으로 변환 (Kokoro-82M TTS)
npx hyperframes tts "안녕하세요, HyperFrames입니다." --output assets/narration.mp3
# 언어 지정
npx hyperframes tts "Hello, World." --lang en --output assets/en-narration.mp3
전환·오버레이·데이터 시각화 등 50개 넘는 컴포넌트를 카탈로그에서 바로 설치할 수 있다.
# 카탈로그에서 블록 설치 (전환, 오버레이, 데이터 시각화 등 50+종)
npx hyperframes add lower-third
npx hyperframes add progress-bar
npx hyperframes add kinetic-title
환경 진단은 언제든 다시 돌릴 수 있다.
# Node.js, FFmpeg, 의존성 상태 체크
npx hyperframes doctor
비주얼 아이덴티티
HyperFrames는 컴포지션 작성 전에 반드시 비주얼 아이덴티티를 정의하도록 요구한다. DESIGN.md 파일은 다음 구조를 갖춘다.
## Style Prompt
다크 배경에 선명한 색상 대비를 사용하는 모던 테크 스타일.
빠른 모션과 샤프한 타이포그래피가 특징.
## Colors
- `#0a0a0a` — 배경 (Primary Background)
- `#ffffff` — 주 텍스트 (Primary Text)
- `#6366f1` — 강조색 (Accent / Brand)
- `#a1a1aa` — 보조 텍스트 (Secondary Text)
- `#1e1e2e` — 카드/패널 배경 (Surface)
## Typography
- 헤드라인: `Inter` (700~900 weight)
- 본문: `Inter` (400~500 weight)
## What NOT to Do
- 밝은 배경 사용 금지
- 4개 이상의 색상 동시 사용 금지
- 12px 이하 폰트 사용 금지
- 선형 그라디언트를 전체 배경으로 사용 금지 (H.264 밴딩 발생)
- 애니메이션 없이 요소가 등장하는 것 금지
기본 제공되는 비주얼 스타일 프리셋은 8가지다.
| 스타일 | 특징 |
|---|---|
| Swiss Pulse | 그리드 기반, 극도로 절제된 타이포그래피 |
| Velvet Standard | 럭셔리, 깊은 다크 톤, 골드 포인트 |
| Deconstructed | 레이어드, 오버래핑, 혼돈미 |
| Maximalist Type | 타이포그래피가 주인공, 텍스트로 가득 |
| Data Drift | 데이터 시각화 중심, 테크니컬 |
| Soft Signal | 페이스텔, 부드러운 그라디언트, 따뜻함 |
| Folk Frequency | 핸드드로운 질감, 유기적 형태 |
| Shadow Cut | 필름 느와르, 하이 콘트라스트 그림자 |
레이아웃 작성 원칙
"레이아웃 먼저, 애니메이션 나중" 원칙
잘못된 방식은 요소를 오프스크린 위치에 두고 GSAP으로 최종 위치로 이동시키는 것이다 — 이러면 레이아웃이 추측에 의존하게 된다. 올바른 방식은 두 단계다.
- 요소가 가장 잘 보이는 순간(히어로 프레임)을 CSS로 먼저 정의
gsap.from()으로 그 위치까지 들어오는 경로를 정의
CSS 레이아웃 템플릿은 다음처럼 패딩으로 콘텐츠를 안으로 밀어넣는 방식을 쓴다.
/* scene-content는 패딩으로 내용을 안으로 밀어넣음 */
.scene-content {
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
height: 100%;
padding: 120px 160px; /* 콘텐츠를 프레임 안으로 위치시킴 */
gap: 24px;
box-sizing: border-box;
}
하드코딩된 픽셀값과 절대 위치는 다른 해상도에서 깨지므로 금지 패턴이다.
/* ❌ 하드코딩된 픽셀값과 절대 위치 */
.scene-content {
position: absolute;
top: 200px;
left: 160px;
width: 1920px; /* 다른 해상도에서 깨짐 */
}
렌더링된 비디오에서 가독성을 보장하려면 텍스트 스타일에 최소 크기를 지켜야 한다.
| 요소 | 최소 크기 |
|---|---|
| 대형 헤드라인 | 60px 이상 |
| 본문 텍스트 | 20px 이상 |
| 데이터 레이블 | 16px 이상 |
품질 검사
린팅은 다음 명령으로 실행한다.
npx hyperframes lint
이 명령은 필수 data-* 속성 누락, 같은 트랙에서 클립 시간 겹침, window.__timelines 미등록, repeat: -1 사용을 검사한다.
명암비 검사(WCAG AA)는 다음처럼 실행한다.
npx hyperframes validate
5개 타임스탬프에서 스크린샷을 찍고, 텍스트 요소 뒤의 배경 픽셀을 샘플링해 명암비를 계산한다.
⚠ WCAG AA 명암비 경고 (2건):
· .subtitle "보조 텍스트" — 2.67:1 (4.5:1 필요, t=3.2s)
· .caption "캡션" — 3.01:1 (4.5:1 필요, t=7.8s)
기준은 일반 텍스트 4.5:1 이상, 큰 텍스트(24px+ 또는 19px+ 볼드)는 3:1 이상이다.
애니메이션 맵은 별도 스크립트로 생성한다.
node skills/hyperframes/scripts/animation-map.mjs ./my-video \
--out ./my-video/.hyperframes/anim-map
animation-map.json에서는 각 트윈의 요약 설명과 이동 경로, ASCII 간트 차트(전체 타임라인 시각화), 스태거 간격 감지, 1초 이상 애니메이션 없는 구간(의도적인지 확인 필요), offscreen·collision·invisible 플래그를 확인할 수 있다.
AI 에이전트 통합
HyperFrames는 에이전트 우선(Agent-first) 설계로, Claude Code, Cursor, Gemini CLI와 직접 통합된다. 스킬 설치는 한 줄이면 된다.
npx skills add heygen-com/hyperframes
제공되는 스킬(슬래시 커맨드)은 다음과 같다.
| 스킬 | 역할 |
|---|---|
/hyperframes |
컴포지션 작성 전체 가이드 (타이밍, GSAP 규칙, 씬 전환) |
/hyperframes-cli |
CLI 명령어 레퍼런스 |
/hyperframes-registry |
카탈로그 블록 검색 및 설치 |
/website-to-hyperframes |
URL → 비디오 변환 파이프라인 |
/gsap |
GSAP 애니메이션 API 패턴 |
에이전트 워크플로우는 자연어 요청에서 시작해 렌더링까지 이어진다.
실전 예제
예제 1: 간단한 텍스트 애니메이션
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
</head>
<body>
<div data-composition-id="text-demo" data-width="1920" data-height="1080">
<div
id="bg"
data-start="0"
data-duration="8"
data-track-index="0"
style="position:absolute; inset:0; background:#0a0a0a;"
></div>
<div id="content" data-start="0" data-duration="8" data-track-index="1">
<div class="scene-content">
<p class="eyebrow">HYPERFRAMES</p>
<h1 class="headline">비디오의 미래는<br />HTML입니다</h1>
<p class="body-text">
코드로 작성하고, 에이전트가 편집하고, 파이프라인이 렌더링합니다.
</p>
</div>
</div>
<style>
[data-composition-id="text-demo"] {
font-family: "Inter", sans-serif;
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
}
.scene-content {
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
height: 100%;
padding: 160px 200px;
gap: 32px;
box-sizing: border-box;
}
.eyebrow {
font-size: 24px;
font-weight: 600;
letter-spacing: 0.3em;
color: #6366f1;
text-transform: uppercase;
}
.headline {
font-size: 110px;
font-weight: 800;
color: #ffffff;
line-height: 1.05;
}
.body-text {
font-size: 36px;
color: #a1a1aa;
max-width: 900px;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<script>
window.__timelines = window.__timelines || {};
const tl = gsap.timeline({ paused: true });
tl.from(
".eyebrow",
{ y: 30, opacity: 0, duration: 0.5, ease: "power2.out" },
0.4,
);
tl.from(
".headline",
{ y: 70, opacity: 0, duration: 0.8, ease: "power3.out" },
0.7,
);
tl.from(
".body-text",
{ y: 40, opacity: 0, duration: 0.6, ease: "power2.out" },
1.1,
);
// 마지막 씬: 페이드 아웃
tl.to(
"#content",
{ opacity: 0, duration: 0.6, ease: "power2.in" },
7.2,
);
window.__timelines["text-demo"] = tl;
</script>
</div>
</body>
</html>
예제 2: 비디오 + 자막 오버레이
<div data-composition-id="video-caption" data-width="1920" data-height="1080">
<!-- 배경 영상 -->
<video
id="main-video"
data-start="0"
data-duration="20"
data-track-index="0"
src="assets/interview.mp4"
muted
playsinline
></video>
<!-- 오디오 분리 -->
<audio
id="main-audio"
data-start="0"
data-duration="20"
data-track-index="2"
src="assets/interview.mp4"
data-volume="1"
></audio>
<!-- 로워 서드 (자막 바) -->
<div id="lower-third" data-start="2" data-duration="5" data-track-index="3">
<div class="lower-third-wrap">
<div class="speaker-name">김철수</div>
<div class="speaker-title">HyperFrames 개발팀 리드</div>
</div>
</div>
<style>
[data-composition-id="video-caption"] {
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
font-family: "Inter", sans-serif;
}
.lower-third-wrap {
position: absolute;
bottom: 120px;
left: 120px;
border-left: 6px solid #6366f1;
padding-left: 24px;
}
.speaker-name {
font-size: 52px;
font-weight: 700;
color: #ffffff;
}
.speaker-title {
font-size: 32px;
font-weight: 400;
color: #a1a1aa;
margin-top: 8px;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<script>
window.__timelines = window.__timelines || {};
const tl = gsap.timeline({ paused: true });
// 로워 서드 등장
tl.from(
"#lower-third .lower-third-wrap",
{ x: -60, opacity: 0, duration: 0.5, ease: "power3.out" },
2.2,
);
// 로워 서드 퇴장 (마지막 요소이므로 허용)
tl.to(
"#lower-third .lower-third-wrap",
{ x: -60, opacity: 0, duration: 0.4, ease: "power2.in" },
6.5,
);
window.__timelines["video-caption"] = tl;
</script>
</div>
자주 하는 실수 (금지 사항)
| 실수 | 올바른 방법 |
|---|---|
window.__timelines 등록 안 함 |
반드시 window.__timelines["id"] = tl 등록 |
| 비디오에서 오디오 사용 | muted 비디오 + 별도 <audio> 요소 분리 |
data-layer 속성 사용 |
data-track-index 사용 |
data-end 속성 사용 |
data-duration 사용 |
repeat: -1 사용 |
Math.ceil(duration / cycle) - 1 계산 |
setTimeout 안에서 타임라인 구성 |
동기적으로 타임라인 빌드 |
video.play() / audio.play() 직접 호출 |
프레임워크에 재생 제어를 맡김 |
Math.random() 사용 |
Mulberry32 등 시드 기반 PRNG 사용 |
| 전환 전 퇴장 애니메이션 | 전환이 퇴장을 처리 — 퇴장 트윈 제거 |
루트 컴포지션에 <template> 사용 |
<body>에 직접 배치 |
텍스트에 <br> 강제 줄바꿈 |
max-width로 자연스러운 줄바꿈 |
.scene-content에 position: absolute |
width: 100%; height: 100%; padding: 사용 |
| 비디오 요소 치수 애니메이션 | 래퍼 <div> 애니메이션 |
후속 씬 요소에 gsap.set() 사용 |
타임라인 내 tl.set(selector, vars, time) 사용 |
빠른 참고 카드
# 프로젝트 생성
npx hyperframes init my-video && cd my-video
# 개발
npx hyperframes preview
# 검증
npx hyperframes lint && npx hyperframes validate
# 렌더링
npx hyperframes render --output output.mp4
# AI 스킬 설치
npx skills add heygen-com/hyperframes
// 타임라인 필수 패턴
window.__timelines = window.__timelines || {};
const tl = gsap.timeline({ paused: true });
// ... 트윈 추가 ...
window.__timelines["composition-id"] = tl;
마무리
HyperFrames는 HTML을 비디오의 소스로 삼는 독창적인 접근으로, AI 에이전트가 코드로 직접 영상을 제작하는 워크플로우를 현실화한다. data-* 속성 기반의 타임라인 정의, GSAP 애니메이션, Puppeteer + FFmpeg 렌더링 파이프라인이 결합해 결정론적이고 버전 관리 가능한 비디오 제작 환경을 제공한다. 에이전트 친화적 CLI와 슬래시 커맨드 스킬을 활용하면 자연어 요청에서 MP4 출력까지 전 과정을 자동화할 수 있어, 반복적인 영상 콘텐츠 제작 파이프라인 구축에 적합한 프레임워크다.