vid-sizzle-reel
Genera un sizzle reel o hype video de alta energía a partir de activos de marca y mensajes clave. Pipeline: Composición HTML de HyperFrames (timelines GSAP) → Chromium headless → FFmpeg H.264 MP4. Sin Runway. Sin Pika. Sin APIs de video IA. Costo de ejecución cero más allá de Node.js + FFmpeg.
Diferenciación con vid-product-launch:
vid-product-launchcuenta una historia narrativa — una revelación ganada, cinco secciones, arco de tensiónvid-sizzle-reelgenera energía y emoción — cortes rápidos, música primero, sin un solo ritmo narrativo
Reglas Críticas (leer antes de cada generación)
-
Escribe los mensajes clave tú mismo — o pídele al usuario que los escriba. Estas son las frases exactas que aparecen en pantalla. "La IA lo resolverá" produce relleno. 3-5 líneas contundentes son todo el contenido de un sizzle reel.
-
El nombre de la marca aparece SOLO en la sección Land. Cold Open y Build crean tensión sin nombrar la marca. La sección Land es la recompensa.
-
Declara el patrón de ritmo en un comentario al inicio del script antes de escribir cualquier HTML. Formato:
// RHYTHM: flash-sequence | Cold[0-5s]: stat | Build[5-40s]: msg(1.5s)|flash|... | Peak[40-55s]: ... | Land[55-60s]: logo+CTA. Cada decisión de temporización se verifica contra esto. -
Construye primero el diseño del estado final en CSS — aún no GSAP. Posiciona cada elemento en su momento más visible en CSS estático. Luego agrega entradas GSAP con
gsap.from()y salidas congsap.to(). Nunca posiciones en el estado inicial animado. -
class="clip"en cada elemento temporizado. Requerido por HyperFrames. Sin esto, el elemento es invisible para el reproductor. -
data-start,data-duration,data-track-indexen cada clip. Sin excepciones. Los clips del mismo track no pueden superponerse — usa diferentes índices de track. -
No
Math.random(). HyperFrames requiere composiciones deterministas. Usa PRNG con semilla (mulberry32) para cualquier valor pseudoaleatorio. -
Todos los timelines GSAP comienzan
{ paused: true }y se registran enwindow.__timelines["comp-id"]. El reproductor de HyperFrames controla la reproducción — nunca llames atl.play(). -
Nunca
position: absoluteen contenedores.scene-content. Usawidth:100%; height:100%; padding:Npx; display:flex. Reservaposition:absolutesolo para elementos decorativos. -
Música: elemento
<audio>separado condata-track-index. El video debe tenermuted playsinline. Nunca pongas audio en un elemento<video>. -
Lee
references/cut-patterns.mdYreferences/tone-presets.mdantes de generar cualquier HTML. -
Nunca vuelques HTML en el chat. Guarda en archivo. Muestra solo el resumen.
Paso 1: Entrada
Requerido:
key_messages— 3-5 líneas contundentes para mostrar en pantalla (el usuario debe escribirlas)
Parámetros opcionales y valores por defecto:
| Parámetro | Por defecto | Descripción |
|---|---|---|
| brand_assets | ninguno | URL/ruta del logo, colores de marca (hex), capturas de pantalla clave |
| tone | energetic | energetic / cinematic / emotional / professional |
| music | ninguno | Ruta de archivo, o cadena BPM/género (ej. "128bpm electronic") |
| duration | 60 | 30 / 60 / 90 segundos |
| aspect_ratio | 9:16 | 9:16 (1080x1920) / 16:9 (1920x1080) |
| cut_style | auto | fast (1-2s) / cinematic (3-5s) — auto deriva del tono |
| end_card | auto | Logo + tagline + URL de CTA para la sección Land |
cut_style auto-defaults: energetic → fast; professional → fast (2s); cinematic → cinematic; emotional → mixed
Si faltan key_messages, pregunta exactamente:
"Para generar el sizzle reel, necesito tus mensajes clave — las frases exactas que aparecerán en pantalla. Dame 3-5 líneas contundentes. Esto es lo más importante que estás escribiendo para este video.
Ejemplos: 'Usado por 500+ equipos de crecimiento' / 'De días a minutos' / 'Funciona con Claude, Codex, Gemini'
También útil: tono (energético/cinematográfico/emocional/profesional), duración (30/60/90s), colores de marca o URL del logo."
Si key_messages están presentes → procede al Paso 2 inmediatamente.
Paso 2: Instalar HyperFrames
Instala las habilidades de HyperFrames (solo la primera vez — omite si ya están instaladas):
npx skills add heygen-com/hyperframes
Verifica el entorno:
node --version # debe ser >= 22
ffmpeg --version # debe estar presente
Inicializa el proyecto:
npx hyperframes init sizzle-[slug] --example kinetic-type --non-interactive
cd sizzle-[slug]
Slug: kebab-case del nombre de marca end_card o del primer key_message, máximo 25 caracteres.
Paso 3: Arquitectura Interna (nunca se muestra al usuario)
1. Deriva la temporización de cortes de duration + cut_style:
| Sección | 30s | 60s | 90s |
|---|---|---|---|
| Cold Open | 0-5s | 0-5s | 0-8s |
| Build | 5-25s | 5-40s | 8-65s |
| Peak | 25-28s | 40-55s | 65-80s |
| Land | 28-30s | 55-60s | 80-90s |
Convierte cada límite a milisegundos. Calcula la duración por escena desde cut_style:
fast: 1.2s-1.8s por mensaje (usa 1.5s por defecto)cinematic: 3.5s-5.0s por mensaje (usa 4.0s por defecto)mixed: 2.0s-4.0s variando según la energía de la sección
2. Mapea key_messages a escenas:
- Cold Open (1 escena): una estadística impactante o pregunta — SIN nombre de marca, genera intriga
- Build: una escena por key_message
- Peak: repite el mensaje más fuerte a escala doble, o combina 2 mensajes en un flash rápido
- Land: nombre de marca + tagline + URL de CTA
3. Detecta sincronización de ritmo si se proporciona música:
npx hyperframes tts --analyze-beats [archivo-musica]
Si no está disponible, calcula desde BPM: beat_ms = 60000 / BPM. Alinea los cortes a cada 2do beat.
4. Selecciona preset de tono — lee references/tone-presets.md para tokens CSS.
5. Declara patrón de ritmo — escribe comentario al inicio del script:
// RHYTHM: flash-sequence
// Cold Open [0-5000ms]: stat
// Build [5000-40000ms]: msg-1(1500ms)|flash|msg-2(1500ms)|flash|msg-3(1500ms)|flash|msg-4(1500ms)|flash|build-accum(10000ms)
// Peak [40000-55000ms]: msg-strongest(1200ms)|flash|msg-2nd(1200ms)|flash|HOLD(5600ms)
// Land [55000-60000ms]: logo+tagline+cta
6. Determina dimensiones de píxeles:
9:16→ W=1080, H=1920 (por defecto — Instagram Reels, TikTok, YouTube Shorts)16:9→ W=1920, H=1080
Paso 4: Composición HTML de HyperFrames
Lee references/cut-patterns.md y la habilidad de HyperFrames (/hyperframes) antes de escribir cualquier código.
Estructura HTML requerida:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
[enlace CDN de fuente desde preset de tono]
<style>
:root {
[todos los tokens CSS del preset de tono]
}
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
</style>
</head>
<body>
<div
data-composition-id="sizzle-[slug]"
data-width="[W]"
data-height="[H]"
style="position:relative;width:[W]px;height:[H]px;overflow:hidden;background:var(--bg)"
>
[si cut-flash: true]
<div id="cut-flash"
style="position:absolute;inset:0;background:var(--cut-flash-bg);opacity:0;pointer-events:none;z-index:200"></div>
[fin si]
[si film-grain: true]
<canvas id="grain-overlay"
width="240" height="135"
style="position:absolute;inset:0;width:[W]px;height:[H]px;pointer-events:none;opacity:0.022;mix-blend-mode:overlay;z-index:50"></canvas>
[fin si]
[si vignette: true]
<div id="vignette-overlay"
style="position:absolute;inset:0;background:radial-gradient(ellipse at center,transparent 35%,rgba(0,0,0,0.65) 100%);pointer-events:none;z-index:51"></div>
[fin si]
<!-- Escenas — un div por escena -->
<div id="scene-cold"
class="clip"
data-start="0"
data-duration="5"
data-track-index="0"
style="position:absolute;inset:0;opacity:0"
>
<div class="scene-content" style="width:100%;height:100%;padding:120px;display:flex;flex-direction:column;align-items:center;justify-content:center;box-sizing:border-box">
[contenido de cold open — una estadística o frase impactante, sin nombre de marca]
</div>
</div>
[un div por escena de Build, data-start incrementa por duración de corte]
[Escena(s) de Peak]
[Escena de Land — nombre de marca + tagline + CTA]
[si se proporciona música]
<audio id="music"
class="clip"
data-start="0"
data-duration="[duracion_total]"
data-track-index="10"
data-volume="0.85"
src="[ruta_musica]"
></audio>
[fin si]
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<script>
// ── DECLARACIÓN DE RITMO ──────────────────────────────────────────────────────
// RHYTHM: [flash-sequence | cinematic-hold | beat-sync | mixed]
// Cold Open [0-Nms]: ...
// Build [N-Nms]: ...
// Peak [N-Nms]: ...
// Land [N-Nms]: ...
// ── PRNG CON SEMILLA (solo para grano — ningún otro valor aleatorio) ───────────
function mulberry32(seed) {
return function() {
seed |= 0; seed = seed + 0x6D2B79F5 | 0;
let t = Math.imul(seed ^ seed >>> 15, 1 | seed);
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
return ((t ^ t >>> 14) >>> 0) / 4294967296;
};
}
[si film-grain: true]
// ── GRANO DE PELÍCULA (solo cinematográfico/emocional) ──────────────────────────
const grainCanvas = document.getElementById('grain-overlay');
const grainCtx = grainCanvas ? grainCanvas.getContext('2d') : null;
// Grano actualizado por el ticker de GSAP (no captura por cuadro — HyperFrames maneja esto)
[fin si]
// ── TIMELINE GSAP ───────────────────────────────────────────────────────────
const tl = gsap.timeline({ paused: true });
// ── COLD OPEN ───────────────────────────────────────────────────────────────
[tweens de entrada y salida de cold open — ver cut-patterns.md para el patrón exacto]
// ── SECCIÓN BUILD ───────────────────────────────────────────────────────────
[tweens por mensaje — flash-sequence o cinematic-hold de cut-patterns.md]
// ── SECCIÓN PEAK ─────────────────────────────────────────────────────────────
[tweens de peak — mismo patrón que build pero con temporización más ajustada]
// ── SECCIÓN LAND ─────────────────────────────────────────────────────────────
[entradas de land — logo, tagline, CTA. Sin salida — se mantiene hasta el final]
// ── REGISTRO DEL TIMELINE ────────────────────────────────────────────────────
window.__timelines = window.__timelines || {};
window.__timelines["sizzle-[slug]"] = tl;
</script>
</div>
</body>
</html>
Reglas de calidad de diseño:
- Estadística de cold open: tamaño de fuente ≥160px para 16:9, ≥100px para 9:16
- Mensajes clave: tamaño de fuente ≥80px — legibles de un vistazo
- Nombre de marca en sección Land: ≥120px para 16:9
- Todos los colores de variables de token de preset — sin hex libre excepto
var(--accent)hex literal dentro de canvasfillStyle scene-contentsiempre usapadding + flex— nuncaposition:absoluteen el contenedor de contenidoclass="clip"en cada elemento condata-start
Paso 5: Autocontrol de Calidad (corrige cada fallo antes del Paso 6)
Contenido:
-
key_messages— cada frase aparece exactamente una vez, sin duplicados - El nombre de la marca aparece SOLO en la sección Land (no en cold open, build ni peak)
- Cold open NO contiene nombre de marca NI nombre de producto — solo estadística o frase impactante
- La sección Land tiene: nombre de marca + tagline + URL de CTA (tres elementos como mínimo)
- La URL de CTA de end_card está presente en el fotograma final
Contrato HyperFrames:
-
class="clip"en cada elemento condata-start -
data-start,data-duration,data-track-indexen cada clip -
data-composition-iden el div raíz de composición -
window.__timelines["sizzle-[slug]"] = tlregistrado - Todos los timelines comienzan
{ paused: true } - Sin
Math.random()— solo PRNG con semilla - Sin llamadas a
tl.play()otl.resume()
GSAP / Diseño:
-
gsap.from()para todas las entradas (DESDE fuera de pantalla HASTA posición CSS) -
gsap.to()para todas las salidas (DESDE posición CSS HASTA fuera de pantalla) - Sin
position:absoluteen contenedores.scene-content - Patrón de ritmo declarado en comentario al inicio del script
Temporización de cortes:
- Tono
fast: duración promedio de corte ≤ 2.0s - Tono
cinematic: duración promedio de corte ≥ 3.0s - El flash de corte se dispara en cada límite de corte para el tono
energetic(60ms, opacidad 0→1→0) - Los clips del mismo track NO se superponen (verifica todas las agrupaciones de
data-track-index)
Efectos de sonido:
-
window.__sfxTimelinepresente y colocado antes del registro del timeline - Evento
word-hitpor mensaje clave -
whooshen la transición Build → Peak -
cta-chimeal inicio de Land - Sin efectos de sonido antes de 500ms
Paso 6: Lint, Render, Salida
npx hyperframes lint
npx hyperframes inspect --json
Corrige todos los errores de lint. Corrige todos los errores de inspect (desbordamiento de texto, clips escapando de contenedores). Luego renderiza:
npx hyperframes render --output sizzle/[slug]/sizzle-reel.mp4
Salida:
## Sizzle Reel: [nombre de marca de end_card]
Tono: [tone] | Duración: [N]s | Estilo de corte: [cut_style] | Aspecto: [ratio]
Ritmo: [patrón declarado]
Secciones
Cold Open [0-Ns]: [estadística o frase impactante]
Build [N-Ns]: [N mensajes clave listados]
Peak [N-Ns]: [mensaje más fuerte a escala máxima]
Land [N-Ns]: [nombre de marca] + "[tagline]" + [URL de CTA]
Archivos
Fuente: sizzle/[slug]/index.html
Salida: sizzle/[slug]/sizzle-reel.mp4
Timeline de Efectos de Sonido (incrustar en cada composición)
El mismo sistema de efectos de sonido sintetizados por FFmpeg que vid-product-launch. Coloca window.__sfxTimeline inmediatamente antes del registro del timeline GSAP.
Referencia de tipos de efectos de sonido para sizzle reel:
| Tipo | Sonido | Duración | Colocación |
|---|---|---|---|
word-hit | Golpe sub (50Hz) + clic transitorio (2.2kHz) + ráfaga de ruido | 180ms | Uno por inicio de escena de key_message |
whoosh | Barrido de ruido de dos bandas (1.1kHz body + 4-8kHz air) | 700ms | En el límite Build → Peak |
cta-chime | Acorde mayor A (440+554+659+880Hz) + brillo de campana aecho | 1.2s | Exactamente al inicio de Land |
window.__sfxTimeline = [
// un word-hit por key_message (calculado desde los tiempos de inicio de escena)
{ ms: MSG_1_START_MS, sfx: 'word-hit', vol: 0.50 },
{ ms: MSG_2_START_MS, sfx: 'word-hit', vol: 0.50 },
{ ms: MSG_3_START_MS, sfx: 'word-hit', vol: 0.50 },
// ... repetir para cada mensaje
{ ms: PEAK_START_MS - 700, sfx: 'whoosh', vol: 0.55 },
{ ms: LAND_START_MS, sfx: 'cta-chime', vol: 0.65 },
];
Calcula todos los valores ms desde las secciones declaradas.
…
[truncado para traducción]