Opus Visual Motion Engine
Una pipeline disciplinada para entregables visuales: planificar → construir desde la plantilla → renderizar → QA → corregir → exportar. La calidad proviene de los gates aplicados antes y después del renderizado, no del gusto. Sigue los pasos en orden. No te saltes ningún paso porque la tarea "parezca simple".
Las rutas a continuación son relativas al directorio de este skill (la base skill_resources que se te proporcionó). Llámalo $SKILL.
0. Leer primero (incondicional)
Antes de escribir cualquier código, lee las referencias que coinciden con el entregable. Son autoritativas en su tema:
| Entregable | Leer |
|---|---|
| video, motion graphic, explicativo, video de producto | references/video-generation.md, references/motion-design.md, references/javascript-animation.md |
| tipografía cinética / pieza liderada por texto | el conjunto de video anterior + references/typography.md |
| página de aterrizaje, dashboard, maqueta, UI | references/visual-design.md, references/typography.md |
| diagrama, esquema, visualización de datos | references/visual-design.md §4 |
| decisiones de encuadre / cámara | references/cinematography.md |
| duraciones y easing | references/animation-primitives.md |
| música o voz para sincronizar | SKILL.md §10 + references/video-generation.md §6 |
Los ejemplos prácticos están en examples/*.md. Lee el más cercano al briefing.
1. Planificar antes del código (escribirlo)
Escribe el plan en tu respuesta (o en un PLAN.md junto al archivo de salida) antes de crear el HTML. Un plan que existe solo en tu cabeza no cuenta.
UI / página
- Mensaje principal: una oración. ¿Qué debe entender el espectador en 3 segundos?
- Dirección nombrada: un nombre, 4–6 tokens hexadecales con significados asignados, tipografías con roles, tratamiento de formas. Derívalo de la industria del tema, los materiales y el lenguaje vernáculo. Una juguetería y un dashboard fintech nunca comparten una dirección.
- Jerarquía: etiqueta cada bloque como PRIMARY / SECONDARY / TERTIARY. Solo PRIMARY recibe el único gesto audaz.
- Progresión de secciones: una línea por sección, indicando su función y cómo realiza la transición a la siguiente.
- Matemáticas previas al renderizado: ancho del texto ≈
caracteres × 0.55 × font-size(sans) o× 0.5(serif); ancho de la caja =max(título_caracteres × 8, subtítulo_caracteres × 7) + 24a 14px; ajuste de fila = Σanchos + espacios ≤ contenedor. Traza cada conector contra cada caja. Si se cruza con uno, enruta un camino en forma de L.
Video / motion graphic
- Mensaje central: una oración. Si necesita dos, hay dos videos.
- Tiempos clave: 3–7. Cada tiempo clave responde "¿qué sabe ahora el espectador?"
- Tarjeta de escena por tiempo clave: inicio/fin (s), composición, cámara, tamaños de texto, primitivas (2–4), salida. Usa la plantilla en
references/motion-design.md§2. - Momento insignia: nombra el ÚNICO fotograma donde se muestra el mecanismo, no se etiqueta (por ejemplo, ruido filtrado en señal). Si la historia solo funciona con sus leyendas, rediseña el tiempo clave.
- Línea de tiempo global: marcas de tiempo absolutas. La SALIDA de una escena se completa antes de que la siguiente escena ocupe el espacio. Establece
DURATIONen el final real (10–20s por defecto; 30–45s solo con narración).
Una escena sin tarjeta no pertenece al video.
2. Construir desde la plantilla (video)
Copia assets/composition-template.html junto al entregable y completa los bloques EDIT. No reescribas los bloques ENGINE. Ya proporcionan:
- un
#stagede 1920×1080 (cambiaSTAGE_W/Hpara 1080×1920 o 1080×1080), ajustado a cualquier ventana; window.DURATION,window.seek(t),window.render(t),window.SCENES: el contrato de captura/exportación (QA captura cada escena deSCENES);at(),ease.*,lerp,mulberry32(PRNG con semilla), ylayer()para la visibilidad de escenas más EXIT;- ayudantes de audio
audioAt(t, "rms"|"low"),since(t, list),hit(t, list, dur)que leenwindow.AUDIO(§10) y devuelven 0 sin él; - un reproductor de vista previa (espacio, ←/→, Inicio/Fin) que se desactiva automáticamente durante la captura y con movimiento reducido;
- tokens de color en las 3 capas requeridas (claro, con protección
prefers-color-scheme,[data-theme]).
Para páginas, escribe un HTML normal de un solo archivo siguiendo §6.
3. Tiempo determinista (video)
render(t)es el único controlador de estado. Cada posición, opacidad, tamaño, número y dashoffset se calcula a partir deten segundos.seek(x)renderiza el mismo fotograma sin importar lo que se haya renderizado antes.testá en segundos. Limitata[0, DURATION], nunca a[0, 1]: limitar segundos a 0..1 congela silenciosamente toda la línea de tiempo.- Prohibido:
setTimeout,setInterval, animaciones/transiciones CSS como línea de tiempo,Math.random()sin semilla,Date.now()en estado.requestAnimationFramesolo puede avanzar el reloj de reproducción y llamar arender(clock). La plantilla ya hace esto. - Aleatoriedad: genera en tiempo de construcción a partir de
mulberry32(semilla), nunca dentro derender. - El dibujo SVG necesita los tres:
pathLength="1",stroke-dasharray="1", ystroke-dashoffsetde 1 a 0. Sin dasharray, la línea aparece completamente dibujada desde el fotograma 0. - Los spans de
inline-blockse tragan los espacios finales. Pon el espaciado enmargin-right.
4. Gramática del movimiento
Cinco movimientos, cada uno con un trabajo. Si un movimiento no es ninguno de estos, elimínalo.
- ENTER: una secuencia orquestada por página o escena, en orden de lectura, hermanos escalonados 60–120ms. Desvanecer hacia arriba en cada sección es el sello de IA.
- EMPHASIS: un elemento a la vez: un aumento de escala (≤1.2), un brillo o un cambio de color que tenga significado.
- TRANSITION: un cambio de estado. La parte que cambia se mueve; todo lo demás permanece quieto. 0.2–0.6s.
- CAMERA: una escala/traslación a nivel de escenario, como máximo una vez por escena, 0.8–1.2s, nunca durante una lectura de texto.
- EXIT: 0.3–0.5s, completamente completado. Ningún elemento sobrevive en estado intermedio a través de un límite de escena, excepto un ancla de continuidad deliberada que persiste idéntica o morph (el mismo objeto cambiando de estado, que es la transición más fuerte disponible).
Las transiciones tienen significado: corte duro = cambio de categoría, morph = lo mismo cambiando, paneo = recorrido. Nunca un crossfade predeterminado en todas partes. Las duraciones y easings están en references/animation-primitives.md. Los vecinos que se mueven juntos obtienen duraciones diferentes o un desplazamiento.
5. Decisión del stack
| Situación | Stack |
|---|---|
| micromovimiento de página | transiciones CSS en transform/opacity, 0.2–0.6s; bucles ≤2s dentro de @media (prefers-reduced-motion: no-preference) |
| video / motion graphic / tipo cinético | plantilla + vanilla render(t) (por defecto) |
| >6 pistas escalonadas u orquestación de morph SVG | GSAP UMD fijado, UNA línea de tiempo maestra en pausa, seek(t) → tl.seek(t) |
| gráficos | SVG en línea (por defecto) o Chart.js UMD con animation:false, colores hex (canvas no puede leer variables CSS) |
| arte generativo | p5.js con randomSeed/noiseSeed |
| 3D, solo cuando 3D es el mensaje | Three.js UMD fijado; cámara scripteada en render(t) |
Usa el stack más simple que alcance el resultado. Los scripts externos deben ser versiones fijadas exactas de cdnjs.cloudflare.com o cdn.jsdelivr.net. Inlinea todo lo demás. Cada fuente obtiene una pila de respaldo real.
6. Base técnica (no negociable)
- Único
.htmlcon CSS y JS en línea, menos de 16 MB, imágenes/datos como URIdata:. - Colores como tokens
:root; capa oscura bajo@media (prefers-color-scheme: dark)protegida por:root:not([data-theme="light"])y repetida bajo:root[data-theme="dark"]; fondobodyexplícito. meta viewportconviewport-fit=cover; rellenoenv(safe-area-inset-*);height:100%en html/body, nunca100vh; bloques anchos se desplazan dentro de su propiooverflow-x:auto; las cuadrículas usanminmax(0,1fr).- Los números calculados mostrados pasan por
Math.round,toFixedoIntl.NumberFormat. Los contadores deben aterrizar exactamente en el valor final. - Accesibilidad: contraste ≥4.5:1 (≥3:1 para texto ≥24px), foco visible,
prefers-reduced-motionrespetado (la plantilla muestra el fotograma final establecido). - Tipo legible: en un fotograma de video 1080p, ningún texto por debajo de 20px (H/54). En páginas móviles, ningún texto del cuerpo por debajo de 14px.
- El almacenamiento del navegador nunca mantiene el estado de la línea de tiempo. Cuando se use almacenamiento, envuélvelo en try/catch.
7. Evitar diseño genérico de IA
Cuando el briefing deje libertad, nunca la gastes en estos valores predeterminados (qa.mjs señala varios de ellos automáticamente):
| Sello | Corrección |
|---|---|
crema #F4F1EA-ish + serif de exhibición + acento terracota #D97757-ish | una paleta tomada de los propios materiales del tema |
| casi negro + un acento ácido, usado independientemente del tema | permitido solo cuando la dirección lo justifica por escrito |
| tarjetas redondeadas idénticas, un radio en todas partes, la misma sombra suave | tokens de radio por jerarquía; tarjeta solo para objetos delimitados |
| degradados como decoración, degradados púrpura, glassmorphism | rellenos planos; un degradado como máximo, solo para una propiedad física continua |
| cejas en MAYÚSCULAS con tracking, meta strings con punto medio, "→" en enlaces | minúscula; elimina etiquetas que repiten el contenido |
| Inter / fuente del sistema por defecto | tipografías elegidas para este tema, con una pila de respaldo |
| una sola palabra acentuada en un titular | permitido SOLO cuando el color de acento es un token semántico utilizado para el mismo significado en otra parte de la pieza (por ejemplo, coral = ruido) |
| héroe genérico: titular + subtítulo + CTA + captura de pantalla | abre con la cosa más característica en el mundo del tema |
| todo centrado, el mismo ritmo de cuadrícula en cada sección | un sistema de alineación por sección; varía el ritmo |
| todo animándose a la vez, 300ms en todas partes | el mapa de duraciones + escalonamiento (§4) |
Gasta la audacia en un lugar, luego elimina un accesorio.
8. Renderizado + QA (obligatorio antes de entregar)
Ejecuta el QA automatizado. Renderiza el archivo sin interfaz gráfica y escribe evidencia más un veredicto. Necesita Node ≥18. La primera ejecución en una copia nueva del skill necesita npm install en $SKILL/scripts (saltar si scripts/node_modules existe).
node "$SKILL/scripts/qa.mjs" ruta/al/entregable.html
En DSH en Windows, ejecútalo con la herramienta pwsh usando el mismo comando. Opciones: --step 0.25 (una cuadrícula más fina para escenas rápidas), --out <dir>, --mode page|video.
Modo video (automático cuando existen DURATION + seek) captura un fotograma cada paso y verifica: APIs prohibidas, determinismo en revisitas fuera de orden, una línea de tiempo congelada, aire muerto, espacios casi vacíos, un fotograma final inestable, colisiones de texto, texto sobre otros gráficos, recorte, contraste, tamaño mínimo de letra, líneas dash-offset que nunca se dibujan, bucles con movimiento reducido, errores de consola/página y sellos de plantilla.
Modo página renderiza escritorio 1440, móvil 390 y esquema oscuro, y verifica las mismas reglas de diseño más el desbordamiento horizontal en móvil.
Las salidas se colocan en qa-<nombre>/: report.md (veredicto y hallazgos), report.json (cobertura de tinta por fotograma y cambio), contact-sheet.png (cada fotograma en una imagen), moments.png (la entrada, el medio y el final de cada escena, tomados de window.SCENES), key_000..100.png, y frames/.
- Pasa el momento insignia y cualquier punto de sincronización explícitamente:
--at 5.4,9.1. - Las capas deliberadas (tipo cinético apilado a propósito, un título sobre material de archivo) van dentro de un elemento con
data-qa-allow-overlap. Eso omite las comprobaciones de colisión y texto sobre gráficos solo para ese subárbol. Nunca lo uses para silenciar una colisión accidental. - Una ADVERTENCIA
fontsignifica que la familia de primera elección no se cargó y el texto está en un respaldo. Arréglelo antes de juzgar el diseño, porque cada ancho está mal.
Código de salida 0 = sin FALLO. FALLO es bloqueante. Cada ADVERTENCIA está corregida o justificada en una línea en tu crítica.
9. Bucle de crítica
- Si tu modelo puede leer imágenes: abre
contact-sheet.png, luego cualquierkey_*.pngque parezca incorrecta. Juzga los píxeles, no tus intenciones. - Si tu modelo es solo texto: usa
report.mdmás la tabla por fotograma enreport.json.coverage< 0.015 significa que el fotograma se lee vacío. Undiffde 0 en una escena significa que nada se mueve. Un pico endiffmarca un corte. Verifica cada tarjeta de escena y sus marcas de tiempo contra estos números. - Responde cada pregunta con una observación concreta, no con "✓":
- Composición: en cada fotograma clave, ¿dónde aterriza primero el ojo? ¿Ese es el mensaje?
- Jerarquía: ¿PRIMARY es inconfundible? ¿Hay exactamente una cosa en movimiento que importa por fotograma?
- Mecanismo: ¿el momento insignia muestra la idea con las leyendas eliminadas?
- Continuidad: ¿todas las transiciones son limpias, sin nada en estado intermedio en un límite y los anclajes idénticos o en morph?
- Tiempo: ¿hay desplazamientos, sin duraciones predeterminadas, y tiempo de lectura ≥1.2s por cada 8 palabras?
- Tipografía / contraste / densidad: tamaños según el plan, ≤5 elementos compitiendo por fotograma, legible en modo oscuro?
- Narrativa: ¿podría un espectador repetir el mensaje central después de una visualización?
- Corregir → re-ejecutar
qa.mjs→ re-criticar. Repetir hasta que el QA pase y la crítica no tenga elementos abiertos. - Pregunta final, respondida honestamente: "¿Esto parece diseñado intencionalmente, o meramente generado?" Si "generado": nombra qué es genérico, revisa el plan (§1) y reconstruye. Un primer renderizado casi nunca es el entregable. Espera 2–3 generaciones.
10. Sincronización de audio (cuando hay música o voz)
Nunca adivines el tiempo de oído. Convierte el audio en datos y deja que render(t) los lea:
node "$SKILL/scripts/audio-data.mjs" music.mp3 --inject composition.html # escribe window.AUDIO en el HTML
AUDIO.beats(una cuadrícula espaciada uniformemente al tempo estimado),AUDIO.onsets(golpes, notas, sílabas), y por fotogramaAUDIO.rms/AUDIO.low(0..1;lowes la banda de kick/bajo <150 Hz).- Conduce el movimiento con los ayudantes de la plantilla:
hit(t, AUDIO.beats, 0.2)para un pulso de golpe (EMPHASIS),audioAt(t, "low")para escala reactiva a golpes,since(t, AUDIO.onsets)para revelaciones por golpe. Pon los cortes de escena en los beats: elige los inicios de escena deAUDIO.beats. - La sincronización es exacta por fotograma por construcción: un pulso aterriza en el primer fotograma en o después de su beat (≤1 fotograma, ≤33 ms a 30 fps). Si el audio comienza más tarde que t=0, pasa
--offset <s>. - Los niveles se verifican antes de que nadie escuche. Las muestras recortadas salen con código 1. Un pico de muestra por encima de −1 dBFS advierte, porque la codificación AAC lo recortará. Sonoridad fuera de −18..−12 LUFS se informa (las plataformas normalizan a aproximadamente −14).
--bpmanula la detección automática de tempo.--injectagregawindow.AUDIOa<script id="audio-data">.- Usa
--test-onlypara obtener un resumen de la canción sin inyectar.
El audio nunca está en el HTML renderizado. La exportación de video mezcla el audio original con la salida renderizada en un paso posterior.
11. Exportación de video (MP4, H.264)
Cuando el QA pase y la crítica esté completa, renderiza el video final:
node "$SKILL/scripts/export-video.mjs" composition.html
Requisitos: FFmpeg en PATH (ejecuta ffmpeg -version para verificar). El script:
- Renderiza la composición desactivando la vista previa interactiva (
--headless). - Captura fotogramas en la frecuencia de cuadros estándar (30 fps, o
--fps 60). - Codifica H.264 con perfil de máxima compatibilidad:
-pix_fmt yuv420p -profile:v high -level:v 4.0 -crf 18. - Aplica
--audio file.mp3para multiplexar el audio desde la fuente original. Sin él, el video no tiene sonido. - Escribe el resultado como
composition-video.mp4junto al archivo de entrada.
El paso --mux-only reemplaza el audio en un video ya renderizado sin volver a capturar fotogramas. Útil para ajustes de pista de voz sin tocar el diseño visual.
Salida de video: 1920×1080 (o la resolución configurada en la plantilla), 30 fps por defecto, duración exacta de DURATION. Los metadatos del archivo se escriben con creation_time desde la fecha de construcción.
12. Almacenamiento de entregables y versionado
Cada ejecución exporta contact-sheet y reportes a qa-<nombre>/ y el video a *-video.mp4. Nunca sobrescribas manualmente: deja que qa.mjs y export-video.mjs gestionen sus directorios. Si un entregable necesita múltiples iteraciones, usa archivos de entrada con nombres diferentes o mueve las salidas anteriores a un subdirectorio archive/.
Los activos de referencia (references/, assets/) son de solo lectura. Si cambias la plantilla o las reglas, hazlo en tu propia bifurcación.
Opus Visual Motion Engine v3.2 - Disciplina sobre opinión.