Whiteboard Teacher Video · Video Explicativo con Animaciones Dibujadas
Convierte un guion explicativo en un video que se "dibuja a sí mismo". Las imágenes no se generan como fotos, sino que el código las dibuja trazo a trazo: por lo tanto, se pueden re-renderizar, ajustar con precisión y sin costo de generación de imágenes.
Contrato del Motor (Comprende esto primero)
El video completo es una función pura del tiempo. ENGINE.renderAt(t) renderiza el fotograma en el segundo t, independientemente de lo que se haya reproducido antes.
Esto otorga cuatro capacidades, que son la razón fundamental por la que debes seguir las reglas:
| Capacidad | ¿Cómo se logra? |
|---|---|
| Modificar una página solo re-renderiza los segmentos afectados | Caché segmentada con clave = sha1(motor + código de esa página) |
| 12 navegadores renderizando en paralelo | Los segmentos no dependen entre sí |
| La vista previa puede saltar a cualquier instante | No necesita reproducir desde el inicio |
| Verificación determinista | Renderizar dos veces el mismo instante y comparar píxeles |
Línea roja: El código de página tiene prohibido Math.random() / Date.now() / transiciones CSS / rAF. La aleatoriedad debe usar PV.rng(seed), y el tiempo debe usar P.c(i) / P.cw(i,'palabra'). Si rompes esto, las cuatro capacidades anteriores quedarán inservibles.
Flujo de Trabajo (Sigue este orden, no te saltes pasos)
Usuario: "Haz un video de 90 segundos sobre la relatividad"
↓
① Leer references/storyboard-method.md → escribir storyboard.json
② ★★★ DETENTE y lee el storyboard al usuario para confirmación ★★★ ← Única compuerta humana obligatoria
③ Escribir script.js (narración, con cues según storyboard)
④ python scripts/ftnew.py <proyecto> --title "..." --style whiteboard
⑤ python scripts/ftvoice.py <proyecto> → voice.js + subtítulos.srt
⑥ Escribir páginas una por una en pages/pNN.js ← Principal trabajo creativo
⑦ python scripts/ftcheck.py <proyecto> → lint + collage de cada página
⑧ Abrir out/snap/pNN.jpg con Read para ver página por página → Corregir de forma dirigida, **no más de 2 rondas**
⑨ python scripts/ftrender.py --fps 24 --skip-subs → **Primero un borrador que se pueda ver**
⑩ ★ Entregar el borrador al usuario ★ ← No acumules correcciones sin entregar
⑪ Corregir una ronda basándose en las imágenes → ftrender.py → Entregar versión final
El paso ② no se puede omitir. Si el storyboard está mal, por más bonito que se dibuje después, solo será una presentación de diapositivas. El proyecto original implementa este paso como una compuerta de promesa por una razón.
★★ El paso ⑩ es igualmente importante: primero entrega el video, luego púlelo
El usuario quiere un video, no un video perfecto. El ritmo de entrega es más importante que la precisión del producto final.
- La revisión de calidad del paso ⑧ debe llegar hasta "sin errores evidentes" (lint sin errores, texto no tapado, NaN convertido a 0), no persigas la perfección de la composición
- El primer renderizado usa
--fps 24 --skip-subs(40% más rápido), entrega primero una versión visible al usuario - No esperes a corregir todas las rondas para entregar. El usuario solo puede dar retroalimentación real cuando vea el borrador
- ¿Quieres cambiar el estilo, añadir páginas, ajustar contenido? Modifica y re-renderiza, la caché segmentada hará que la segunda ronda sea muy rápida (probado: modificar una página en 1 minuto)
Lección probada: Al hacer el video de 95 segundos con 8 páginas, dudé mucho en la segunda ronda sobre si valía la pena corregir los problemas que reportaba el lint, y entregué después de dos rondas de duda. El usuario se impacientó. Lo correcto: Corrige los errores de lint, corrige los bugs evidentes, entrega el borrador primero, y luego corrige en la siguiente ronda.
★ Lint todo verde no significa que la imagen sea correcta
lint es una verificación automática; solo puede detectar errores duros como elementos fuera de límites, superposiciones o tamaño de fuente; la composición, si los elementos están apretados, si el texto está cubierto por bloques de color, solo se ve al mirar la imagen.
Lección probada: En la página p07, el lint reportó 0 errores y la verificación de calidad mostró todo verde, pero al mirar el collage descubrí que la palabra "microsegundo" en "7 microsegundos" estaba completamente cubierta por un resaltador, dejando solo el "7" en rojo: el espectador no podía leer la unidad en absoluto.
Reglas Estrictas
★ Ritmo (La regla más importante)
- El usuario quiere un video, no un video perfecto. El borrador también es un video; no acumular sin entregar es como no haber hecho nada.
- La revisión de calidad debe detenerse en "sin errores evidentes", no persigas la perfección de la composición.
- El primer renderizado usa
--fps 24 --skip-subs(40% más rápido), entrega primero una versión visible. - Corrige una ronda y luego entrega la versión final. La caché segmentada hace que la segunda ronda sea muy rápida (probado: modificar una página en 1 minuto).
Imagen
- Área de contenido: x 140–1780, y 180–880. y > 900 es la zona de subtítulos, no pongas contenido allí.
- ★ Para centrar texto, debes dar
w+align:'c'— si no dasw,xes el borde izquierdo, no el centro. Este es el error más frecuente. - ★ En páginas con
P.push(), coloca los elementos hacia el centro de la imagen — al ampliar, los elementos en los bordes se saldrán del lienzo (lint reportará off-canvas). - Tamaño de fuente del cuerpo: ≥ 32px (< 24px hará que lint falle).
- Máximo 8 caracteres por línea de texto chino.
- Colores: solo usa nombres semánticos:
inksoftmutedfaintblueorangeredgreenpurpleyellow, añadeFpara relleno claro (blueF). No codifiques colores hex — si escribes mal el nombre, el motor lo registrará y el informe de calidad lo mostrará. - Una página, una idea, 2–3 frases de narración, cada 2–4 segundos debe aparecer algo nuevo.
Animación
P.c(i, dt)= inicio de la frase i + dt segundos.P.cw(i, 'palabra', dt)= cuando la frase i pronuncia una palabra específica + dt segundos (usa esto para números clave: que aparezcan justo cuando se pronuncian).P.ce(i)= fin de esa frase;P.endT= fin de la página.- Los énfasis se hacen con trazos (
ringcírculo /underlinesubrayado /markresaltador /crosscruz /checkpalomita), no con efectos especiales. - Al cambiar de página, debe haber algo en la página, no puede estar en blanco.
Iteración
- Modificar una página → re-renderizar solo esa página (
ftcheck.py --only pNN), no todo. - Si lint tiene errores, no renderices, corrige primero.
- Aunque lint pase, revisa el collage — lint solo controla errores duros, no comprueba si la composición es bonita o si hay elementos tapados.
ftcheck.pyreportará NaN — son errores fatales que lint no detecta (el gráfico desaparece en silencio).- Si no puedes corregir todo, marca "Necesita atención" y continúa, no te bloquees (máximo 2 rondas para sacar el video).
Tiempos de Referencia (Probados localmente, video de 8 páginas, 87 segundos)
| Fase | Duración |
|---|---|
| Crear proyecto + escribir código de 8 páginas | Aprox. 10 minutos (tiempo de creación humana) |
| Locutar 26 frases | Aprox. 1.5 minutos |
| Control de calidad (lint + collage de 8 páginas) | Aprox. 1 minuto |
| Renderizado (primera vez, 12 workers) | Aprox. 2 minutos 45 segundos |
| Renderizado (después de modificar una página, con caché) | Aprox. 1 minuto |
Renderizado (versión rápida --fps 24 --skip-subs) | Aprox. 1 minuto 40 segundos |
Por lo tanto, desde crear el proyecto hasta obtener el primer video, el proceso total dura aproximadamente 15 minutos. Iterar sobre una página toma un minuto, así que no hay necesidad de acumular todas las correcciones antes de entregar.
Tres Errores Frecuentes con la API del Motor
// ① Centrar texto: si no das w, x es el borde izquierdo, no el centro
P.text('Andén', { x: 420, y: 640, w: 280, align: 'c', size: 46 }); // ✓ centro en 420
// ② El tercer parámetro de check/cross es el "tamaño", no un objeto de configuración (si lo pasas mal, solo da NaN, no error)
P.cross(970, 716, 64, { color: 'red', at: t }); // ✓
P.cross(970, 716, { color: 'red', at: t }); // ✗ sz = [object Object]
// ③ No llames R al parámetro del callback de liveRough; además, R solo tiene cuatro métodos: box/circle/line/path
const R = 1400; // ← será sobrescrito por el parámetro del callback
P.liveRough((lt, R) => (xL + R) / 2); // ✗ NaN
P.liveRough((lt, rough) => [rough.box(x, y, w, h, { color: 'ink' })]); // ✓
Durante el renderizado, presta atención a la consola por NaN — no lanza excepción, el lint no siempre lo reporta, pero el gráfico correspondiente no se dibuja.
La lista completa está en references/page-api.md.
Qué se puede hacer / Qué no se puede hacer
Sí se puede: Divulgación científica, explicación de conceptos, deducción de procesos, explicación de gráficos de datos, demostraciones de productos (se pueden incrustar capturas de pantalla reales), explicación de proyectos de GitHub; cinco estilos; orientación horizontal/vertical; locución en chino/inglés; subtítulos automáticos; duración de 20 segundos a 10 minutos.
No se puede:
- Fotos realistas o estilo de grabación 3D realista (para esa textura, usa herramientas de generación de imágenes).
- Música de fondo automática y efectos de sonido (
page.sfxpuede hacerlo, pero debes preparar los archivos de audio tú mismo). - Ilustraciones dibujadas a mano generadas por IA (los componentes son vectores dibujados con código).
- Generar decenas de páginas de una vez (cada página requiere código, se recomienda ≤ 12 páginas).
Diferencia con whiteboard-explainer
| whiteboard-explainer | frametell-video (esta habilidad) | |
|---|---|---|
| Imagen | Trazos de píxel a nivel de crecimiento | Dibujo estilizado con rough.js |
| Estilo | Único (pizarra blanca, color de fondo codificado) | Cinco estilos, incluido modo oscuro |
| Componentes | Todos escritos a mano | 29 prefabricados |
| Control de calidad | Verificación de pertenencia | lint + collage + determinismo |
| Adecuado para | Deducción matemática, fuerte sensación de dibujo a mano | Divulgación científica, gráficos densos, múltiples estilos |
Si necesitas "sensación real de dibujo a mano / crecimiento elemento por elemento" → usa whiteboard-explainer. Si necesitas "múltiples estilos / gráficos complejos / reutilización de componentes / control de calidad sólido" → usa esta habilidad.
Índice de Documentación
| Cuándo leerlo | Qué leer |
|---|---|
| Primera vez | assets/example-relativity/ (código fuente completo de un video real: 8 páginas, léelo y entenderás) |
| Antes de dibujar cualquier página | references/page-api.md (referencia rápida de la API de dibujo + tres errores frecuentes) |
| Al definir el storyboard / escribir la narración | references/storyboard-method.md (estructura de cinco partes + prohibiciones) |
| Al elegir estilo / dudas sobre colores | references/style-packs.md (cinco estilos + resumen de sintaxis) |
Si no entiendes los campos de script.js | references/script-format.md |
| Si la locución da error / quieres cambiar TTS | references/tts-backends.md (incluye explicación de que edge-tts está bloqueado) |
| Si no entiendes los errores del motor | references/troubleshooting.md (indexado por síntoma) |
Primer Uso
Si scripts/ muestra "Motor no listo" o faltan dependencias, ejecuta primero la configuración:
python scripts/setup.py # Multiplataforma
powershell -ExecutionPolicy Bypass -File scripts/setup.ps1 # Windows
Requisitos: Node.js ≥ 22.19, Git, Chrome/Edge local, Python 3.9+
La primera vez tarda entre 2 y 4 minutos: descarga el motor desde GitHub (aproximadamente 16 MB, sparse checkout solo para packages/engine) + npm install dependencias (aproximadamente 130 MB).
Si no tienes Git instalado: Descarga manualmente https://github.com/sitor-ai/frametell , copia packages/engine al directorio raíz de la habilidad como engine/, y vuelve a ejecutar setup.
La lista de dependencias y su tamaño está en README.md; la información de licencias está en vendor/ATTRIBUTION.md.