HyperFrames
HTML es la fuente de verdad para el video. Una composición es un archivo HTML con atributos data-* para el tiempo, una línea de tiempo GSAP para la animación y CSS para la apariencia. El framework maneja la visibilidad de los clips, la reproducción de medios y la sincronización de la línea de tiempo.
Integración con Open Design (crítica para esta superficie)
Cuando este skill se ejecuta dentro de Open Design (es decir, $OD_PROJECT_DIR está definido), el flujo de salida es fijo: solo el .mp4 renderizado debe terminar en la raíz del proyecto. Los archivos fuente de la composición (hyperframes.json, meta.json, index.html, assets) pertenecen a un directorio de caché oculto para no saturar el FileViewer del usuario ni los chips de "archivos producidos" del chat.
Flujo de renderizado dentro de OD — ruta rápida:
Para la mayoría de las solicitudes de OD ("video de prueba", "presentación de producto de 5s", "clip demo"), NO escribas la composición HTML desde cero. Utiliza el scaffold incorporado de HyperFrames y edita solo lo que el prompt realmente cambia. La ruta "autor desde cero" cuesta minutos de salida del modelo y tiempo de herramienta de chat silenciosa; la ruta del scaffold cuesta segundos.
# 1. Elige un slot de caché oculto. El prefijo de punto (dotfile) hace que el
# listado de archivos del proyecto OD lo omita, por lo que los archivos
# fuente nunca saturan el chat.
COMP_REL=".hyperframes-cache/$(date +%s)-$(openssl rand -hex 2)"
COMP="$OD_PROJECT_DIR/$COMP_REL"
# 2. Obtén un scaffold inmediatamente renderizable (hyperframes.json,
# meta.json, index.html con GSAP CDN + window.__timelines.main
# ya registrados). Esto se ejecuta en tu shell — copia de archivos pura,
# sin Chrome, sin red más allá de la caché de npx.
npx hyperframes init "$COMP" --example blank --skip-skills --non-interactive
# 3. Edita SOLO $COMP/index.html — cambia `data-duration` en la raíz
# si necesitas una duración no predeterminada, intercambia la paleta
# de marcador de posición en <style>, añade 1–3 <div>s de clip para
# texto/imágenes, y agrega los tweens de GSAP correspondientes dentro
# del bloque existente
# `window.__timelines["main"] = gsap.timeline({paused:true})`.
# Mantén las ediciones al mínimo; el scaffold ya es HF válido.
# 4. Despacha el renderizado a través del daemon de OD. NO ejecutes `npx hyperframes
# render` desde este shell — el daemon lo ejecuta por ti en un proceso sin
# sandbox. (Muchos CLI de agentes, Claude Code en particular, envuelven Bash
# en sandbox-exec de macOS bajo el cual el subproceso de Chrome de puppeteer
# se cuelga a mitad de la captura de fotogramas. El proceso del daemon no
# tiene sandbox, por lo que los renders se completan de manera confiable).
#
# El despachador regresa en ~1s con un {taskId}; lleva el renderizado a
# completitud haciendo un bucle de llamadas a `"$OD_NODE_BIN" "$OD_BIN" media wait <taskId>`.
# Cada llamada realiza una espera larga de hasta 25s (muy por debajo del límite
# predeterminado de 30s de tu herramienta de shell) y sale con 0/2/5 para
# indicar completado/en ejecución/fallido.
out=$("$OD_NODE_BIN" "$OD_BIN" media generate \
--project "$OD_PROJECT_ID" \
--surface video \
--model hyperframes-html \
--output "<nombre-descriptivo>.mp4" \
--composition-dir "$COMP_REL")
ec=$?
task_id=$(printf '%s\n' "$out" | tail -1 | jq -r '.taskId // empty')
since=$(printf '%s\n' "$out" | tail -1 | jq -r '.nextSince // 0')
while [ "$ec" -eq 2 ] && [ -n "$task_id" ]; do
out=$("$OD_NODE_BIN" "$OD_BIN" media wait "$task_id" --since "$since")
ec=$?
since=$(printf '%s\n' "$out" | tail -1 | jq -r '.nextSince // '"$since")
done
[ "$ec" -ne 0 ] && { echo "$out" >&2; exit "$ec"; }
Cada llamada a generate y cada wait duran como máximo ~25s, por lo que el límite predeterminado de ~30s de la herramienta de shell del agente nunca se activa. Las líneas de progreso de HF (Capturing frame N/M) se transmiten a stderr en vivo durante todo el bucle. Cuando el renderizado finaliza, la última línea de stdout es {"file": { "name": "<output>", "size": …, "kind": "video", … }} — cita file.name en tu respuesta para que el usuario sepa lo que se produjo.
Omite la Puerta de Identidad Visual dentro de OD. La sección HARD-GATE a continuación (bajo "Enfoque") te indica que leas DESIGN.md / visual-style.md o te detengas y hagas 3 preguntas de estado de ánimo antes de escribir cualquier composición. Esa puerta es para proyectos HF independientes. Los proyectos OD ya tienen su propia capa de sistema de diseño — el usuario eligió su dirección visual al crear el proyecto. Para un renderizado de prueba de OD, usa por defecto: lienzo oscuro (#0b0b0f), un acento cálido (#ffb76b), un acento frío (#7da4ff), movimiento moderado. Solo pide información estilística si el prompt del usuario es demasiado vago como para siquiera elegir un tema (muy raro).
Cuándo omitir el scaffold y escribir desde cero: solo cuando el usuario solicite explícitamente algo que la plantilla en blanco claramente no puede alojar (por ejemplo, líneas de tiempo de múltiples composiciones, superposiciones reactivas al audio, subtítulos sincronizados con una pista de TTS que ya han generado). Para todo lo demás, init + editar es la ruta predeterminada.
Los subcomandos de HF más ligeros que AÚN PUEDES ejecutar desde tu propio shell (no necesitan iniciar Chrome):
npx hyperframes lint "$COMP"— validar composición antes de despacharnpx hyperframes transcribe <audio>— generar subtítulosnpx hyperframes tts <text>— generar narración
Reserva el despacho del daemon para render/inspect/preview (cualquier cosa que requiera Chrome).
NO llames a "$OD_NODE_BIN" "$OD_BIN" media generate --model hyperframes-html — esa ruta de despachador devuelve un 400 (AGENT_RENDERED) a propósito. HyperFrames es renderizado por ti directamente a través de npx.
NO coloques hyperframes.json / meta.json / index.html en la raíz del proyecto; el listado de archivos de OD escanea recursivamente y el usuario vería tres archivos no relacionados aparecer en el chat.
Para opciones de CLI más allá de render (lint, preview, transcribe, tts, inspect, benchmark), llámalas directamente desde tu herramienta de shell cuando la tarea lo justifique (por ejemplo, generar audio TTS en la caché antes de referenciarlo desde la composición).
Enfoque
Antes de escribir HTML, piensa a alto nivel:
- Qué — ¿qué debería experimentar el espectador? Identifica el arco narrativo, los momentos clave y los latidos emocionales.
- Estructura — cuántas composiciones, cuáles son subcomposiciones vs en línea, qué pistas llevan qué (video, audio, superposiciones, subtítulos).
- Tiempo — qué clips determinan la duración, dónde aterrizan las transiciones, cuál es el ritmo.
- Diseño — construye primero el estado final. Ver "Diseño Antes de la Animación" a continuación.
- Animar — luego agrega movimiento usando las reglas a continuación.
Para ediciones pequeñas (corregir un color, ajustar el tiempo, agregar un elemento), ve directamente a las reglas.
Puerta de Identidad Visual
Verifica en este orden:
- ¿Existe DESIGN.md en el proyecto? → Léelo. Usa sus colores exactos, fuentes, reglas de movimiento y las restricciones de "Qué NO Hacer".
- ¿Existe visual-style.md? → Léelo. Aplica su
style_prompt_fully campos estructurados. (Nota:visual-style.mdes un archivo específico del proyecto.visual-styles.mdes la biblioteca de estilos con 8 presets nombrados — archivos diferentes). - ¿El usuario nombró un estilo (ej., "Swiss Pulse", "dark and techy", "luxury brand")? → Lee visual-styles.md para los 8 presets nombrados. Genera un DESIGN.md mínimo con:
## Style Prompt(un párrafo),## Colors(3-5 valores hex con roles),## Typography(1-2 familias de fuentes),## What NOT to Do(3-5 anti-patrones). - ¿Ninguno de los anteriores? → Haz 3 preguntas antes de escribir cualquier HTML:
- ¿Cuál es el estado de ánimo? (explosivo / cinematográfico / fluido / técnico / caótico / cálido)
- ¿Lienzo claro u oscuro?
- ¿Algún color de marca, fuente o referencia visual específica? Luego genera un DESIGN.md mínimo a partir de las respuestas.
Cada composición debe rastrear su paleta y tipografía hasta un DESIGN.md, visual-style.md o una dirección explícita del usuario. Si estás usando #333, #3b82f6 o Roboto — te saltaste este paso.
Para valores predeterminados de movimiento, tamaño, patrones de entrada y easing — sigue house-style.md. El estilo de la casa (house style) maneja CÓMO se mueven las cosas. El DESIGN.md maneja QUÉ aspecto tienen las cosas.
Diseño Antes de la Animación
Posiciona cada elemento donde debería estar en su momento más visible — el fotograma donde ha entrado completamente, está colocado correctamente y aún no está saliendo. Escribe esto como HTML+CSS estático primero. Aún no uses GSAP.
Por qué es importante: Si posicionas los elementos en su estado inicial animado (fuera de la pantalla, escalados a 0, opacidad 0) y los interpolas (tween) a donde crees que deberían aterrizar, estás adivinando el diseño final. Las superposiciones son invisibles hasta que el video se renderiza. Al construir primero el estado final, puedes ver y solucionar problemas de diseño antes de agregar cualquier movimiento.
El proceso
- Identifica el fotograma principal (hero frame) para cada escena — el momento en que la mayoría de los elementos son visibles simultáneamente. Este es el diseño que construyes.
- Escribe CSS estático para ese fotograma. El contenedor
.scene-contentDEBE llenar la escena completa usandowidth: 100%; height: 100%; padding: Npx;condisplay: flex; flex-direction: column; gap: Npx; box-sizing: border-box. Usa padding para empujar el contenido hacia adentro — NUNCA usesposition: absolute; top: Npxen un contenedor de contenido. Los contenedores de contenido con posicionamiento absoluto se desbordan cuando el contenido es más alto que el espacio restante. Reservaposition: absolutesolo para elementos decorativos. - Agrega entradas con
gsap.from()— anima DESDE fuera de la pantalla/invisible HASTA la posición CSS. La posición CSS es la verdad absoluta; el tween describe el viaje para llegar allí. - Agrega salidas con
gsap.to()— anima HASTA fuera de la pantalla/invisible DESDE la posición CSS.
Ejemplo
/* scene-content llena la escena, el padding posiciona el contenido */
.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: 120px;
}
.subtitle {
font-size: 42px;
}
/* El contenedor se adapta a cualquier tamaño de escena (1920x1080, 1080x1920, etc.).
El padding posiciona el contenido. Flex + gap maneja el espaciado. */
INCORRECTO — dimensiones codificadas y posicionamiento absoluto:
.scene-content {
position: absolute;
top: 200px;
left: 160px;
width: 1920px;
height: 1080px;
display: flex; /* ... */
}
// Paso 3: Animar HACIA esas posiciones
tl.from(".title", { y: 60, opacity: 0, duration: 0.6, ease: "power3.out" }, 0);
tl.from(".subtitle", { y: 40, opacity: 0, duration: 0.5, ease: "power3.out" }, 0.2);
tl.from(".logo", { scale: 0.8, opacity: 0, duration: 0.4, ease: "power2.out" }, 0.3);
// Paso 4: Animar FUERA desde esas posiciones
tl.to(".title", { y: -40, opacity: 0, duration: 0.4, ease: "power2.in" }, 3);
tl.to(".subtitle", { y: -30, opacity: 0, duration: 0.3, ease: "power2.in" }, 3.1);
tl.to(".logo", { scale: 0.9, opacity: 0, duration: 0.3, ease: "power2.in" }, 3.2);
Cuando los elementos comparten espacio a lo largo del tiempo
Si el elemento A sale antes de que el elemento B entre en la misma área, ambos deben tener posiciones CSS correctas para sus respectivos fotogramas principales. El orden de la línea de tiempo garantiza que nunca coexistan visualmente — pero si omites el paso de diseño, no detectarás el caso en que se superpongan accidentalmente debido a un error de tiempo.
Qué cuenta como superposición intencional
Los efectos en capas (brillo detrás del texto, elementos de sombra, patrones de fondo) y los diseños apilados en z (pilas de tarjetas, capas de profundidad) son intencionales. El paso de diseño se trata de detectar superposiciones no intencionales — dos títulos que caen uno encima del otro, una estadística que cubre una etiqueta, contenido que se sale del fotograma.
Atributos de Datos
Todos los Clips
| Atributo | Requerido | Valores |
|---|---|---|
id | Sí | Identificador único |
data-start | Sí | Segundos o referencia de ID de clip ("el-1", "intro + 2") |
data-duration | Requerido para img/div/composiciones | Segundos. Video/audio usa por defecto la duración del medio. |
data-track-index | Sí | Entero. Los clips en la misma pista no pueden superponerse. |
data-media-start | No | Desplazamiento de recorte en el origen (segundos) |
data-volume | No | 0-1 (por defecto 1) |
data-track-index no afecta la superposición visual — usa CSS z-index.
Clips de Composición
| Atributo | Requerido | Valores |
|---|---|---|
data-composition-id | Sí | ID de composición único |
data-start | Sí | Hora de inicio (composición raíz: usa "0") |
data-duration | Sí | Tiene prioridad sobre la duración de la línea de tiempo GSAP |
data-width / data-height | Sí | Dimensiones en píxeles (1920x1080 o 1080x1920) |
data-composition-src | No | Ruta al archivo HTML externo |
Estructura de la Composición
Las subcomposiciones cargadas a través de data-composition-src usan un wrapper <template>. Las composiciones independientes (el index.html principal) NO usan <template> — colocan el div data-composition-id directamente en <body>. Usar <template> en un archivo independiente oculta todo el contenido del navegador y rompe el renderizado.
Estructura de la subcomposición:
<template id="my-comp-template">
<div data-composition-id="my-comp" data-width="1920" data-height="1080">
<!-- contenido -->
<style>
[data-composition-id="my-comp"] {
/* estilos con ámbito */
}
</style>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<script>
window.__timelines = window.__timelines || {};
const tl = gsap.timeline({ paused: true });
// tweens...
window.__timelines["my-comp"] = tl;
</script>
</div>
</template>
Cargar en la raíz: `<div id="el-1" data-composition-id="my-comp" data-composition-src="compositions/my-comp.html" data-star...