¿Qué hace motionkit?
Una película es una página más un script cuyo renderFrame(f) dibuja el fotograma f (30 por segundo, 1920x1080) y nada más. Chrome oculto avanza los fotogramas, ffmpeg codifica (el mp4 se duplica a 60 fotogramas por segundo con alta calidad, aproximadamente 4 a 5 MB por segundo de película) y la banda sonora se sintetiza.
Dónde están las cosas
- Motor: la carpeta
engine/junto a este archivo (movimientos, renderizador, sonido, comprobaciones, fuentes). Llámalo$Ey usa su ruta completa. Léelo; nunca coloques películas en él (la configuración añade su propionode_modulesallí, lo cual es esperado). - Carpeta de trabajo: la carpeta en la que el usuario está trabajando. Las películas (
<nombre>.html,<nombre>.js),assets/,ref/yout-<nombre>/van allí, y todos los comandos se ejecutan desde allí. Una página encuentralib.js,moves.jsyfonts/en el motor por sí misma. Si la carpeta de trabajo es un repositorio git, añadeout-*/yref/a su.gitignore.
Primero: configuración (una vez por máquina)
- Ejecuta
sh $E/setup.sh. Solo comprueba: bun, ffmpeg con ffprobe, python3, uv, un navegador Chromium (Chrome, Chromium o Edge;CHROME_PATHlo sobrescribe) y el único paquete del motor. - Si imprime
Ready, continúa. Si solo falta elengine package(normal en una instalación nueva), ejecutash $E/setup.sh --installsin preguntar: solo obtiene un paquete en la propia carpeta del skill. Si una herramienta o el navegador estánMISSING, muéstrale al usuario esa lista y los comandos de instalación que imprimió, y pregunta una vez antes de instalar software en su máquina. - Si responde que sí, ejecuta
sh $E/setup.sh --install(Homebrew en macOS, los instaladores oficiales de bun y uv), luego vuelve a ejecutar la comprobación. Las líneas que dicenrun this yourselfnecesitan la contraseña del usuario (sudoen Linux) o una descarga manual: nunca las ejecutes; dale al usuario el comando exacto y espera. Si una instalación falla, dale al usuario el comando único para que lo ejecute él mismo. - Demuéstralo que funciona: desde la carpeta de trabajo ejecuta
bun $E/build.ts helloy confirma que existeout-hello/hello.mp4.
Probado solo en macOS; indícalo si el usuario está en Linux o Windows.
Ejemplos de trabajo para copiar: $E/hello.js (una película sobre una cuadrícula de ritmo, cada sonido de clips, escenario dibujado en código), $E/title.js (un título transparente para superponer sobre metraje), $E/moves-demo.js (cada movimiento una vez).
Construir una película: primero los clips
Declara cada uso de un movimiento una vez como un clip. La imagen, el sonido y la comprobación del tiempo de lectura provienen de esa única línea.
const M = window.MOVES, B = M.brand, G = M.grid(120); // 120 bpm: un pulso son 15 fotogramas, un compás 60
const K = { line: M.clip("words", G.bar(1) + 2, 10, { text: "Copia el movimiento.", x: 960, y: 590, size: 110, fam: B.display, stretch: B.wide, weight: 600, color: B.paper, until: G.bar(2) }) };
// en una escena: K.line.draw(X, f) K.line.on(f) es verdadero mientras deba ser visible
window.timeline = () => ({ fps: 30, total: TOTAL, bpm: G.bpm, sections: [[0, "intro"], [4, "build"], [8, "drop"], [24, "outro"]], cues: M.cues(), captions: M.captions() });
Para una película de lanzamiento o producto, lee primero «Películas de lanzamiento y producto» a continuación. Inicia una película con bun $E/new.ts <nombre>: escribe <nombre>.html y <nombre>.js en la carpeta de trabajo, copiados del ejemplo hello. Reescribe los clips, las escenas y el texto. bun $E/build.ts <nombre> luego hace, en orden: línea de tiempo, banda sonora (sound.py), comprobación de sonido, comprobación de tiempo de lectura (lint.py), renderizado, unión, comprobación de sincronía (sync.py) y escribe out-<nombre>/<nombre>.mp4. Una línea de tiempo sin bpm obtiene una banda sonora más simple de audio.py (solo cortes). Cambios de línea de tiempo: sound: "fx" (solo sonidos de movimientos, sin música), sound: "none", alpha: true (fondo transparente, salida .mov; consulta la sección de EditMax).
Anatomía de un script de película
$E/hello.js es el modelo; léelo una vez. Qué es cada parte:
- Globales de
lib.js:W,H(1920, 1080),X(el contexto del canvas),clamp,lerp,TAU,hash,noise,txt(ctx, text, x, y, size, weight, color, fam, italic, align),rr(rectángulo redondeado),vignette(strength, innerRadius). M.clip(move, start, length, options):startylengthson fotogramas;lengthes cuánto tiempo tarda el movimiento en reproducirse, no cuánto tiempo permanece. Devuelve{ draw(ctx, f), t(f) (0..1 durante el movimiento), on(f), o (las opciones) }. Después de su duración, un clip sigue dibujando su estado final: no se oculta. Ocúltalo terminando la escena, o conif (K.x.on(f)), dondeones verdadero desdestarthastauntil(para siempre sinuntil). Para mostrar un movimiento de texto terminado dentro delfromde una transición, dibújalo ent = 1:M.words(ctx, 1, K.line.o). Los efectos de un solo disparo (burst,glitchCut) también necesitanK.x.t(f) < 1.- Formas de llamada dentro de un clip: las transiciones toman
fromytoen las opciones; los envoltorios (punch,shake,sweep,smear) tomandraw. Todo lo demás (pathDraw,burst,bars,chart,callout,logoDraw,bounce,ticker) solo toma opciones.meshyparallaxson fondos: llámalos directamente,M.mesh(ctx, f / 240, { colors }), dondetes solo un número que crece lentamente. - Opciones de texto:
xes el centro de la línea (align: "center"es el predeterminado),yla línea base.stretch: B.wideda el corte ancho,stretch: nulllo desactiva. Nada mide ni ajusta el texto por ti: con un tamaño de 150 ancho, 13 caracteres abarcan aproximadamente 1340 px, así que renderiza un fotograma fijo y verifica que cada línea quepa dentro del marco con margen. - Tabla de escenas:
[primer fotograma, draw(f), drawAfterFinish(f)].renderFramerestablece el estado del contexto (mantén esa línea), dibuja la escena actual, luego grano y viñeta, luego la tercera ranura para cualquier cosa que deba permanecer limpia (logotipos). window.ready: una promesa que el renderizador espera:Promise.all([M.fonts(), M.levels("out-<nombre>/levels.json"), M.load({ ... })]).M.levelssolo se necesita paraM.eqy es seguro antes de que el archivo exista.- Línea de tiempo:
sectionsson[pulso, nombre]; una película corta puede usar solo[[0, "intro"], [8, "drop"], [16, "outro"]], y cada inicio dedroprecibe el medio pulso silencioso (mantén omute:clips allí).syncenumera los fotogramas donde un corte y un golpe de bajo deben coincidir: usa el primer fotograma de cada escena a partir del drop. El «cambio» se evalúa en comparación con la diferencia habitual de fotograma a fotograma de la película, por lo que si una transición termina exactamente en un fotograma de sincronía, no deja ningún cambio allí y la comprobación imprimeNO CHANGE: añade un destello corto o inicia el siguiente movimiento en ese fotograma.cutses cada inicio de escena. - Colores: lee todo de
M.brand(B).M.rgba(B.ink, 0.7)da un color con una opacidad,M.mix(a, b, t)un color entre dos.M.setBrandcambiaM.branden su lugar; llámalo antes de declarar cualquier clip y estableceaccentOnDarktambién (el acento usado en fondos oscuros; no sigue aaccent). Para una fuente sin corte ancho, establecewide: null. - Duración: el tiempo de lectura establece el mínimo: una línea de 3 palabras necesita aproximadamente 1.3 s después de haber aparecido, así que una línea por compás a 120 bpm. La banda sonora se ejecuta aproximadamente medio segundo después del último fotograma y se corta allí; termina en una sección
outropara que el corte caiga en su cola. - Resultados de la construcción:
build.tssale con 0 incluso cuando una comprobación falla; los fallos se imprimen como líneasWARNING:con el detalle arriba. Trata cada advertencia como un fallo: lee la salida (filtra las líneas de progresoframe n/total), corrige, reconstruye hasta que no haya ninguna. Los fotogramas fijos se escriben comoout-<nombre>/fNNNN.jpg; léelos.bun $E/pure.ts <nombre>confirma que los fotogramas se renderizan igual solos y después de otros fotogramas. sound.pynecesita numpy y siempre se ejecuta a través deuv run; los otros scripts de Python solo usan la biblioteca estándar.
Películas de lanzamiento y producto: muestra el producto
Una película de palabras grandes sobre un fondo se lee como básica. Las películas de lanzamiento que funcionan mantienen el producto en pantalla en un mundo visual único con profundidad y una cámara que se mueve lentamente, usan texto breve y pequeño, y mantienen las tomas durante 2 a 6 segundos (unos pocos compases), no un corte por pulso. Para una película de lanzamiento o producto NO empieces desde hello:
- Decide primero el aspecto: un mundo de fondo, un color de acento, una familia tipográfica. Dilo al usuario en dos líneas.
- Storyboard de 6 a 8 tomas de cosas reales: la pantalla o salida real del producto, comandos reales, código real, resultados reales. Nunca dibujes una interfaz que el producto no tenga, nunca inventes números y nunca uses el logotipo de otra empresa. Los pies de foto son etiquetas pequeñas (
txt, mono, abajo a la izquierda), no titulares. - Construye primero una toma principal, renderiza fotogramas fijos y compárala con una película de lanzamiento real antes de construir el resto.
La página debe cargar stage.js después de moves.js (las páginas creadas por new.ts lo hacen). Comienza renderFrame con M.frame(); M.reset(X);. Piezas del escenario (todas en MOVES):
| pieza | qué es |
|---|---|
backdrop(ctx, f, { base, lights[], streak }) | casi negro con piscinas lentas de luz: el mundo en el que todo se asienta |
glass(ctx, x, y, w, h, { r, top, bottom, rim, shadow }) | un panel redondeado oscuro con un borde de luz y una sombra suave |
camera(ctx, cam, draw) con cam(a, b, t, ease) | mira un punto de una escena más grande: { x, y, zoom, rot }; comienza macro-cerca y retrocede, o deriva lentamente |
tilt(ctx, draw, { depth, focus, blur, fog, top, height, width, cx, roll }) | dibuja una escena de fotograma completo como un plano inclinado hacia atrás, el borde lejano más pequeño, más oscuro y desenfocado |
tiles(ctx, f, { cells: [{ draw(ctx, t), label, bg, len, hold }], cols, x, y, w, gap }) | una cuadrícula de pantallas pequeñas, cada una reproduciendo un movimiento en vivo (nunca pongas una transición dentro de un mosaico) |
term(ctx, f, { x, y, size, lines: [{ text, at, speed, prompt, mark: "ok" o "run", color }] }) | líneas de terminal que se escriben o aparecen en su fotograma |
code(ctx, { lines[], x, y, size, lead, hot, shown }) | JavaScript con color de sintaxis discreto y una línea iluminada |
wave(ctx, { peaks, x, y, w, h, played, beats[], blocks[] }) | la forma de una banda sonora real con un cabezal de reproducción, marcas de pulso y clips en un carril |
Las piezas del escenario no generan sonido propio: añade señales manualmente a la línea de tiempo, { f, kind, gain } con tipos como type, tick, pop, ding.
Cortar a una canción real
- El usuario proporciona el archivo de música y debe tener el derecho de usarlo; indícalo y pon el crédito que solicite una licencia en la tarjeta final. Nunca descargues una canción de un sitio de streaming.
uv run $E/beats.py <archivo>imprime el tempo y qué tan fuerte es cada compás. Elige un extracto que comience en un compás con un cambio de sección unos compases después, luegouv run $E/beats.py <archivo> --from <segundos> --len <segundos> --out beats.jsonescribebeats.jsonybeats.js.- Carga
beats.jsen la página antes del script de la película. Escribe cada inicio de escena como un número de pulso:const b = (n) => window.BEATS.frames[n]. Corta en los compases (cada 4 pulsos), donde la pista cambia. - En la línea de tiempo establece
music: { file, from },sound: "fx"(los sonidos propios de los movimientos permanecen, en silencio, bajo la música) o"none", unbpmysync: [fotogramas de los cortes reales]. La construcción mezcla la pista a -16 LUFS, desvanece su final y verifica que cada corte tenga un golpe de bajo dentro de un fotograma. No añadas destellos solo para pasar esa verificación.
Movimientos para EditMax (un clip en la línea de tiempo de un editor de video)
Usa esto cuando el usuario está editando en EditMax y quiere un movimiento en el corte: un título animado sobre metraje, una tarjeta de introducción o cierre, un ritmo cinético entre tomas. Necesita dos cosas en la misma sesión del agente: un shell (para renderizar) y las herramientas MCP de EditMax (inspect_timeline, import_media, ...; configuradas desde la pestaña Agents de EditMax). El chat interno de la aplicación de EditMax rechaza comandos de shell, por lo que no puede ejecutar este skill: indícalo y dile al usuario que conecte un agente como Claude Code. Si faltan las herramientas de EditMax o no responden, renderiza el clip de todos modos y dale al usuario la ruta del archivo para arrastrar.
1. Elige la ruta. Prefiere las herramientas propias de EditMax cuando cubran el movimiento, porque el texto permanece editable en la aplicación:
| deseado | uso |
|---|---|
| un título o tercio inferior que se desvanece, desliza, aparece, escribe o limpia | add_text / add_lower_third + set_text_animation |
| una disolución, empuje, deslizamiento, cortinilla o iris entre dos clips | set_transition |
| un clip que se mueve, escala o desvanece | set_keyframe, set_clip_transform, set_clip_fades |
| cualquier otra cosa: golpe, revuelo, solapa dividida, rueda de palabras, tachado, resaltado, dibujo de trayectoria o logotipo, gráficos, contadores, ráfagas, secuencias de múltiples movimientos, una introducción o cierre completo | renderiza un clip con este skill |
2. Lee el proyecto. inspect_timeline: anota el tamaño del fotograma, la velocidad de fotogramas, las pistas y el tiempo donde va el movimiento. Este motor renderiza 1920x1080 a 30 fotogramas por segundo. Un proyecto 16:9 de otro tamaño lo escala limpiamente. Para un proyecto vertical o cuadrado, di claramente que el clip se encuadrará en formato carta o deberá escalarse y recortarse con set_clip_transform, y que un título nativo puede ser mejor.
3. Renderiza.
- Superposición (se coloca sobre el metraje):
bun $E/new.ts <nombre> --overlay, modelado en$E/title.js. La línea de tiempo tienealpha: trueysound: "fx"; la construcción escribeout-<nombre>/<nombre>.mov(ProRes 4444 con un fondo transparente). Reglas: limpia el fotograma (X.clearRect), nunca lo rellenes; singrain,vignette,mesh,parallaxni transiciones de fotograma completo; coloca una sombra suave o una placa debajo del tipo para que se lea sobre cualquier metraje; mantén el tipo dentro del 90% central del fotograma; desvanécelo al final. - Fotograma completo (introducción, cierre, una tarjeta entre tomas):
bun $E/new.ts <nombre>, modelado en$E/hello.js; la construcción escribeout-<nombre>/<nombre>.mp4. - Sonido (establece
sound:en la línea de tiempo): déjalo fuera para la banda sonora completa (una introducción o cierre independiente);"fx"para solo los sonidos que hacen los movimientos (cualquier cosa que se reproduzca sobre metraje, voz o la música del usuario);"none"para silencio. Pregunta cuál quiere el usuario si la edición ya tiene música. - Construye, corrige cada
WARNINGy lee fotogramas fijos antes de continuar.
4. Coloca el archivo en algún lugar que dure. EditMax referencia los archivos importados, no los copia. Copia el archivo terminado fuera de out-<nombre>/ a una carpeta que el usuario conserve (pregunta una vez; sugiere ~/Movies/Motions/) e importa desde allí.
5. Colócalo sin mover el corte del usuario. insert_clip con un tiempo es un desgarro…
…[truncado para traducción]