HyperFrames Core
출력 프레임워크는 Remotion입니다. 컴포지션은 <Composition>과 함께 등록된 React 컴포넌트 트리입니다. 현재 프레임이 유일한 클럭입니다. 미디어 재생은 DOM이 아닌 Remotion의 <Video> / <Audio> 컴포넌트에 속합니다.
이 스킬은 기술 계약입니다: 하나의 프로젝트를 구축하는 방법입니다. 아래 본문은 빌드 가이드입니다. 주제별 세부 내용은 references/에 있으며, 필요 시 읽으십시오. 다른 고려 사항은 형제 도메인 스킬에 있습니다: hyperframes-animation, hyperframes-creative, media-use, hyperframes-cli, hyperframes-registry. /hyperframes의 기능 맵은 각 스킬이 다루는 범위를 설명합니다.
이식 참고. 이 계약은 이전의 HTML +
data-*+ 일시 중지된 GSAP 타임라인 계약을 대체했습니다.data-composition-id/data-start/data-duration을 가진index.html이 있거나window.__timelines을 설명하는 참조 파일이 있다면, 이전 계약을 보고 있는 것입니다. 건드리기 전에references/legacy-html-contract.md를 읽으십시오.
참조
| 파일 | 읽는 이유… |
|---|---|
references/minimal-composition.md | 가장 작은 렌더링 가능한 프로젝트 골격부터 시작 |
references/project-structure.md | src/ 레이아웃, 장면을 컴포넌트로 분할, 모놀리식 vs 모듈러 결정 |
references/timing.md | 초를 프레임으로 변환, <Sequence> 배치, <Series> / <TransitionSeries> 사용, 한 장면을 다른 장면에 상대적으로 시간 지정 |
references/layers-and-order.md | 레이어 스택, z-순서 제어, 같은 시간대에 두 장면 오버랩 |
references/props-and-schemas.md | Props 선언, Zod 스키마 작성, defaultProps 및 calculateMetadata 연결 |
references/media.md | <Video> / <OffthreadVideo> / <Audio> / <Img> 배치, 트리밍, 볼륨 설정, staticFile 사용 |
references/determinism-rules.md | 렌더링을 탐색 가능하고 재현 가능하게 유지; 금지 사항; delayRender를 통한 비동기 에셋 로딩 |
references/editing-recipes.md | 컷 / 트림 / 재정렬 / 재타이밍 / 프리즈 / 카메라 / 마스크 / 크로스페이드 / 오디오 레시피 및 한계 |
references/legacy-html-contract.md | 여전히 이전 HTML + data-* 계약을 사용하는 프로젝트 인식 및 마이그레이션 |
references/tailwind.md | Tailwind v4 프로젝트에서 작업 |
references/storyboard-format.md | STORYBOARD.md 계획 작성 (+ 파싱된 매니페스트) |
references/review-loop.md | 라이브 보드에서 계획 → 스케치 → 빌드 검토 패스 실행 |
references/production-loop.md | 승인된 계획을 최종 비디오로 전환 |
references/brief-contract.md | 브리핑의 기본 규칙: 모드 유도, 공유 필드 레지스트리, 질문 불변 조건 |
references/brief-format.md | BRIEF.md 작성 |
references/script-format.md | 선택적 SCRIPT.md 고정 내레이션 작성 |
references/subagent-dispatch.md | 하위 에이전트 전달 동사를 당신의 하네스에 매핑 |
references/frame-worker-core.md | 공유 프레임-워커 역할 계약 |
애니메이션 관련 세부 사항(스프링 설정, 이징, 런타임 어댑터)은 hyperframes-animation으로 가십시오.
컴포지션 구축
프로젝트의 형태
package.json # remotion, react, react-dom; studio/render용 스크립트
remotion.config.ts # 렌더 타임 설정 (코덱, 이미지 포맷, Tailwind/webpack 오버라이드)
tsconfig.json
public/ # staticFile()이 참조하는 모든 파일
src/
index.ts # registerRoot(Root)
Root.tsx # 모든 <Composition>이 여기에 있음
<Name>/
index.tsx # 컴포지션의 최상위 컴포넌트
scenes/*.tsx # 각 장면별 하나의 컴포넌트
schema.ts # Zod 스키마 + 추론된 Props 타입
src/index.ts는 진입점이며 정확히 한 가지 작업을 수행합니다:
import { registerRoot } from "remotion";
import { Root } from "./Root";
registerRoot(Root);
컴포지션 등록
렌더링 가능한 모든 것은 Root.tsx 안의 <Composition>입니다. id는 CLI가 이름으로 렌더링하는 것입니다.
import { Composition } from "remotion";
import { LaunchVideo } from "./LaunchVideo";
import { launchSchema } from "./LaunchVideo/schema";
const FPS = 30;
export const Root: React.FC = () => (
<>
<Composition
id="launch"
component={LaunchVideo}
durationInFrames={45 * FPS}
fps={FPS}
width={1920}
height={1080}
schema={launchSchema}
defaultProps={{ productName: "Acme", accent: "#5B8CFF" }}
/>
</>
);
durationInFrames는 렌더링 길이입니다. 프레임 단위이며 초 단위가 아닙니다. 이것을 잘못 입력하는 것이 가장 흔한 저작 실수입니다. 숫자를 직접 쓰지 말고 명명된 FPS 상수에서 유도하십시오.
프레임은 유일한 클럭입니다
모든 애니메이션 값은 useCurrentFrame()의 순수 함수입니다. 이것이 렌더링을 탐색 가능하게 만드는 계약입니다: 렌더러는 812 프레임으로 콜드 스타트하며, 처음부터 재생할 때와 동일한 픽셀을 얻어야 합니다.
import { AbsoluteFill, useCurrentFrame, useVideoConfig, interpolate, spring } from "remotion";
export const Title: React.FC<{ text: string }> = ({ text }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const enter = spring({ frame, fps, config: { damping: 18 } });
const y = interpolate(enter, [0, 1], [40, 0]);
return (
<AbsoluteFill style={{ justifyContent: "center", alignItems: "center" }}>
<h1 style={{ opacity: enter, transform: `translateY(${y}px)` }}>{text}</h1>
</AbsoluteFill>
);
};
프레임 범위를 매핑하는 interpolate는 항상 클램핑하십시오. 그렇지 않으면 값이 범위 밖으로 계속 외삽되어 페이드인이 opacity 4가 될 수 있습니다:
const opacity = interpolate(frame, [0, 15], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
타이밍은 <Sequence>
<Sequence>는 자식의 시간을 이동시킵니다: 내부에서 useCurrentFrame()은 시퀀스 자체의 시작에서 0을 반환합니다. 이 로컬 리베이싱이 핵심입니다. 장면 컴포넌트는 마스터 타임라인에서 자신의 위치를 알 필요가 없기 때문입니다.
<AbsoluteFill>
<Sequence durationInFrames={90}>
<Hook />
</Sequence>
<Sequence from={90} durationInFrames={150}>
<Feature />
</Sequence>
<Sequence from={240} durationInFrames={120}>
<Close />
</Sequence>
</AbsoluteFill>
연속적인 장면의 실행에는 <Series>가 오프셋을 계산하므로 지속 시간 편집이 모든 이후의 from을 수동으로 수정하는 일이 발생하지 않습니다:
<Series>
<Series.Sequence durationInFrames={90}><Hook /></Series.Sequence>
<Series.Sequence durationInFrames={150}><Feature /></Series.Sequence>
<Series.Sequence durationInFrames={120}><Close /></Series.Sequence>
</Series>
<Series.Sequence offset={-15}>는 장면을 앞당겨 이전 장면과 겹치게 합니다. 이것이 산술 없이 크로스페이드 범위를 얻는 방법입니다. 자세한 내용과 프레젠테이션 기반 전환을 위한 <TransitionSeries>는 references/timing.md에 있습니다.
레이어가 트랙을 대체합니다
트랙 인덱스는 없습니다. 스태킹은 <AbsoluteFill> 내부의 DOM 순서입니다: 나중의 형제가 위에 그려집니다. 음성 해설 트랙, 배경 음악, 그리고 세 개의 시각적 장면은 다섯 개의 형제입니다.
<AbsoluteFill>
<Audio src={staticFile("music.mp3")} volume={0.18} />
<Audio src={staticFile("vo.mp3")} />
<Sequence durationInFrames={90}><Hook /></Sequence>
<Sequence from={90}><Feature /></Sequence>
<Overlay /> {/* 위의 모든 것 위에 그려짐 */}
</AbsoluteFill>
<Sequence layout="none">는 암시적 <AbsoluteFill> 래퍼를 제거합니다. 자식이 절대 위치 지정 대신 부모의 flex 또는 grid 레이아웃에 참여해야 할 때 사용하십시오.
미디어는 Remotion에 속합니다
절대 원시 <video>, <audio>, 또는 <img> 태그를 사용하지 마십시오. Remotion의 컴포넌트는 렌더러가 결정론적으로 프레임을 탐색하고 추출할 수 있게 해줍니다.
| 필요 사항 | 사용 |
|---|---|
| 렌더링된 비디오 | <OffthreadVideo src> (권장) |
| DOM 수준 제어가 필요한 비디오 | <Video src> |
| 오디오 | <Audio src volume trimBefore trimAfter> |
| 정지 이미지 | <Img src> |
| 포함된 페이지 | <IFrame src> |
public/ 아래의 모든 파일 | staticFile("dir/name.ext") |
<OffthreadVideo>는 브라우저 외부에서 FFmpeg로 프레임을 추출하므로 렌더링에 더 빠르고 정확합니다. <Video>는 실제 요소가 필요할 때만 사용하십시오(예를 들어 currentTime을 기반으로 무언가를 구동하는 경우). staticFile()은 로컬 에셋에 필수입니다. 단순히 "/music.mp3" 문자열은 Studio에서는 작동하지만 Lambda 렌더링에서는 깨집니다. 트리밍, 볼륨 램프, 재생 속도는 references/media.md에 있습니다.
Props 및 스키마
컴포지션 입력은 React Props이며, Zod 스키마로 타입이 지정되어 Studio가 편집 가능한 컨트롤을 렌더링하고 잘못된 --props 페이로드가 잘못 렌더링되는 대신 큰 소리로 실패하게 합니다.
import { z } from "zod";
import { zColor } from "@remotion/zod-types";
export const launchSchema = z.object({
productName: z.string(),
accent: zColor(),
features: z.array(z.object({ title: z.string(), body: z.string() })),
});
export type LaunchProps = z.infer<typeof launchSchema>;
지속 시간이 Props에 의존하는 경우(알 수 없는 길이의 기능 목록, 알 수 없는 길이의 오디오 파일), 추측을 하드코딩하는 대신 calculateMetadata에서 계산하십시오. references/props-and-schemas.md를 참조하십시오.
양보할 수 없는 규칙
타입 검사가 잡지 못하는 조용한 버그. 전체 근거는 references/determinism-rules.md에 있습니다.
- 벽시계 금지.
Date.now(),new Date(),performance.now()금지. 시간은useCurrentFrame()에서 가져옵니다. - 시드 없는 무작위성 금지.
Math.random()은 프레임마다 다른 값을 제공하여 깜빡임을 유발합니다. Remotion의random(seed)를 사용하십시오: 동일한 시드, 동일한 값, 모든 프레임과 모든 렌더링에서. - 상태 기반 애니메이션 금지.
useState/useReducer/useEffect는 시각적인 것을 구동해서는 안 됩니다. 렌더러는 프레임을 순서 없이 그리고 병렬 워커에서 마운트합니다. 앞으로 재생하여 축적된 상태는 존재하지 않습니다. 대신frame에서 유도하십시오. - CSS 애니메이션, 트랜지션,
requestAnimationFrame금지. 이들은 브라우저 자체 클럭에서 실행되며, 렌더러가 진행시키지 않습니다. 이들 각각은 얼어붙은 첫 번째 프레임으로 렌더링됩니다. - 비동기 작업은
delayRender로 대기하십시오. fetch, 폰트, 측정된 레이아웃: 작업 전에delayRender()를 호출하고 후에continueRender(handle)를 호출하십시오. 그렇지 않으면 작업이 완료되기 전에 프레임이 캡처됩니다. - 인터폴레이션을 클램핑하십시오. 클램핑되지 않은
interpolate는 입력 범위 밖으로 외삽됩니다. - 애니메이션 되는 모든 것은 절대 위치 지정하십시오. 일반 흐름 내에서 변환된 요소는 움직이면서 주변 형제를 끌어당깁니다.
기존 컴포지션 편집
- 먼저 파일을 읽으십시오. 관련 없는 타이밍, Props, id 및 에셋 경로를 보존하십시오.
- 기존
Compositionid와 일치시키십시오. CLI, 렌더 스크립트, CI 모두 이 id로 컴포지션을 주소 지정합니다. 이름을 바꾸는 것은 호환성을 깨는 변경입니다. - 장면 추가:
<Series>를 선호하여 오프셋이 자동 재계산되도록 하십시오. 수동 오프셋<Sequence>목록에서는 이후의 모든from을 수정하십시오. - 지속 시간 변경:
<Composition>의durationInFrames도 확인하십시오. 컴포지션 길이를 초과하여 실행되는 장면은 조용히 잘립니다. - 컴포넌트 추가: Props와
frame의 순수 함수로 유지하십시오.
검증
명령어 세부 사항은 hyperframes-cli에 있습니다.
-
npx tsc --noEmit실행하여 스키마와 Props 타입이 실제로 일치하는지 확인 -
npx remotion still <id> out/probe.png --frame <n>을 몇몇 장면 중간점에서 실행하고 각각을 살펴보십시오 -
npx remotion studio실행하여 검토할 수 있도록 하고, 사용자가 스크러빙하고 Props를 직접 조정할 수 있게 하십시오 -
npx remotion render <id>는 사용자가 승인한 후에만 실행하십시오