¿Qué hace Handmade Stage Film?
Noche de Halloween, no hay nada en la tele: una chica con un bob negro se sienta dentro de un sofá de cartón pintado, con un trago rosa en la mano, y hace clic en el control remoto de cartón. La televisión de cartón se enciende con estática, un pequeño fantasma baila en la pantalla, luego se despega y flota. Planea junto a su taza ("Quería un sorbo."), aterriza en la mesa cuadriculada donde un tenedor gigante apuñala un plato de bayas pintadas ("Gardenia. Bayas. Yogur."), y un sorbo después está sentada sobre un enorme sombrero de bruja mientras aparecen estrellas a su alrededor ("...magia completa."). La taza recibe su primer plano con un garabato "Agua Mágica Bonita" y una flecha, y la última tarjeta tiene la marca, la taza, el fantasma, "Edición limitada de Halloween" y dónde está a la venta. Cada fotograma proviene de las siete fotos de campaña propias de la marca.
Se compone de cuatro partes:
scripts/generate.mjsconvierte las fotos fijas en las piezas: keyframes 9:16 (Nano Banana 2 pinta la extensión, los píxeles originales se superponen), clips de Kling, props recortados, etiquetas recolocadas y la marca.scripts/reel.mjscorta los clips en un único reel en el reloj de la película.scripts/sfx.mjssintetiza el sonido: un vals de caja de música original y un sonido en cada evento.scripts/render.mjslo reproduce todo como un espectáculo escénico: movimiento escalonado a 12 fotogramas por segundo, vibración de mano, grano en movimiento, títeres, subtítulos en papel rasgado, etiquetas con flechas, una pantalla de televisión que se enciende y la última tarjeta. Se renderiza localmente con Chrome sin interfaz gráfica y FFmpeg.
Configuración (una vez por máquina)
Ejecuta dentro de la carpeta de esta habilidad:
npm install
Esto instala puppeteer-core y ffmpeg-static. Debes tener Google Chrome instalado, o configurar CHROME_PATH con la ruta a un ejecutable de Chrome/Chromium (npx @puppeteer/browsers install chrome-headless-shell@stable te muestra una).
El metraje necesita una clave API para Nano Banana 2 y Kling 2.5 Turbo; los scripts las llaman a través de una puerta de enlace API de modelos, donde se crea la clave (https://fal.ai/dashboard/keys). El usuario la configura en su propio terminal como FAL_KEY (por ejemplo, en su perfil de shell). Nunca le pidas que pegue la clave en el chat, y nunca la imprimas.
Costo
Uso de modelos, facturado a tu propia clave API: $0.08 por cada foto fija extendida a 9:16 (y cada edit), $0.35 por cada clip de 5 segundos, y una fracción de centavo por cada recorte (precios de octubre de 2026). Una película de siete fotos fijas con cinco clips cuesta aproximadamente $2.30. El reel, los títeres, los subtítulos, el sonido y el MP4 final se hacen gratis en tu máquina. Informa al usuario del costo antes de generar.
Qué lo hace funcionar
- Sus propias fotos, intactas donde importa. La extensión y cada clip comienzan a partir de las fotos fijas reales de la marca;
extendvuelve a colocar los píxeles originales sobre la versión de la IA, yrelabelcoloca una etiqueta nítida sobre cualquier fotograma donde el clip la haya borroso. - Una historia, no una presentación de diapositivas. Planea una pequeña trama a través de las tomas, con algo que viaje entre ellas (aquí un títere fantasma que escapa de un TV). Los subtítulos son pequeños latidos de la historia, no eslóganes.
- Torpe a propósito. Todo se mueve en pasos de 1/12 s con un temblor diminuto, los títeres se tambalean sobre sus puntos clave, los subtítulos aparecen en tres pasos y las flechas se dibujan en tres trazos. No lo suavices.
- Mitad producto, mitad marca. El producto aparece en la mayoría de las tomas y recibe un primer plano con una etiqueta; la última tarjeta es la marca, el producto, el nombre y dónde comprarlo.
Flujo de trabajo
- Recolecta de 4 a 8 fotos fijas de una sesión (3:4, 4:5 o 9:16), el nombre del producto, una línea (sabores, materiales), una nota ("Edición limitada de Halloween") y la CTA. Pregunta si las fotos llevan una marca o insignia impresa en la parte inferior (se pinta fuera de los fotogramas y se recorta para la última tarjeta).
- Guión (muéstralo antes de generar): el orden de las tomas, la trama, lo que se mueve en cada clip (una o dos acciones pequeñas, el producto se mantiene quieto), qué props recortar como títeres y qué hacen, los subtítulos y las etiquetas, y el costo. Mantenlo en 15–20 s.
- Keyframes:
node scripts/generate.mjs extend --in still.jpg --out keys/<shot>.png --cover <rows>para cada foto fija que no sea 9:16 (--cover= la altura en píxeles de la banda de marca impresa en la parte inferior del original, si la hay). Mira cada una a tamaño completo: si el modelo dejó un objeto duplicado o reflejado en las nuevas bandas, cúbrelo con textura plana de la misma foto. - Prueba: un clip, la toma con el gancho de la trama:
node scripts/generate.mjs clip --start keys/<shot>.png --prompt "…" --out clips/<shot>.mp4. El prompt nombra solo lo que se mueve ("ella presiona un botón en el control remoto con su pulgar; la taza permanece vertical e inmóvil"); un sufijo fijo mantiene la cámara bloqueada y el decorado, la cara y el producto sin cambios. Muéstralo, luego haz el resto. - Clips para las otras tomas en movimiento. Mira cada uno fotograma a fotograma: anota dónde aterriza la acción (para colocar el sonido y los títeres ahí) y dónde se emborrona el texto pequeño del producto. Arregla las etiquetas con
relabel --clip … --key keys/<shot>.png --box x,y,w,h(el cuadro de la etiqueta en el keyframe), o usa solo los fotogramas anteriores a que el producto se mueva. No es necesario un clip para cada toma: un primer plano fijo con un lento zoom in (still,box,boxTo) suele ser mejor para el producto. - Props:
cutout --in keys/<shot>.png --box x,y,w,h --out props/<name>.pngpara cada títere (un personaje, estrellas, una flor, una baya, el producto en una mano);editpara un plato con algo eliminado (una pantalla de TV sin su fantasma). La marca:wordmark --in still.jpg --box x,y,w,h --out props/wordmark.png. El fondo de la última tarjeta es un recorte de una pared pintada de uno de los keyframes. - Construye
effect.jsona partir deexamples/example.json(ver Tomás y la última tarjeta), luegonode scripts/reel.mjs effect.json --out reel.mp4y configura"reel": "reel.mp4". - Sonido:
node scripts/sfx.mjs effect.json --out stage.m4a, luego"music": "stage.m4a". Ejecútalo de nuevo después de cualquier cambio en la sincronización.--no-bed --music track.mp3cambia el vals por una pista que el usuario tenga licencia para usar. - Valida:
node scripts/validate.mjs effect.json— corrige todos los errores y lee las advertencias al usuario. - Borrador:
node scripts/render.mjs effect.json --quality draft --width 540 --out draft.mp4, y revisa un fotograma de cada toma: ningún subtítulo, etiqueta o títere sobre una cara o el producto, todo fuera del 14% derecho donde se encuentran los botones de la plataforma. - Final:
node scripts/render.mjs effect.json --out film.mp4 --poster poster.png. Para Xiaohongshu, recorta una versión 3:4:ffmpeg -i film.mp4 -vf crop=720:960:0:160 -c:a copy film-3x4.mp4(con el ancho predeterminado de 720; verifica que nada importante esté en los 160 píxeles superior o inferior).
Tomás
Todas las posiciones están en píxeles de fotograma 1080 × 1920. Cada toma es una de:
- Una toma de clip:
clip(el archivo que reel.mjs corta),fromyto(segundos en el clip),speed(0.5 = media velocidad, que con la reproducción de 12 pasos usa cada fotograma de un tramo bueno corto),hold(segundos que el primer fotograma se mantiene antes de moverse). - Una toma fija:
still(una imagen),len,boxyboxTo([x, y, width]ventanas 9:16 en los píxeles de la imagen; la vista se mueve de una a otra).
Cada toma puede tener:
captions:{ "text", "at", "out", "x", "y", "rot", "size" }— recortes de papel rasgado con palabras escritas a mano;\nrompe una línea.tags:{ "text", "at", "x", "y", "to": [x, y], "ink": "dark" | "light", "rot", "size" }— palabras escritas a mano con una flecha que se dibuja haciato.puppets:{ "image", "path": [[t, x, y, width, rotation], …], "sound", "exit", "flip", "wobble" }— un recorte movido entre sus puntos clave (t en segundos dentro de la toma, x e y su centro). Un punto clave con ancho 0 y luego uno completo un paso después es un pop. Mantiene su último punto clave a menos que"exit": true.soundsuena en su primer punto clave.screen:{ "rect": [x, y, w, h], "off", "static", "plate", "plateAt" }— una pantalla de TV o teléfono que está negra hastaoff, estática hastastatic, y muestraplate(el mismo keyframe con la pantalla vacía) desdeplateAt, para que un títere pueda salir de ella.sounds:[[t, "kind"], …]—click,tvon,static,ghost,boing,tap,pop,whoosh,land,stab,sip,sparkle,chime,paper,scribble,cut. Los cortes, subtítulos, etiquetas, pantallas y títeres con unsoundobtienen el suyo automáticamente.
La última tarjeta
end: len, bg (una pared pintada 9:16), wordmark (wordmarkY, wordmarkWidth), name (nameY, nameSize), line (lineY), note en un recorte de papel (noteX, noteY), cta en una píldora (ctaY), y puppets como los de una toma (el producto en una mano deslizándose desde el borde, el personaje, las estrellas). Cada uno tiene un tiempo …At.
Campos
| Campo | Obligatorio | Notas |
|---|---|---|
reel | sí | ruta a un archivo de video, relativa al archivo JSON — cada toma de clip cortada en un único reel 9:16 sin sonido por scripts/reel.mjs (reel.mp4) |
shots | sí | lista de 2–10 entradas: clip, from, to, speed, hold, still, len — la película en orden: tomas de clip (un clip de IA de una de tus fotos fijas) y tomas fijas (una foto fija con un movimiento lento); ver SKILL.md, Tomás |
end.bg | no | ruta a una imagen, relativa al archivo JSON — el fondo de la última tarjeta: una pared pintada o telón de fondo de tus fotos, 9:16 |
end.wordmark | no | ruta a una imagen, relativa al archivo JSON — tu marca sobre fondo transparente (scripts/generate.mjs wordmark) |
end.name | no | texto, menos de 4 palabras — el nombre del producto, escrito a mano en la última tarjeta |
end.line | no | texto, menos de 6 palabras — una línea corta debajo del nombre, por ejemplo los sabores |
end.note | no | texto, menos de 4 palabras — en un recorte de papel rasgado, por ejemplo "Edición limitada de Halloween" |
end.cta | no | texto, menos de 12 palabras |
music | no | ruta a un archivo de audio, relativa al archivo JSON — la banda sonora de scripts/sfx.mjs (stage.m4a) |
palette | no | colores como #RRGGBB; claves: ink (subtítulos y etiquetas oscuras), paper (los recortes de papel y etiquetas claras) y accent (la píldora CTA) |
Consejos
- Escribe los prompts de los clips ajustados: una o dos acciones pequeñas, una cara inexpresiva, "la taza permanece vertical e inmóvil, la etiqueta mirando a la cámara". Los gestos grandes emborronan las caras y las etiquetas.
- Kling elige su propio ritmo: mira dónde aterriza la acción y coloca el clic, el corte y los títeres en ese fotograma, no en un número redondo.
- Dale a la historia un viajero: un títere recortado de una foto que aparece en las siguientes tomas une siete fotos fijas en una película.
- Recorta los títeres de las propias fotos, no de arte nuevo: la misma pintura, papel y luz los hacen pertenecer.
- Mantén los subtítulos en tres o cuatro palabras; las imágenes cuentan la mayor parte de la historia.
Salida
H.264 MP4, 1080 × 1920, 30 fps (la imagen se mueve en 12 pasos por segundo), 15–20 segundos con el sonido, más un fotograma de póster (PNG).