¿Qué hace motion-reels?
Gráficos en movimiento dibujados con código para Reels verticales. Cada reel consta de uno o más bloques. Un bloque es un pequeño archivo JavaScript que coloca titulares, dibujos, fotos y muestras en un mundo y dice cuándo aparece cada uno. Cada instante proviene de una línea de tiempo de palabras: ya sea una hoja de ritmos (sin voz) o las sincronizaciones de palabras de una toma de voz. El motor renderiza cada fotograma en su propia página de navegador nueva, comprueba el diseño y ensambla el resultado con voz y/o música.
La persona dueña de la página decide el aspecto, las palabras y qué se publica. Claude nunca publica nada.
Configuración (una vez)
- Python 3.10+ y ffmpeg/ffprobe en PATH. Luego el
setup.ps1(Windows) osetup.sh(macOS/Linux) del repositorio, opip install -r requirements.txt+python -m playwright install chromium(o la variable de entornoMG_CHROME= un Chrome existente). - Solo voz:
pip install -r requirements-voice.txt(faster-whisper; la primera ejecución descarga el modelo de voz). - Prueba:
python engine/selftest.pydebe imprimirSELFTEST OK(las comprobaciones pasan en los ejemplos, las pruebas negativas fallan como deben, los subtítulos en árabe se dividen correctamente, y un render corto + escaneo de fallos + ensamblaje alcanza -14 LUFS).--quickomite el render.
Archivos del motor (todos en engine/): render.py (comprobar / storyboard / fotogramas fijos / video), beats.py, words.py,
frames.py, faultscan.py, assemble.py, storyboard_sheet.py, make_grain.py, templates/ (shell, reel.js, reel.css, fuentes).
Una carpeta por reel
<nombre del reel>/
brief.md para qué es el reel, la idea principal, el llamado a la acción, fotos disponibles
beats.txt las palabras en pantalla, un ritmo por línea (o script.txt + take.wav cuando hay voz)
words.json línea de tiempo (de beats.py o words.py)
photos/ las fotos del dueño (solo aquellas de las que tiene los derechos)
A.js, B.js ... bloques
build/ json de comprobación, fotogramas de storyboard, videos de bloques, archivos render.json, hojas de contacto
<Reel>_REV01_<fecha>.mp4 el archivo entregable; una corrección posterior se entrega como _REV02, nunca se sobrescribe
Puertas (nunca saltar ni reordenar; detenerse en cada ⛔ para la aprobación del dueño)
- Brief. Una idea por reel (una revelación de habitación, una historia de materiales, un cambio de plano, un consejo). Pide las fotos y el identificador. Enumera lo que muestra cada foto. Engancha en los primeros 2 segundos.
- Hoja de ritmos (
beats.txt): las palabras exactas en pantalla, en orden, un ritmo por línea. Mantén un ritmo corto (2–7 palabras). Pon cada elemento de una lista en su propia línea, para que cada uno tenga tiempo de asentarse. ⛔ El dueño firma las palabras. Después de la aprobación, un cambio de palabras implica re-sincronizar el bloque (y regrabar la voz, si la hay): dilo antes de cambiar algo. - Línea de tiempo.
- Sin voz:
python engine/beats.py beats.txt words.json(ritmo predeterminado 2.6 palabras/s + 0.7 s después de cada ritmo;[+1.5]al final de una línea mantiene la pausa más tiempo;[pause 2]mantiene la pausa sin nada nuevo). - Voz (cualquier TTS o una grabación, una toma por bloque, nunca editada):
python engine/words.py take.wav words.json, luego compara la transcripción con el guión palabra por palabra. La imagen se ajusta a la toma, nunca al revés.
- Sin voz:
- ⛔ Fotogramas de storyboard. Construye los diseños de los bloques (solo las posiciones son suficientes), ejecuta
check, luegorender.py A.js words.json storyboard build/sbystoryboard_sheet.py build/storyboard.png build/sb. Revisa la hoja tú mismo (lista más abajo), luego envíala. Nada se anima antes de que el dueño apruebe el diseño. - Construye el movimiento. Sincroniza cada elemento con la palabra que lo nombra (reglas más abajo). Ejecuta
checkdespués de cada cambio. - Render.
render.py A.js words.json video build/A.mp4 --workers 4por bloque (nunca--draftpara nada que se envíe). Elbuild/A.mp4.render.jsonde cada bloque debe indicar"capture": "fresh"y el mismo navegador que los otros bloques; si el navegador cambió, vuelve a renderizar todos los bloques. - Escaneo de fallos.
faultscan.py build/A.mp4 --sheet build/A_contact.pngen cada bloque, luego en el reel final. Revisa cada instante marcado. Una banda negra o un destello de un fotograma es un fallo: detente y corrígelo. El contenido oscuro de una foto es aceptable. - Ensamblaje.
assemble.py blocks.json <Reel>_REV01_<fecha>.mp4 [--music bed.mp3]. Imprime el volumen medido; debe estar en el objetivo (-14 LUFS por defecto) y el pico verdadero en o por debajo de -1.5 dBTP. - ⛔ Revisión. Envía el reel y su hoja de contacto. Las correcciones solo de imagen vuelven a renderizar solo los bloques cambiados y se entregan como la siguiente _REV. El dueño lo publica.
Reglas de movimiento (por qué: una sensación tranquila y premium; nada tiembla, nada es difícil de leer)
- Las palabras nunca se mueven. Una palabra aparece (0.28 s) y luego permanece quieta. Sin deslizamiento, escala, rebote ni deriva en el texto.
- Titulares:
hl(el, text, cue(...))hace aparecer cada palabra en su propia palabra en la línea de tiempo. Cuando un titular parafrasea una voz, usa{stream:true}: las palabras aparecen de izquierda a derecha, con 0.1 s de separación, desde la señal. - Cámara: completamente quieta mientras se mantiene una toma; solo se desliza (0.8 s, ease-in-out) entre tomas. En 9:16, apila las tomas
verticalmente en el mundo (toma 2 en y 1920, toma 3 en y 3840) y deslízate hacia abajo:
SHOTS=[[t,540,960,1],[t2,540,2880,1],...]. - Las fotos pueden acercarse lentamente:
push:[1,1.04]= velocidad constante, nunca más de +6 % (MOTION.PUSH_MAX; el motor rechaza más). Sin push en una foto que lleva texto. - Los dibujos se dibujan a medida que se nombran, referencia primero: paredes antes de la ventana y la puerta en ellas, la línea del suelo antes de las
alturas medidas desde ella.
at('#walls', cue('Walls'), null, 'draw', 0.9); 0.6–1.0 s por elemento. - Dimensiones a una sola escala. Si un plano muestra dos dimensiones, ambas usan la misma escala.
- Etiqueta lo que las palabras nombran; cada número nombra su unidad ("3.2 m", nunca un "3.2" desnudo).
- Sin rebote, sobreimpulso, ease elástico ni de resorte en ningún lugar. Cada sincronización proviene de
MOTIONenreel.js; cámbialo ahí, nunca como literal en un bloque. - Un elemento SVG discontinuo desnudo dado a
at()se muestra antes de su señal: envuélvelo en un<g>.
Tokens de sincronización (MOTION en reel.js)
| Token | Valor | Uso |
|---|---|---|
| WORD_FADE / WORD_LEAD | 0.28 s / 0.08 s | aparición de palabra; comienza justo antes de la palabra |
| STREAM | 0.10 s | espaciado de palabras en titulares en modo stream |
| REVEAL / EXIT | 0.5 s / 0.4 s | bloques, chips, muestras, fotos dentro / fuera |
draw (at(...,'draw',dur)) | 0.6–1.0 s | por elemento dibujado |
| CAM_SLIDE / CAM_LEAD | 0.8 s / 0.35 s | deslizamiento de cámara; comienza antes de la señal de la toma |
| PUSH_MAX | 0.06 | límite de push de foto |
| WIPE | 1.2 s | barrido de antes/después |
| Easing: ease-out cúbico para revelados, ease-in-out cúbico para deslizamientos, dibujos y barridos. |
Diseño y la comprobación
- Zona segura (
SAFEen reel.js): texto, etiquetas y subtítulos permanecen dentro de x 65–1015, y 269–1248 del fotograma 1080x1920 (14 % superior, 35 % inferior, 6 % laterales). Estas cifras son una guía ampliamente utilizada de terceros para Reels, no una regla publicada por Meta para publicaciones orgánicas: vuelve a medir desde una captura de pantalla de un reel publicado y establecesafeZone({...})si la aplicación cambia. Las fotos y dibujos pueden extenderse más allá; el texto no. render.py <block> words.json check build/<block>.check.jsones obligatorio antes de cada render. Muestrea cada 0.25 s y falla en cualquier superposición mantenida 0.5 s o más entre elementos separados (texto, dibujos, chips, muestras, fotos, subtítulos) y en cualquier texto fuera de la zona segura mientras la cámara se mantiene. Un fallo significa arreglar el diseño, nunca renderizar. Demuestra una nueva regla con una prueba negativa (reintroduce un fallo conocido; la comprobación debe fallar).- El texto que pertenece a una foto va dentro del elemento de la foto (
parent: photoDiv) con unscrim()detrás, o la foto es un fondo (bg:true). De lo contrario, la comprobación informa texto sobre foto, que es lo que quieres. - La comprobación no ve cosas cortadas en el borde del fotograma: la autorevisión sí.
Construcción de bloques (reel.js)
boot(()=>{ // todo bloque comienza con boot(...)
handle('@yourstudio'); // identificador pequeño en la parte superior de la zona segura
$('world').insertAdjacentHTML('beforeend', `<div id="h1" class="abs serif" style="left:90px;top:360px;width:900px;font-size:104px"></div>`);
hl('h1', 'A small room, *re-planned.*', cue('A small')); // *...* = acento cursiva; ~word = no está en la línea de tiempo
at('#walls', cue('Walls'), null, 'draw', 0.9); // fx: 'fade' | 'pop' | 'wipe' | 'draw'
photo({src:rel('photos/living.jpg'), x:0, y:1920, w:1080, h:1350, a:cue('Light'), push:[1,1.04]});
beforeAfter({before:rel('photos/before.jpg'), after:rel('photos/after.jpg'), x:90, y:2440, w:900, h:900, a:cue('Before'), t:cue('after')});
swatch({name:'Oak', note:'floor', color:'#B08A62', x:90, y:4480, size:270, a:cue('Oak')}); // o src: rel('photos/oak.jpg')
clip({dir:rel('clips/walk'), n:240, x:0, y:5760, w:1080, h:1920, a:cue('Walk'), t0:cue('Walk')}); // fotogramas de frames.py
captions(1120, 3); // subtítulos quemados de la línea de tiempo (reels con voz)
SHOTS=[[0,540,960,1],[cue('Light'),540,2880,1]];
});
rel(path) resuelve un archivo junto al bloque. cue('words', after) lanza un error si las palabras no están en la línea de tiempo, por lo que un
error tipográfico falla de forma notoria. Ejemplo completo: examples/demo/demo.js.
Reels en árabe y otros de derecha a izquierda
- Llama a
rtl()primero enboot(). Los titulares usan Noto Naskh Arabic, el texto IBM Plex Sans Arabic (OFL); el latín usa las fuentes predeterminadas. Sin cursiva, espaciado entre letras ni mayúsculas en árabe (las letras deben unirse); las palabras acentuadas cambian solo de color. - Los cuadros de texto mantienen
left+width; el texto se alinea al borde derecho. Coloca las listas de derecha a izquierda (el primero nombrado = el más a la derecha). - Las señales ignoran diacríticos, tatweel, variantes de alef/yaa/taa-marbuta y convierten dígitos arábigo-indios; una señal sin letras da error.
ة y ه cuentan como una letra, así que señala una frase más larga donde dos palabras difieren solo ahí. Las palabras numéricas coinciden en
inglés solamente: en árabe, señala los dígitos o la palabra según se transcribe (
words.py --lang arelige el modelo multilingüe). - Los subtítulos se rompen en ، ؛ ؟ y puntuación latina, y no la muestran.
- Ejemplo:
examples/arabic/.
Aspecto
El tema vive solo en engine/templates/reel.css (colores como variables CSS, fuentes) y los archivos de fuentes. Valor por defecto "minimalista
cálido": papel #F4EFE8, tinta #2B2622, acento #9C6B4E, titulares Cormorant Garamond + texto Jost (ambos SIL OFL 1.1).
Para cambiar el aspecto: edita las variables, o añade fuentes OFL a engine/fonts/ con su archivo de licencia y enuméralas en
fonts.css y window.FONTS (reel.js). Pide al dueño 3–5 capturas de pantalla de su cuadrícula primero y ajusta.
Autorevisión (Claude, antes de enviar cualquier hoja o render)
- Cada elemento comienza en su palabra; las líneas de referencia se dibujan primero.
- Las palabras permanecen quietas una vez colocadas; la cámara quieta mientras se mantiene; sin salto al final de un deslizamiento.
- Nada cortado en el borde del fotograma; todo el texto dentro de la zona segura; nada importante debajo del 35 % inferior.
- Dimensiones a una sola escala; cada número tiene su unidad; la ortografía coincide con la hoja de ritmos firmada.
- CHECK PASS en cada bloque;
render.jsondicecapture: fresh, mismo navegador para todos los bloques. - Escaneo de fallos limpio, o cada instante marcado inspeccionado y explicado.
- Volumen y pico verdadero impresos por
assemble.pyen el objetivo.
Derechos y privacidad
- Solo las fotos del dueño, o aquellas de las que tiene los derechos. Casas de clientes: sin rostros, direcciones ni documentos sin consentimiento.
- Música: solo pistas para las que el dueño tenga licencia, o la biblioteca propia de la plataforma añadida en la aplicación al publicar.
- Sin afirmaciones que el dueño no haya hecho (precios, plazos, premios).
Solución de problemas
cue not found: "...": las palabras no están enwords.jsonen ese orden; corrige la hoja de ritmos o la señal.ERR reqfail:file:///...jpg: la ruta de una foto o clip es incorrecta; usarel()y verifica el nombre del archivo.- Copias fantasma de dibujos anteriores o una banda negra: renderizaste con
--draft. Vuelve a renderizar sin él. - Renders lentos:
--workers 4(hasta 8) en una máquina con esa cantidad de núcleos; comprobación y storyboard son rápidos, el video no.