¿Qué hace thought-to-motion?
La idea es el producto; todo lo demás es una capa. Esta habilidad toma una grabación (un video de persona hablando o solo una nota de voz), encuentra los momentos clave en lo que se dijo y diseña movimiento a su alrededor: subtítulos en el idioma hablado, tarjetas sobre las palabras clave, una revelación en el giro argumental, una pregunta de cierre.
Tres modos:
- talking-head: el metraje se mantiene; las tarjetas, subtítulos y zooms se colocan sobre él.
- voice: no se necesita metraje (un archivo de audio, o el audio extraído de un video); un lienzo de marca a pantalla completa con escenas, subtítulos cinéticos y una forma de onda en vivo.
- text: sin grabación; el texto plano se convierte en subtítulos cinéticos temporizados y escenas sobre el lienzo de marca, generalmente con música de fondo.
Dos formatos: 9:16 (predeterminado; Reels, TikTok, Shorts) y 16:9 ("aspect": "16:9"; YouTube, LinkedIn, web). El mismo plan se renderiza en ambos: en 16:9 el diseño 9:16 se coloca sobre un escenario ancho (talking-head: el orador enmarcado a la derecha, diseño de movimiento a la izquierda).
Todo se ejecuta en esta máquina. Ningún metraje, audio o texto se sube a ningún lado.
0. Configuración (una vez por máquina)
Desde la carpeta de la habilidad:
bash scripts/setup.sh # venv + paquetes fijados + engine/node_modules desde el lockfile + kit de sonido local
Requiere ffmpeg, Node 18+, Python 3.10+. La primera transcripción descarga modelos de huggingface.co (y su CDN de archivos *.hf.co) en .cache/hf. Si el acceso a la red está permitido por lista blanca, esos hosts deben estar permitidos. Si ya existe un headless shell de Chrome/Chromium, establece BROWSER_EXECUTABLE=/ruta/a/headless_shell; de lo contrario, Remotion descarga el suyo propio en engine/node_modules en el primer renderizado. Ejecuta los scripts de Python con .venv/bin/python.
Tu marca (una vez)
Copia brand.example.json a brand.json y complétalo: logotipo (tu nombre o marca), etiqueta de serie, usuario, línea de marca, tema (warm, cinematic o tech, consulta engine/src/lib/theme.js; añade tus propios colores y fuentes allí). Los campos brand, theme y bumpers de cada plan provienen de este archivo. Sin brand.json -> sin encabezado, sin intro ni outro.
1. Admisión (pregunta solo lo que no puedas deducir)
- La fuente. Video con una persona hablando -> talking-head; solo audio, o un video donde solo importa la voz -> voice (la preparación extrae el audio); texto plano -> modo texto.
- Formato: 9:16, 16:9, o ambos (dos renderizados del mismo plan con diferente
aspect). - Música: activada o desactivada. Solo las pistas generadas integradas están incrustadas (calma / cálida / pulso, sin derechos de autor, nada que pueda ser reclamado). Si el usuario quiere una canción específica, deja la música desactivada; él la añadirá en la aplicación.
- Idioma(s) de los subtítulos: predeterminado = el idioma hablado. Dos opciones para un segundo idioma: bilingüe (un video, el idioma hablado arriba y una traducción más pequeña debajo:
lang2+text2) o un renderizado separado. - Tema (
warmcálido /cinematicoscuro /techazul marino, consulta engine/src/lib/theme.js), logotipo de marca + etiqueta de serie (o ninguno), y la pregunta de cierre/CTA si la tienen. - Modo voz: cualquier foto propia que quieran usar (colócala en
<job>/public/).
2. Preparación
Modo texto (sin grabación):
.venv/bin/python scripts/prep_text.py <texto.txt | "texto en línea"> <job> [--lang he|en] [--wpm 150] [--aspect 9:16|16:9]
Divide el texto en líneas de tamaño de subtítulo en los límites de las frases, las temporiza a un ritmo de lectura cómodo y escribe plan.draft.json (modo "text", música calmada, movimiento calmado). No reescribas las palabras del usuario; solo divídelas y temporízalas. Luego diseña las escenas (sección 4) como para el modo voz. Salta a la sección 4.
Video o audio:
.venv/bin/python scripts/prep.py <media> <job> [--mode voice] [--lang he|en|auto] [--no-cut]
Normaliza a 1080x1920 30fps (o audio), corta pausas > 0.45 s, limpia el habla (eliminación de ruido DeepFilterNet3 + ecualización, de-esser, compresión, -14 LUFS; --clean gentle por defecto, full para clips exteriores muy ruidosos, off para grabaciones de estudio), copia el kit de sonido y transcribe la línea de tiempo CORTADA con marcas de tiempo de palabras -> <job>/transcript.json. El hebreo usa el modelo ivrit.ai automáticamente. El script imprime las palabras de baja confianza: verifica esas primero.
Opcional: reemplazar el fondo (talking-head)
Mejores resultados: filma un plano limpio. Antes de sentarte, filma 3-5 segundos de la habitación vacía, luego entra y comienza. Bloquea el teléfono en un trípode y bloquea la exposición y el enfoque (en iPhone: mantén presionada la pantalla hasta que aparezca "AE/AF LOCK"), para que la habitación no se aclare u oscurezca cuando entres. No hagas zoom ni muevas la cámara. prep.py detecta automáticamente la apertura vacía, la guarda como la referencia de fondo exacta (work/clean_plate.png) y la corta (y la entrada) del video. Sin un plano limpio, el motor tiene que adivinar la habitación detrás de la cabeza y el torso. La habitación vacía también puede ser un clip separado filmado desde la misma posición de teléfono bloqueado (antes o después de la charla):
.venv/bin/python scripts/plate_from_clip.py <job> <clip_habitacion_vacia>
Usa solo la apertura vacía e inmóvil de ese clip (se detiene cuando alguien entra), la alinea con el fondo propio de la charla (ECC, afín) y empareja la exposición y el color por canal. Verifica work/plate_check.jpg (charla | placa | diferencia) y los números de coincidencia impresos antes de procesar.
.venv/bin/python scripts/background.py <job> <plate.jpg> --draft [--reveal SEGUNDOS] # borrador: media resolución, ~4x más rápido
.venv/bin/python scripts/background.py <job> <plate.jpg> [--reveal SEGUNDOS] # final: resolución completa
# opciones: [--match 0.65] [--wrap 0.35] [--preview SEGUNDOS]
Máscara de retrato local (MODNet) combinada con una máscara de diferencia de habitación limpia: con una cámara estática, la mediana del clip es la habitación vacía, y cualquier cosa que difiera de ella (manos rápidas, cabello, brazos) se mantiene como la persona. La habitación vacía se aprende solo de fotogramas donde cada punto está realmente vacío (la persona se enmascara primero), por lo que la pared real junto a la cabeza nunca se confunde con la persona. Los bordes semitransparentes (cabello) se descontaminan contra esa habitación vacía, por lo que no se muestra un borde de la pared antigua. Todo debajo de la línea del escritorio (--desk auto: aproximadamente 1.2 alturas de rostro debajo de la barbilla; o una fracción como 0.9; off para tomas de pie) está delante de la persona: el escritorio y los antebrazos apoyados, y se mantiene tal como se filmó, reiluminado para que coincida. Usa --moving-camera para metraje tomado a mano (solo modelo). Una pared lisa o verde detrás del orador da el resultado más limpio. Luego, la persona se reilumina al brillo y color de la placa, la luz de la placa se envuelve en sus bordes, y una viñeta + grano se aplica sobre ambos para que compartan una cámara. --reveal mantiene la habitación real hasta ese momento y se desvanece a la nueva (genial cuando el orador lo pide en cámara). Siempre ejecuta --preview en 1-2 fotogramas y muéstraselo al usuario antes de procesar todo el clip. Después del procesamiento, ejecuta scripts/bg_qa.py <job>: compara el video original y el nuevo cada dos fotogramas e informa dónde se perdió a la persona (rojo) o se filtró la habitación antigua (amarillo), por segundo, con una hoja de los peores fotogramas (work/bg_qa_sheet.jpg). Corrige o marca cada segundo fallido antes de que el usuario vea el video. Falso positivo conocido: un objeto que se movió entre la placa limpia y la toma (una silla, una puerta) se cuenta como "persona"; mira la hoja antes de actuar sobre esos segundos. La placa debe coincidir con la escena: interior/exterior, hora del día, luz cálida/fría, altura de la cámara. Escribe public/source_bg.mp4.
3. Lee la transcripción, corrígela, escribe los subtítulos
- Corrige errores de reconocimiento obvios (nombres, jerga, palabras en idiomas mixtos). Enumera cada corrección para el usuario; nunca cambies el significado silenciosamente. Si no estás seguro de lo que se dijo, pregunta.
- Elimina los rellenos ("um", "אה"). Subtítulos: una idea por señal, <= ~42 caracteres, como máximo 2 líneas, velocidad legible (<= ~20 caracteres/s), tiempos de señal de las marcas de tiempo de las palabras.
- Segundo idioma: traduce las señales tú mismo, manteniendo la voz del orador y los mismos tiempos (como
text2para subtítulos bilingües; mantén la traducción lo suficientemente corta para una línea). - En subtítulos RTL, mantén el inglés entre comillas como una sola ejecución ("כי אני זוכר Is that cheese") - el motor lo aísla.
4. Plan de movimiento -> PUERTA 1 (aprobación)
Lee primero references/motion-rules.md. Luego ejecuta los mensajes de etapa en references/prompts/ en orden, como tus propios pasos de trabajo (producen material para las puertas; nunca las reemplazan):
- Editor de historia y gancho (1-story-hook-editor.md): candidatos a gancho, la pregunta abierta y dónde se cierra, el arco, una lista de cortes. Solo tramos literales. Modo texto: usa 6-text-to-video-writer.md en su lugar.
- Diseñador de subtítulos (3-caption-designer.md): fragmentación, velocidad de lectura, palabras de énfasis, RTL.
- Director de movimiento (2-motion-director.md): un elemento por ritmo, aterrizando en su palabra, un punto focal a la vez, transformar en lugar de reemplazar.
- Diseñador de sonido (4-sound-designer.md): sonidos dispersos en los aterrizajes, estilo musical, un ritmo de silencio.
Encuentra los ritmos (consulta references/design-rules.md): gancho, referencias nombradas (programas, personas, lugares, productos), el concepto clave, contrastes o elecciones, listas, números que el orador realmente dijo, el giro/revelación, la pregunta/CTA. Asigna cada ritmo a un elemento (references/plan-schema.md). Presenta una tabla al usuario:
| # | tiempo | lo que se dice | elemento | por qué | enseña | verificar |
|---|
También propón la frase clave del video (la idea principal del video, una línea que el orador realmente dice, de aproximadamente 2-3 s) para el abridor. También enumera: correcciones de subtítulos, cualquier hecho en pantalla que hayas añadido (por ejemplo, los años de un programa) con una fuente, tema, sonidos. Detente y espera la aprobación. Nada se renderiza antes de esto.
5. Escribe y valida el plan
Escribe <job>/plan.json (esquema en references/plan-schema.md, ejemplo en examples/). Luego:
.venv/bin/python scripts/validate_plan.py <job>/plan.json
Corrige cada ERROR. Lee las ADVERTENCIAS y corrige lo que importa. Además del esquema, verifica las reglas de movimiento: contenido a los 0.5 s, sin espacio de más de 6 s sin un evento visual, como máximo 2 gráficos a la vez, velocidad de lectura (máx. 20 caracteres/s), duración de subtítulos, límites de zoom, seguridad de fugas de luz, espaciado de efectos de sonido, ritmos de silencio. Rompe una regla solo a propósito y di por qué en la Puerta 2.
El validador también aplica el área segura (x 60-900, y 220-1420: las partes del marco que ninguna interfaz de aplicación cubre) y mantiene cada tarjeta y panel fuera de la cara del presentador (rastreado en faces.json). Añade --fix para mover tarjetas automáticamente. Consulta references/safe-zones.md.
Cámaras virtuales (talking-head, automáticas)
.venv/bin/python scripts/shots.py <job> [--plan plan.json]
Hace que una toma fija parezca una edición multicámara: cámara A (general, el encuadre original), B (medio, ~1.2x) y C (primer plano, ~1.4x; hasta 1.7x en 16:9), recortadas del mismo fotograma y ancladas en la cara rastreada. Los cambios de cámara aterrizan en los cortes de pausa que hizo prep (ocultando el salto), de lo contrario en los finales de oración; las tomas duran 3-8 s; nunca la misma cámara dos veces; general mientras las tarjetas necesitan espacio, primer plano en zooms y revelaciones; cada toma con zoom se acerca ~1.5% (calmado). Cada cámara se prueba contra las tarjetas y subtítulos para que la cara permanezca despejada. Los selfies en primer plano (cara más alta que un tercio del marco) no reciben cámaras virtuales en 9:16 - no hay espacio para acercarse más. Sin ángulos generados por IA: el metraje se mantiene real y local. Añade una columna "cámara" (A/B/C) a la tabla de la Puerta 1.
6. Fotogramas fijos -> PUERTA 2 (aprobación)
.venv/bin/python scripts/render.py stills <job> # momentos clave -> <job>/stills/sheet.jpg
La hoja muestra guías: zonas no seguras en rojo, la cara rastreada en verde. Mírala tú mismo primero: todo dentro del área segura, nada cubriendo la cara/boca, orden hebreo/inglés correcto, fuentes cargadas (sin sustituto tipo Times), sin superposiciones. Luego ejecuta el revisor independiente (references/prompts/5-reviewer.md) como un agente separado que no ha visto tu razonamiento: dale solo los fotogramas fijos, plan.json y transcripción. Corrige sus bloqueadores y muestra su veredicto al usuario con los fotogramas fijos. Detente y espera la aprobación. El revisor nunca aprueba en nombre del usuario.
7. Renderiza, verifica, entrega
.venv/bin/python scripts/render.py video <job> --name <NOMBRE> # Borrador 540x960 (predeterminado, rápido) para revisión
.venv/bin/python scripts/render.py video <job> --name <NOMBRE> --final # Completo 1080x1920, máxima calidad
Cada renderizado de revisión es un borrador de media resolución (540x960). Renderiza --final solo para la versión final aprobada. Un renderizado por idioma de subtítulos (cambia lang + captions en el plan). Después de renderizar, verifica: duración coincidente, audio presente, algunos fotogramas se ven bien. Entrega los MP4s (1080x1920 o 1920x1080 H.264 perfil alto, faststart; válido para Reels, TikTok, Shorts, YouTube, LinkedIn) y los archivos .srt de subtítulos escritos junto a ellos (uno por idioma de subtítulos). Con "music" en el plan, la pista se mezcla en el momento del renderizado y se atenúa automáticamente debajo de la voz. El renderizado toma unos minutos por minuto de video en 2 CPUs.
--final también escribe <NOMBRE>_social.mp4: la misma imagen a aproximadamente 10 Mbps (lo suficientemente pequeño para APIs de programación, por ejemplo, el límite de ~300 MB de Instagram) con el audio re-medido y normalizado a -14 LUFS / -1 dBTP en el archivo final. Entrega esa copia para publicar y mantén el master de alta tasa de bits como archivo. Mide el archivo entregado (ffmpeg -i <archivo> -af ebur128=peak=true -f null -) e informa su sonoridad.
El video termina aproximadamente 0.8 s después de la última palabra hablada (prep mantiene esa cola), por lo que la última palabra nunca se corta. Con "bumpers" en el plan, render.py une abridor -> intro -> video -> outro (scripts/bumpers.py), desplaza los archivos .srt por la duración del abridor + intro, y se detiene si el sonido y la imagen están desincronizados (consulta Abridor, intro y outro).
Reglas (no negociables)
- Nunca inventes hechos, números, citas o historias. Los hechos en pantalla más allá de la transcripción (fechas, títulos) deben ser verificados y mostrados al usuario en la Puerta 1.
- No dibujes personajes reconocibles, logotipos o material protegido por derechos de autor; usa tipografía y los iconos genéricos integrados. Excepción: el elemento
logopuede mostrar el logotipo de la marca reemplazando el icono genérico de marca (proporcionado en brand.json o en<job>/public/logo.png). - Los subtítulos nunca superan ~20 caracteres/s (velocidad de lectura cómoda) y permanecen en el área segura. La cara nunca se superpone con texto o gráficos.
- El video nunca se renderiza sin aprobación del usuario en ambas puertas. La Puerta 1 es el plan de movimiento (tabla + frases clave). La Puerta 2 son los fotogramas fijos con veredicto del revisor.
- Los estilos de cámara virtual y gráficos deben coincidir con el tema de la marca.
- La música libre de derechos solo se usa si está habilitada en el plan.
- Si el usuario solicita cambios en el plan después de la Puerta 2, vuelve a la Puerta 1 con una tabla actualizada.