Explicador animado (motion graphics 3D por código)
Todo es código: cada parte del video es un mundo 3D (escena/partes/<parte>.js) que es función pura del tiempo,
motor.js decide cuál se ve y dibuja los subtítulos karaoke, y render.mjs le pide a Chrome sin ventana cada cuadro.
Nada se edita a mano: si algo sale mal se corrige el código y se vuelve a renderizar.
Dónde está todo: HOME3D = $EXPLICADORES_3D o, si no existe, ~/.explicadores-3d (lo crea el instalador). Ahí
viven biblioteca/ (modelos 3D, vendor/ con three.js, fuentes) y una copia de esta skill. SKILL_DIR = la carpeta
de este SKILL.md.
| Archivo | Para qué |
|---|---|
nuevo.py <carpeta> | Crea el proyecto con la plantilla (motor, estilo, lib2d, three.js, fuente Fredoka, scripts de voz/tiempos/audio, una parte de ejemplo, CONTRATO.md en blanco y DECISIONES.md) |
render.mjs | --hoja, --probe a,b,c, --rango a,b, --mitad, --sub N, --rapido, --sin-gpu |
entregar.py <proyecto> <NOMBRE> --ronda N [--escritorio] | Video + audio, versión de celular < 29 MB, hojas de revisión cada 0.5 s y datos.txt (duración, LUFS, pico) |
revisar_hoja.py <proyecto> [--tramo a,b | --ronda N | --cuadros …] | Revisión automática en ~5 s y sin tokens: texto doble en fundidos, halos, gemelos (parecen ojos), blancos quemados, manchas, fuera de zona, saltos de brillo |
ENCARGO_CONSTRUCTOR.md | Plantilla del encargo corto de constructor (una parte por agente) |
ENCARGO_REVISORES.md | Revisores A (retención), B (datos), D (imagen) sobre la hoja y el video |
Requisitos: Node 18+, npm install playwright-core en el proyecto, Chrome (o CHROME_PATH, o
npx playwright install chromium), Python 3.10+ con numpy y faster-whisper, y ffmpeg.
Reglas de la casa
- Cero datos inventados: cada cifra con su fuente en el CONTRATO antes de construir.
- Sin mascotas ni objetos con ojos, y sin formas que se lean como ojos (ver "Errores típicos").
- Estilo por defecto: un objeto limpio sobre fondo oscuro con halo cálido, cámara macro siempre en movimiento suave, palabra clave en dorado, subtítulos karaoke de toda la narración, loop al primer cuadro.
- Ritmo: 3.0-3.2 palabras/s, un cambio de encuadre cada ~3 s, nada quieto más de 5 s, gancho antes de 4.5 s.
- Duración: 15-30 s si es suelto; más de 60 s solo si es parte de una serie con nombre y número.
El flujo
python SKILL_DIR/nuevo.py <tema>-AAAA-MM(crea la carpeta en el directorio actual). Abre y prueba que se vea:npx http-server escena -p 8080(opython -m http.serverdentro deescena/) y entra alocalhost:8080.- Guion con la skill
guion: investigación con fuentes, MAPA, Ronda 0 (A ≥ 9, C ≥ 9, máx. 2 rondas) y paradaGUION LISTO. No sigas sin el OK del usuario. - Voz (gasta créditos o tiempo de CPU, por eso va después del OK):
- ElevenLabs:
export ELEVENLABS_API_KEY=...(la pone el usuario; nunca la escribas tú en un archivo) ypython scripts/voz_eleven.py [--voz <id>]→voz/tomas_eleven/. Plan gratis = sin uso comercial y con crédito. - Gratis y local:
pip install kokoro soundfile(+espeak-ngdel sistema) ypython scripts/voz_local.py [--voz em_alex]→voz/tomas_local/. python scripts/verificar_voz.py(Whisper large-v3, una vez y en segundo plano) marca palabras mal dichas.python scripts/tiempos.py --tomas tomas_eleven(otomas_local) →voz/voz_completa.wav,escena/beats.js(segundo de cada palabra clave, segúnvoz/claves.json) yescena/palabras.js(karaoke).
- ElevenLabs:
- CONTRATO completo (
escena/CONTRATO.md): partes con sus tramosB.*, qué se ve en cada clave, letreros exactos, datos con fuente, zona segura, ritmo. Es lo único que leen los constructores: lo que no esté ahí lo adivinan. Cambia enmotor.jselPLAN(orden de partes y transiciones) yCLAVE(palabras doradas del karaoke). - Modelos 3D: antes de modelar, busca en la biblioteca (skill
biblioteca,HOME3D/biblioteca/CATALOGO.md) y copia lo que sirva aescena/modelos/. Lo que falte lo hace un solo agente (modelo compartido, esfuerzomedium, tope 150 k) y avisa conmodelos/LISTO_<objeto>.txt. - Constructores, con
ENCARGO_CONSTRUCTOR.md:- una parte por agente (una escena de ≤ 15 s), en segundo plano, máximo 3 a la vez;
- encargo corto: solo su tramo, sus modelos con la API en una línea y las reglas; el constructor no lee el motor ni el CONTRATO completo;
- con el bloque "Errores típicos" y el "Tope del constructor" pegados tal cual;
- un constructor fresco por ronda de arreglos, nunca seguir platicando con uno que ya pasó de 40 turnos;
- nunca dejarlos "esperando" a otro agente: primero el modelo compartido, luego las partes.
- Hoja antes del render:
node render.mjs --hoja(un cuadro por beat: el guion visual) ypython SKILL_DIR/revisar_hoja.py .. ROJO = arreglar con un agente frescolowantes de seguir. Luego revisores A, B y D sobre la hoja (ENCARGO_REVISORES.md, Ronda 1), con la salida derevisar_hoja.pypegada en el encargo de D. python scripts/audio.py→audio/mezcla.wava -14 LUFS (música y efectos sintetizados; editaEFECTOSyCAMBIOSarriba del archivo).- Render para iterar:
node render.mjs --rapido --sub 1. Despuéspython SKILL_DIR/entregar.py . NOMBRE --ronda 1ypython SKILL_DIR/revisar_hoja.py . --ronda 1antes de mandar a A y D (1 ronda sobre video). - Render final, una sola vez:
node render.mjs --sub 6. Nunca dos renders a la vez. python SKILL_DIR/entregar.py . NOMBRE --ronda N --escritorioy entrega la versión de celular.- Aportar a la biblioteca (activado por defecto): sigue "Aportar modelos nuevos" de la skill
biblioteca(nuevos.py→ preparar →¿Los subo a la biblioteca? (S/n)). Si el usuario ya dijo que no en este proyecto, no preguntes.
Esfuerzo y modelo por rol
Ver la tabla completa por harness en AGENTS.md del repo. Resumen (nombres de Claude Code; en otros harness usa el
equivalente "rápido/medio/profundo"):
| Rol | Modelo | Esfuerzo |
|---|---|---|
| Sesión que coordina el video | el que uses | medium |
| Investigador de datos y revisor B | medio (Sonnet) | high |
| Modelo 3D compartido | medio | medium, tope 150 k |
| Constructores de escenas | medio | medium, tope 40 turnos / 120 k |
| Revisores A, C, D | medio | medium (los modelos chicos tipo Haiku contradicen reglas: no) |
| Arreglos chicos de una escena | medio | low |
max | nunca | tarda muchísimo y piensa de más |
Presupuesto de tokens y sesiones
El costo es turnos × largo de la conversación, no lo que se escribe. Reglas:
- Una sesión fresca por video. Relevo a los 150 k de contexto: deja
AVANCE.md(qué quedó, qué sigue, decisiones) y sigue en una sesión nueva que lo lea. - Avisos solo en los hitos:
GUION LISTO,VIDEO RÁPIDO LISTO,TERMINADOo cuando estés bloqueado. Lo demás se anota enTIEMPOS.mdsin avisar. - Nada de capturas ni imágenes en la sesión principal: las ve un subagente que contesta en texto.
- Revisores con presupuesto: Ronda 0 sobre el MAPA máx. 2 rondas; Ronda 1 sobre la hoja 1; sobre el video 1 ronda solo con A y D. Segunda ronda solo del área que quedó debajo de 8.5.
Tope del constructor (va pegado, tal cual, al final de cada encargo; para el modelo compartido, 150 k):
Presupuesto: máximo 40 turnos y 120 k tokens de contexto. Cuando llegues a cualquiera de los dos, escribe
AVANCE.mden tu carpeta de trabajo (qué quedó, qué falta, qué probaste y falló) y termina con un resumen de 5 líneas; otro agente seguirá desde ahí. No leas el motor, el CONTRATO completo ni partes ajenas: lo que necesitas está en este encargo. Nunca leas un archivo completo de más de 300 líneas: usa rangos o grep. Las capturas, solo connode render.mjs --probe <seg> --mitady máximo 1 por cada 5 turnos. Antes de pedir una captura, correrevisar_hoja.py(no gasta tokens). No esperes a nadie: si falta algo, anótalo enAVANCE.mdy termina.
Errores típicos (van pegados, tal cual, en cada encargo de constructor; (auto) = lo atrapa revisar_hoja.py):
- Texto doble en los fundidos (auto): saca la pastilla vieja completa antes de que entre la nueva (salida < 0.15 s) o usa corte seco; nunca dos letreros a la vez en la misma zona.
- Halos y auras (auto): orbe, disco o foco con brillo suave alrededor se lee como ojo o sol. Emisivo plano, sin sprite radial; si algo brilla, que sea alargado y sin aro.
- Gemelos (auto): dos ventanas, focos u orbes del mismo tamaño y brillo juntos se leen como ojos. Tamaños distintos, a distinta altura, o uno solo.
- Blancos quemados (auto, aviso): nada en blanco puro con bloom; color de la paleta, mate.
- Manchas y puntos sueltos (auto, aviso): partículas solas, sombras detrás de pastillas, bordes que se quedan.
- Fuera de zona (auto): nada en y < 215 ni abajo de y = 1450 (karaoke); nada cruza una pastilla.
- Saltos de brillo (auto con
--ronda): luz media 60-90, sin saltos > 25 entre tomas.- Duplicados: el mismo texto en letrero y karaoke a la vez; un solo letrero por idea.
- Caras y manos: personas de espaldas o 3/4 trasero; la mano entra de lado o de dorso.
- Letreros chicos: todo >= 42 px (pastillas >= 52 px).
Render: dónde se va el tiempo (medido en una laptop de 4 núcleos con GPU integrada)
| Paso | Por cuadro |
|---|---|
| Sacar el cuadro de Chrome (JPEG, GPU por ANGLE) | ~0.07 s |
x264 slow CRF 16 (entrega) | +0.11 s |
--rapido (Quick Sync en Windows/Intel; x264 veryfast en otros; CODEC_RAPIDO para elegir) | +0.02-0.05 s |
Desenfoque --sub 6 | +0.09 s |
Sin GPU (--sin-gpu) es ~40 % más lento. Nada de transmission en materiales y cada parte < 80 ms por cuadro.
Lecciones que cuestan una vuelta si se olvidan
update(t)es función pura de t: el render pide cuadros sueltos y con subcuadros. Nada deMath.random(usaE.azar(semilla)) ni estado entre cuadros. Protege t < 0.- Movimientos de cámara de ≥ 0.6 s con inOutCubic: con
--sub 6los latigazos salen como fantasmas. - Si una prueba sale con pedazos de otro cuadro, otro proceso estaba renderizando a la vez: repítela.
- El 404 de "Failed to load resource" al final del render es normal; los errores de verdad son
pageerror. - Con
scene.environment, three r186 usascene.environmentIntensity, noenvMapIntensitydel material. - Partículas de la biblioteca (three.quarks) son deterministas por tiempo: no muevas el grupo, usa su
pos. - Panel de ajustes con el mouse:
?ajustes=1en la URL (verbiblioteca/base/LEEME.md); el render nunca lo carga. - Material real (fotos, metraje de archivo): Wikimedia Commons e Internet Archive tienen dominio público y CC BY; anota los créditos en la descripción.