¿Qué hace Framewright?
Vas a hacer un video corto como un grabador hace un grabado: un archivo HTML con un canvas, una lista de escenas llamadas placas, y una función frame que dibuja cualquier fotograma bajo demanda a partir de (número de fotograma, semilla, ancho). Sin metraje, sin archivos de imagen, nada desde un CDN. Los fotogramas se renderizan en Chrome sin interfaz gráfica y se ensamblan con ffmpeg. El sonido se sintetiza al final, ajustado a las longitudes de escena bloqueadas.
Habla al usuario en su idioma. Mantén las reglas siguientes incluso cuando el usuario tenga prisa: cada atajo aquí cuesta más de lo que ahorra.
SKILL_DIR abajo significa la carpeta que contiene este archivo (en Claude Code es
${CLAUDE_SKILL_DIR}; en otros entornos es .agents/skills/framewright). Después del paso 3 los
scripts también están en la carpeta scripts/ del proyecto.
No negociables
- Un fotograma depende solo del número de fotograma, la semilla y el ancho de salida. Nada de
Math.random, ni reloj de pared, ni animación CSS, nirequestAnimationFramecomo fuente de tiempo.scripts/check.mjslo prueba antes de cada render completo. - Sin archivos de imagen, video, fuente o biblioteca, sin base64, sin CDN. Los polígonos trazados desde una foto mediante un script son datos y están bien. Las fuentes del sistema son suficientes.
- Verificas mirando fotogramas renderizados, nunca leyendo código e imaginando. Cualquier arreglo se confirma con un fotograma.
- Una escena a la vez. Escribe una placa, dispara tres fotogramas, mira, arregla, luego la siguiente placa.
- Las longitudes de escena son múltiplos del pulso del primer boceto. El sonido llega al final.
- Render completo solo después de que una hoja de contactos de todo el video se vea correcta.
- Nunca subas
frames/,shots/,*.mp4,*.wavni las fotos del usuario. - Pregunta antes de instalar paquetes del sistema. Nunca publiques la apariencia de una persona real sin que el usuario confirme que tiene derecho a usarla.
Flujo de trabajo de un vistazo
| Paso | Qué sucede | Punto de control con el usuario |
|---|---|---|
| 0 | verificación del entorno | solo si algo debe instalarse |
| 1 | brief | cuestionario, un bloque |
| 2 | conceptos | tres opciones, elegir una |
| 3 | storyboard + andamio | un "adelante" |
| 4 | escenas, una por una | hoja de contactos cada 2-3 escenas |
| 5 | activos (fotos, logotipos) | vista previa del retrato trazado |
| 6 | hoja completa, render, compilación | vista previa MP4 |
| 7 | sonido | MP4 reconstruido |
| 8 | entrega y variaciones | final |
Tiempo típico de pared: brief y conceptos 10 minutos, andamio 10, cada escena 10-20, render 2-4 minutos por minuto de video 1080p, sonido 15.
Paso 0. Entorno
bash SKILL_DIR/scripts/doctor.sh # informe; añade --json para un resumen legible por máquina
bash SKILL_DIR/scripts/doctor.sh --install # instala lo que falta, pregunta antes de cada paso
Requerido: Node 20+, npm, ffmpeg con libx264, Puppeteer con Chrome en el proyecto.
Opcional: Python 3 con numpy, scipy, Pillow, solo cuando se trazará una foto. Si el doctor
informa herramientas faltantes, dile al usuario qué se instalará y dónde, recibe un sí, luego ejecuta
--install (añade --yes cuando el usuario ya aceptó). No comiences el paso 3 con un doctor fallando.
Paso 1. El brief
Ejecuta el cuestionario de references/questionnaire.md. Usa la herramienta de preguntas estructuradas del
armazón cuando exista (Claude Code: AskUserQuestion, hasta cuatro preguntas por llamada,
dos a cuatro opciones cada una, pon la opción recomendada primero y márcala). Sin tal herramienta,
imprime las preguntas como una lista numerada con opciones con letras y un valor predeterminado por pregunta, y
acepta respuestas concisas como 1b 2a 3c. Siempre pregunta, en este orden:
- Mensaje: qué debe entender o sentir el espectador, en una oración. Nombres, fechas, enlaces, frases exactas que deben aparecer en pantalla.
- Dónde se reproduce y el formato: 16:9, 9:16, 1:1, 4:5.
- Duración: 10-15, 20-25, 30-40, 45-60 segundos.
- Tono: juguetón, inexpresivo, cálido, épico, técnico, oscuro.
- Estilo: dejar que propongas (predeterminado) o una familia de
references/styles.md. - Activos: fotos, logotipos, capturas de pantalla. Los archivos deben colocarse en la carpeta del proyecto; una imagen pegada en el chat no puede ser guardada por ti. Pregunta sobre los derechos de las personas reales.
- Sonido: sintetizado, ninguno, o espacio para la pista del usuario (luego pregunta su BPM).
- Entregables y calidad: borrador o final, cortes adicionales (vertical, GIF, fotograma de póster, variaciones de semilla), una portada como primer fotograma del MP4 (los feeds la muestran como vista previa).
Si el usuario dice "tú decides", toma los valores predeterminados, di cuáles tomaste y continúa.
Paso 2. Conceptos
Propón exactamente tres conceptos antes de cualquier código. Constrúyelos desde tres ángulos: literal (muestra el
sujeto), metáfora (un sistema visual que representa la situación: una carta de ajuste de TV para la
espera, un plano para un proyecto, una terminal para un proceso), y parodia de género (boletín de noticias,
modo de atracción de arcade, tarjeta de seguridad, pronóstico del tiempo, teletexto). Para cada concepto da: un nombre,
una frase de una línea, el sistema visual y la paleta, tres a cinco escenas clave en orden, el
final, un esbozo de sonido, por qué encaja, y su principal riesgo. Presenta una tabla compacta, recomienda
uno, y pide al usuario que elija. El método y los ejemplos trabajados están en
references/questionnaire.md. Un concepto cuya frase es un viaje sin cortes (una línea que nunca
se levanta, un vuelo desde un detalle hacia un paisaje) se construye como un mundo continuo
(references/world.md); dilo en su fila, porque cambia cómo se construyen y modifican las escenas.
Un concepto que es una vista dibujada, pintada y cobrada vida (un dibujo a lápiz que se convierte en una
pintura al óleo viva, estilos 13-16) se construye como una película de pintura (references/painting.md); dilo
en su fila también: sus placas son capítulos de tiempo sobre una pintura, y la escena es la obra.
Paso 3. Storyboard y andamio
Llena storyboard.md (plantilla en assets/storyboard.md): una fila por placa, longitudes en
compases, la cuadrícula de pulso (30 fps, 120 BPM: pulso 15 fotogramas, compás 60), el segundo del evento
principal, el total. El total debe igualar la duración acordada; cuando se añade una escena más tarde, otra
escena cede un compás. Muestra la tabla y pide un solo "adelante".
Luego el andamio:
bash SKILL_DIR/scripts/init.sh # index.html, scripts/, audio.mjs, storyboard.md, package.json
bash SKILL_DIR/scripts/init.sh . --world # lo mismo, pero index.html es el esqueleto de un solo mundo (sin cortes)
bash SKILL_DIR/scripts/init.sh . --painting # el esqueleto de pintura; audio.mjs y nature.mjs crean sonido de naturaleza
npm install # puppeteer (Chrome viene de la caché o se descarga una vez)
node scripts/look.mjs shot 0,30,60 1200 7
Abre los PNG con tu herramienta de visualización de imágenes y míralos. La placa de demostración del esqueleto debe
renderizarse antes de que la toques. Luego reemplaza la placa de demostración con tu primera escena. En una película de pintura
el fotograma 0 es papel en blanco: dispara 30,82,200 (el dibujo, el color llegando, la pintura viva)
en su lugar, y reemplaza el bloque de escena de la demostración mientras el kit de pintura permanece como está.
Paso 4. Escenas
Lee references/guide.md una vez antes de la primera escena. Lo esencial:
plate('name', {len: 2*BAR}, (S, R) => { ... }).S.tes progreso 0..1,S.iel fotograma local,S.fel fotograma global,S.gel contexto 2D en coordenadas lógicas (lado corto 1080).Res un generador estable para toda la placa;S.bre-sembrado cada tres fotogramas y da una línea viva;S.nzcambia cada fotograma para ruido. Las semillas derivan del nombre de la placa.- Toda la geometría en unidades lógicas, centrada a través de
CX, CY. Mantén el contenido importante dentro del 92 % central del fotograma; en 9:16 también fuera del 15 % superior y el 20 % inferior, donde están los botones y subtítulos de la plataforma. - Tiempo dentro de una placa en pulsos:
Math.floor(S.i/BEAT),span(S.i, a, b),ease.out(...). Nunca codifiques números de fotograma global dentro de una escena. Algo que continúa a través de cortes toma el tiempo de otra placa por nombre,at('plate', beats); un momento que el sonido debe alcanzar va enCUESy se lee concue('name'). - Los ayudantes viven encima del bloque de placas, nunca entre placas.
- Las transiciones son dibujadas por el motor en los bordes de las placas (indicadores
cutIn/cutOut); las escenas no saben de ellas. - Una película de mundo (
--world) no tiene cortes. Las placas son constructores,build(W, {pen, b, R}), que añaden a una línea de tiempo:b(x)es el pulso x de la placa, el lápiz continúa la línea desde donde la placa anterior lo dejó,camKeymueve una cámara,titleañade texto. Leereferences/world.mdantes del primer constructor. - Una película de pintura (
--painting) dibuja una pintura fija en tres medios. Sus placas todas llaman adrawPainting(S); la vista vive en el objetoSCENEy sus capas entre/* ===== scene ===== */y/* ===== end scene ===== */, y el kit de pintura sobre ella permanece como está. Leereferences/painting.mdantes de la primera capa. Revisa el dibujo solo primero, luego la revelación, luego la vida (references/painting.md, sección 13).
Por escena: escríbela, dispara el primer fotograma, uno en el medio, uno cinco fotogramas antes del final, mira, arregla, mira de nuevo. Después de cada dos o tres escenas:
node scripts/look.mjs sheet 24 480 7 shots/sheet.png
FROM=240 TO=480 node scripts/look.mjs sheet 12 480 7 shots/sheet-b.png # una sección, más densa
Lista de verificación para la hoja, cada elemento con tus ojos: cada celda se lee como un fotograma fijo en 1.5 segundos; ningún texto está recortado; líneas y paneles vecinos no se tocan; dos celdas adyacentes
de una placa nunca se ven idénticas; los cortes caen donde se planeó y no se comen el primer fotograma
de una escena; nada importante está en el 8 % exterior; el color no es turbio; las líneas finas no se deshacen; el evento principal está en su segundo planeado; el último fotograma se ve como un final; ningún título comparte la pantalla en el mismo lugar. Una escena que falla "1.5 segundos" se rediseña, no se decora. Una escena vacía recibe un objeto más grande o una cámara más cercana, nunca más detalle; un primer plano ocupado tiene su decoración atenuada alrededor del sujeto. En una película de mundo también dispara el último fotograma de cada placa junto al primer fotograma del siguiente: nada puede saltar.
Envía la hoja al usuario en estos puntos con dos oraciones de estado. No narres código.
Paso 5. Activos
Las fotos se convierten en polígonos posterizados, nunca píxeles:
bash scripts/portrait.sh foto.jpg --levels 0.14,0.28,0.42,0.56,0.70,0.84 --height 900
node scripts/look.mjs shot <fotograma en la escena de retrato> 1200 7
Mira shots/portrait_preview.png y el fotograma renderizado. Ajusta niveles, desenfoque, recorte y
umbral de fondo según references/photo.md. Mientras el archivo aún no está en la carpeta, construye
la escena sobre el marcador de posición sintético que viene en el esqueleto y dilo.
Paso 6. Hoja completa, render, compilación
node scripts/look.mjs sheet 24 480 7 shots/sheet.png # míralo, arregla, repite
node scripts/check.mjs # determinismo y autocontención, debe decir OK
node scripts/render.mjs frames 7 1920 5 # dir, semilla, ancho (omitido: 1920 horizontal, 1080 vertical), pestañas
bash scripts/build.sh out.mp4 # frames (+ track.wav) -> mp4
La verificación renderiza cinco fotogramas de cada placa en siete órdenes (primer fotograma de una nueva pestaña,
adelante, atrás, mezclado, justo después del fotograma anterior, después de otro ancho, después de un
barrido sobre la película como lo recorre una pestaña de render) y exige píxeles idénticos. También escanea
la fuente en busca de Math.random, Date y performance.now, falla cuando la página solicita cualquier
archivo, e imprime la cuadrícula de pulso y el tiempo de render esperado. Arregla cada FALLO antes de renderizar;
make.sh ejecuta la verificación y se detiene en un fallo. Un fotograma que cambia con el orden de render apunta a
un estado que sobrevive a un fotograma: un canvas agrupado dibujado sin limpiar, un global que una placa cambia, una
clave de caché sin todas sus entradas.
Verifica desde el archivo, no desde los fotogramas: el conteo de fotogramas de ffprobe iguala el total de
look.mjs info, la duración iguala fotogramas dividido por 30, y un mosaico hecho desde el MP4 coincide
con la hoja:
ffmpeg -i out.mp4 -vf "select='not(mod(n\,50))',scale=480:-1,tile=6x4" -frames:v 1 shots/out_sheet.png
Envía el primer MP4 al usuario tan pronto como exista, incluso con marcadores de posición, y di qué es un marcador de posición.
Paso 7. Sonido
scripts/export-curves.mjs escribe curves.json desde la página: inicios de placa, las señales, y para
cada fotograma en una película de mundo la velocidad y posición en pantalla de la cabeza de la línea, en una película de pintura
las cuotas reveladas, vida, gust y zoom. audio.mjs toma su línea de tiempo de ahí, por lo que nunca
se desvía de la imagen (una página sin RISO.curves() necesita los inicios de look.mjs info
copiados en T a mano). Escribe un bloque de eventos por placa usando el mapa de señales en
references/audio.md; los golpes en algo que la imagen marca toman su tiempo de las señales de la página,
cue('name') (CUES, o W.cues en una película de mundo), no de números de pulso copiados de una placa;
en una película de mundo dale a la línea o al personaje una voz que siga las curvas (follow(),
references/audio.md, sección 7). Una pintura o un paisaje recibe ambiente de naturaleza en lugar de
pulsos: viento, agua, susurro y pájaros desde nature.mjs, sincronizados con las señales y con el gust de la página
por el audio.mjs que --painting copia desde assets/audio-nature.mjs (references/audio.md,
sección 8, Ambiente de naturaleza). Luego:
node scripts/export-curves.mjs curves.json
node audio.mjs track.wav
ffmpeg -i track.wav -filter_complex "showwavespic=s=1800x300:split_channels=1" -frames:v 1 shots/wave.png
bash scripts/build.sh out.mp4
No puedes escuchar, así que lee la forma de onda: sin silencio accidental dentro de escenas activas, sin escena anclada al techo. Cada vez que cambie la duración de una escena, regenera la pista.
Paso 8. Entrega
Entrega out.mp4 (1920×1080, H.264, AAC, ~0.7 MB por segundo en estilos ruidosos) y di cómo
reconstruir: bash scripts/make.sh [foto.jpg] [semilla] [ancho]. Ofrece, no impongas:
otra semilla (una impresión diferente de las mismas placas), un corte vertical
(AR=9:16 node scripts/render.mjs frames-v 7 1080 5, luego verifica una hoja con AR=9:16; los títulos
inferiores están al 70 % de la altura allí, TITLE_Y, libres de los botones de la plataforma; un