HyperFrames Core
O framework de saída é o Remotion. Uma composição é uma árvore de componentes React registrada com <Composition>; o frame atual é o único relógio; a reprodução de mídia pertence aos componentes <Video> / <Audio> do Remotion, não ao DOM.
Esta skill é o contrato técnico: como construir um projeto. O corpo abaixo é o guia de construção; os detalhes por tópico estão em references/, leia sob demanda. Outras preocupações estão nas skills de domínio irmãs: hyperframes-animation, hyperframes-creative, media-use, hyperframes-cli, hyperframes-registry. O mapa de capacidades em /hyperframes diz o que cada uma cobre.
Nota de portabilidade. Este contrato substituiu um contrato HTML mais
data-*mais timeline GSAP pausada. Se você encontrar um projeto cujoindex.htmlcarregadata-composition-id/data-start/data-duration, ou um arquivo de referência descrevendowindow.__timelines, você está olhando para o contrato antigo. Leiareferences/legacy-html-contract.mdantes de tocá-lo.
Referências
| Arquivo | Leia para… |
|---|---|
references/minimal-composition.md | começar pelo esqueleto de projeto renderizável mais pequeno |
references/project-structure.md | organizar src/, dividir cenas em componentes, decidir entre monolítico vs modular |
references/timing.md | converter segundos em frames, colocar um <Sequence>, usar <Series> / <TransitionSeries>, temporizar uma cena em relação a outra |
references/layers-and-order.md | empilhar camadas, controlar ordem z, sobrepor duas cenas no mesmo intervalo |
references/props-and-schemas.md | declarar props, escrever o esquema Zod, conectar defaultProps e calculateMetadata |
references/media.md | colocar <Video> / <OffthreadVideo> / <Audio> / <Img>, cortar, definir volume, usar staticFile |
references/determinism-rules.md | manter o renderizável buscável e reproduzível; as proibições; carregamento assíncrono de assets via delayRender |
references/editing-recipes.md | receitas de cortar / aparar / reordenar / re-temporizar / congelar / câmera / máscara / crossfade / áudio e seus limites |
references/legacy-html-contract.md | reconhecer e migrar um projeto ainda no contrato antigo HTML + data-* |
references/tailwind.md | trabalhar em um projeto Tailwind v4 |
references/storyboard-format.md | criar um plano STORYBOARD.md (+ o manifesto analisado) |
references/review-loop.md | executar as passagens de revisão plano → esboço → construção num quadro vivo |
references/production-loop.md | levar um plano aprovado a um vídeo entregue |
references/brief-contract.md | as regras-base do briefing: derivação de modo, registro de campo compartilhado, invariantes de pergunta |
references/brief-format.md | criar BRIEF.md |
references/script-format.md | criar o opcional SCRIPT.md com narração fixa |
references/subagent-dispatch.md | mapear verbos de despacho de subagente para o seu harness |
references/frame-worker-core.md | o contrato de papel do frame-worker compartilhado |
Para especificidades de animação (configurações de spring, easing, os adaptadores de runtime) vá para hyperframes-animation.
Construindo uma composição
A forma de um projeto
package.json # remotion, react, react-dom; scripts para studio/render
remotion.config.ts # configuração de render (codec, formato de imagem, Tailwind/webpack overrides)
tsconfig.json
public/ # tudo contra o que staticFile() resolve
src/
index.ts # registerRoot(Root)
Root.tsx # cada <Composition> vive aqui
<Nome>/
index.tsx # o componente de topo da composição
scenes/*.tsx # um componente por cena
schema.ts # esquema Zod + tipo de props inferido
src/index.ts é o ponto de entrada e faz exatamente uma coisa:
import { registerRoot } from "remotion";
import { Root } from "./Root";
registerRoot(Root);
Registre a composição
Toda coisa renderizável é um <Composition> em Root.tsx. O id é o que a CLI renderiza pelo nome.
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 é o comprimento do render. São frames, não segundos, e errar isso é o erro de autoria mais comum. Derive-o de uma constante FPS nomeada em vez de escrever 1350.
Frame é o único relógio
Todo valor animado é uma função pura de useCurrentFrame(). Este é o contrato que torna um render buscável: o renderizador salta para o frame 812 frio e deve obter os mesmos pixels que obteria reproduzindo sequencialmente.
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>
);
};
Sempre fixe um interpolate que mapeia um intervalo de frames, ou o valor continua extrapolando para além do intervalo e um fade-in torna-se opacidade 4:
const opacity = interpolate(frame, [0, 15], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
Temporização é <Sequence>
<Sequence> desloca temporalmente seus filhos: dentro dele, useCurrentFrame() retorna 0 no início da própria sequência. Esse re-ajuste local é o ponto principal, porque um componente de cena nunca precisa saber onde está na linha do tempo mestre.
<AbsoluteFill>
<Sequence durationInFrames={90}>
<Hook />
</Sequence>
<Sequence from={90} durationInFrames={150}>
<Feature />
</Sequence>
<Sequence from={240} durationInFrames={120}>
<Close />
</Sequence>
</AbsoluteFill>
Para uma sequência de cenas consecutivas, <Series> calcula os deslocamentos para que uma edição de duração não se propague para corrigir manualmente cada from posterior:
<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}> puxa uma cena para mais cedo para sobrepor a anterior, que é como você obtém um intervalo de crossfade sem aritmética. Detalhes completos, mais <TransitionSeries> para transições baseadas em apresentação, em references/timing.md.
Camadas substituem faixas
Não há índice de faixa. O empilhamento é a ordem do DOM dentro de um <AbsoluteFill>: irmãos posteriores pintam por cima. Uma faixa de voz, uma faixa musical e três cenas visuais são cinco irmãos.
<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 /> {/* pinta por cima de tudo acima dele */}
</AbsoluteFill>
<Sequence layout="none"> remove o invólucro implícito <AbsoluteFill>. Use-o quando o filho precisar participar do layout flex ou grid do pai em vez de ser posicionado absolutamente.
Mídia pertence ao Remotion
Nunca use uma tag <video>, <audio> ou <img> nativa. Os componentes do Remotion são o que permitem ao renderizador buscar e extrair frames deterministicamente.
| Necessidade | Use |
|---|---|
| Vídeo no render | <OffthreadVideo src> (prefira-o) |
| Vídeo precisando de controle a nível de DOM | <Video src> |
| Áudio | <Audio src volume trimBefore trimAfter> |
| Imagem estática | <Img src> |
| Página embutida | <IFrame src> |
Qualquer arquivo em public/ | staticFile("dir/name.ext") |
<OffthreadVideo> extrai frames com FFmpeg fora do navegador, que é mais rápido e mais preciso para renderização; use <Video> apenas quando precisar do elemento real (por exemplo, para conduzir algo com base no currentTime). staticFile() é obrigatório para ativos locais. Uma string simples "/music.mp3" funciona no Studio e depois quebra num render Lambda. Corte, rampas de volume e taxa de reprodução estão em references/media.md.
Props e schema
Entradas de composição são props React, tipadas por um esquema Zod para que o Studio renderize controles editáveis para elas e uma carga --props ruim falhe ruidosamente em vez de renderizar errado.
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>;
Quando a duração depende das props (uma lista de características de comprimento desconhecido, um arquivo de áudio de comprimento desconhecido), calcule-a em calculateMetadata em vez de definir um palpite fixo. Veja references/props-and-schemas.md.
Regras não negociáveis
Bugs silenciosos que uma verificação de tipo não apanhará. Raciocínio completo em references/determinism-rules.md.
- Sem relógio de parede. Sem
Date.now(), semnew Date(), semperformance.now(). O tempo vem deuseCurrentFrame(). - Sem aleatoriedade não seedada.
Math.random()dá um valor diferente por frame, o que pisca. Userandom(seed)do Remotion: mesma seed, mesmo valor, a cada frame e a cada render. - Sem animação baseada em estado.
useState/useReducer/useEffectnão devem conduzir nada visual. O renderizador monta frames fora de ordem e em workers paralelos; o estado acumulado reproduzindo sequencialmente não existe. Derive deframeem vez disso. - Sem animações CSS, transições ou
requestAnimationFrame. Elas rodam no próprio relógio do navegador, que o renderizador não avança. Cada uma delas renderiza como um primeiro frame congelado. - Aguarde trabalho assíncrono com
delayRender. Um fetch, uma fonte, um layout medido: chamedelayRender()antes econtinueRender(handle)depois, ou o frame captura antes do trabalho chegar. - Fixe suas interpolações.
interpolatesem fixação extrapola para fora do intervalo de entrada. - Posicionamento absoluto para qualquer coisa animada. Um elemento transformado dentro do fluxo normal arrasta seus irmãos enquanto se move.
Editando composições existentes
- Leia os arquivos primeiro. Preserve temporização, props, ids e caminhos de assets não relacionados.
- Corresponda ao
iddo<Composition>existente. A CLI, qualquer script de render e CI endereçam composições por ele. Renomear um é uma mudança de quebra. - Adicionar uma cena: prefira
<Series>para que os deslocamentos sejam recalculados. Numa lista de<Sequence>com deslocamentos manuais, corrija todofromposterior. - Mudar uma duração: verifique também
durationInFramesno<Composition>. Uma cena que ultrapassa o comprimento da composição é truncada silenciosamente. - Adicionar um componente: mantenha-o como uma função pura de props e
frame.
Validação
O detalhe do comando está em hyperframes-cli.
-
npx tsc --noEmit, para que o schema e os tipos de prop realmente estejam alinhados -
npx remotion still <id> out/probe.png --frame <n>em alguns pontos médios de cena, e veja cada um -
npx remotion studiopara revisão, para que o usuário possa navegar e ajustar props ele mesmo -
npx remotion render <id>apenas após a aprovação do usuário