¿Qué hace teardown?
Una máquina, una página. El visitante ve la máquina funcionar, presiona un botón y la pieza que realiza el trabajo se abre mientras los indicadores a su alrededor muestran los números reales. Si se deja solo, la página se recorre a sí misma con un cursor. El mismo código graba una película del recorrido, cuadro por cuadro, con una banda sonora construida sobre la misma línea de tiempo.
SKILL_DIR es la carpeta donde reside este archivo (para una instalación de Cursor, normalmente ~/.cursor/skills/teardown, ~/.agents/skills/teardown o .cursor/skills/teardown dentro de un proyecto). Las compilaciones y renderizaciones se guardan en ./teardown-out de la carpeta desde donde se ejecuta.
Primera ejecución
cd "$SKILL_DIR" && npm install && npx playwright install chromium
Las páginas no necesitan nada más. El video necesita ffmpeg en PATH y la banda sonora necesita Python 3 (solo biblioteca estándar). Configura FFMPEG, PYTHON o CHROMIUM para apuntar a otros binarios.
Los 3 ejemplos
Compila uno, abre el HTML, presiona botones. Copia el más cercano cuando empieces una nueva máquina.
| La máquina | Qué la impulsa | Vale la pena robar | |
|---|---|---|---|
v8-* | Un V8 de plano cruzado con camisas de vidrio: la carga se quema dentro del cilindro que sigues, las válvulas se levantan sobre sus levas, 8 pistones se mueven desde el mecanismo biela-manivela exacto. | Ángulo del cigüeñal. Orden de encendido 1-8-4-3-6-5-7-2, un modelo de presión en la cámara para el diagrama indicador. | Modos de corte (vidrio, sección, sólido), el orden de encendido como selector de cilindros, gráficos en vivo sincronizados con el cigüeñal. |
cessna-* | El tren de aterrizaje principal del Cessna 337 plegándose dentro del fuselaje al despegar y bajando al aterrizar. | Carrera 0..1: una cremallera de 201 mm gira un piñón de 32 dientes (módulo 4 mm) exactamente 180°. | Un grado de libertad que plantea todo un mecanismo, fases de vuelo como funciones del tiempo, una pista que rueda debajo del modelo. |
strandbeest-* | El Strandbeest de Theo Jansen, 20 patas en un cigüeñal, caminando de lado sobre la arena. | Viento. La velocidad del cigüeñal se integra a partir de él; cada articulación sigue por intersección de círculos a partir de las 11 longitudes de eslabón de Jansen. | Un solucionador de eslabonamiento exacto en 67 líneas, tubos instanciados, huellas de patas, un gráfico de contacto de las 20 patas. |
Cada uno viene en 3 carpetas: <nombre>-shared/ contiene la máquina, <nombre>-film/ el recorrido grabado, <nombre>-live/ la página que se conduce.
node "$SKILL_DIR/sims/build.mjs" v8-live # → teardown-out/dist/v8-live.html, fuera de línea, ~800 KB
node "$SKILL_DIR/sims/build.mjs" # los 6
Cómo se construye una máquina
Una carpeta por máquina, dividida por trabajo. Los ejemplos siguen esto exactamente:
| Archivo | Trabajo |
|---|---|
kin.js / gear.js / jansen.js | Las matemáticas. Dimensiones reales en unidades reales, una entrada (ángulo del cigüeñal, carrera, viento) y cada pose y valor mostrado como función de ella. No hay three.js aquí. |
engine.js / gear.js / beast.js | La geometría, construida desde código: piezas torneadas, tubos, eslabones instanciados. update(input) la posiciona. |
stage.js | Estudio, luces, sombras, postprocesado (GTAO, bloom). frame(state, cam) dibuja un cuadro. |
console.js | La superposición: diseño CSS fijo de 1600 x 900 con paneles, gráficos, interruptores, llamadas con líneas guía. update(state) lo redibuja desde el estado del cuadro. |
film.js | Un SCRIPT de movimientos del operador (vistas, aislar, corte, acelerador) y render(t) como una función pura del tiempo. |
live.js | La misma máquina en tiempo real: botones, deslizadores, órbita, zoom, teclas. Sin entrada, reproduce el SCRIPT de film.js como un recorrido automático con un cursor; cualquier clic, arrastre, rueda o tecla entrega el control, 25 s de inactividad lo devuelven. |
score.py | El sonido en la línea de tiempo de la película. |
Para una máquina fuera de SKILL_DIR, crea una carpeta con un main.js e importa el kit por nombre: import {runSim} from 'teardown-kit/runtime.js'. Compílala con node "$SKILL_DIR/sims/build.mjs" ./mi-maquina.
Reglas que deciden si funciona
- Empieza desde una referencia, nunca desde la imaginación. Una foto, un dibujo en corte de un manual, un clip de la máquina real. Construye contra ella y compara cuadro por cuadro. Las máquinas inventadas a partir de una oración siempre salen genéricas; el mismo modelo reconstruye una real de cerca cuando tiene algo con qué coincidir.
- Números reales, una sola fuente. Obtén las dimensiones reales primero (diámetro, carrera, número de dientes, longitudes de eslabones). Cada indicador, gráfico y etiqueta lee de la misma función que posiciona las piezas, así que nada en pantalla puede discrepar del movimiento. Verifica uno a mano: en el Cessna, 32 dientes × módulo 4 mm es un diámetro primitivo de 128 mm, y la mitad de su circunferencia es la carrera de 201 mm.
- Una sola entrada lo impulsa todo. Pose desde el ángulo del cigüeñal, la carrera o el viento. Nada se anima manualmente, por lo que la película, la página en vivo y la banda sonora nunca se desincronizan.
- La pieza que realiza el trabajo se abre. Aíslala, córtala, hazla transparente. Una cámara que entra adentro vale más que diez órbitas.
- Estudio de luz, un acento. Los fondos pálidos se leen en un feed; los oscuros pierden el primer segundo. El acento marca lo que está cambiando. Observa las piezas casi blancas sobre un fondo pálido: dales un contorno de tinta o un suelo más oscuro.
- Elige la cámara para el mecanismo. Un eslabonamiento plano visto de frente se colapsa en un enrejado; una vista en 3/4 a lo largo de la columna mantiene cada pata legible.
- Arregla las colisiones con un número. Cuando una pieza atraviesa otra, escálala o muévela en el bloque de parámetros. Nunca la escondas con la cámara.
Reglas de la página en vivo
- Prueba en una GPU integrada. Una página que corre a 60 fps en una tarjeta de escritorio puede caer a 15 fps en la integrada de un portátil. Los ejemplos llevan niveles de calidad (
TIERSenlive.js): sin AO, menor relación de píxeles, mapas de sombra más pequeños, paneles redibujados cada dos cuadros. Encuentra primero el shader costoso: un desplazamiento calculado 5 veces por vértice cuesta más que todos los niveles juntos. - Calienta los shaders.
kit/warm.jscompila todos los programas en paralelo antes del primer cuadro, para que la página nunca se congele al dibujar por primera vez. - Mantén la página por debajo de 1 MB. Los rastreadores de enlaces (X, Slack) leen la tarjeta de compartir del HTML y se rinden con una página de 15 MB. Los modelos grandes se envían como archivos junto a la página:
build.json→{"external": {"heavy.glb": "light.glb"}}.
Contrato de render(t)
import {runSim} from '../kit/runtime.js';
const DEFAULTS = {duration: 20, fps: 60, hud: {enabled: false}, director: {enabled: false}};
runSim(DEFAULTS, async ({cfg, canvas, capture}) => {
// construye la escena, precalcula cualquier cosa con estado
return {render(t) { /* dibuja el cuadro t desde cero */ return {}; }, resize() {}};
});
render(t)es pura: sinMath.random(usarng(seed)dekit/util.jsen la configuración), sinDate, sin estado entre cuadros. La captura lo llama en momentos arbitrarios.- Cualquier cosa que se mueva por sus propias reglas (agentes, partículas) se simula una vez al cargar en arrays tipificados;
rendersolo los lee. Una cantidad integrada en el tiempo (el cigüeñal del Strandbeest bajo un viento cambiante) se integra en forma cerrada o se tabula al cargar. - Cada tiempo va a
<nombre>-film/timeline.jsonen segundos. La banda sonora lee el mismo archivo. - El cuadro 0 es la miniatura. Ya debe mostrar la máquina.
Comandos
node "$SKILL_DIR/sims/build.mjs" v8-film
node "$SKILL_DIR/sims/capture.mjs" v8-film --size 1600x900 --dpr 1.2 --stills beats --cols 4 --out teardown-out/v8-beats.png
node "$SKILL_DIR/sims/capture.mjs" v8-film --size 1600x900 --dpr 1.2 --still 12.5 --out teardown-out/v8-12.5.png
node "$SKILL_DIR/sims/capture.mjs" v8-film --size 1600x900 --dpr 1.2 --out teardown-out/v8-silent.mp4
python "$SKILL_DIR/run_sfx.py" "$SKILL_DIR/sims/v8-shared/score.py"
ffmpeg -y -i teardown-out/v8-silent.mp4 -i teardown-out/v8-score.wav -map 0:v -map 1:a -c:v copy -c:a aac -b:a 256k -shortest -movflags +faststart teardown-out/v8.mp4
node "$SKILL_DIR/wave.mjs" teardown-out/v8.mp4 --timeline "$SKILL_DIR/sims/v8-film/timeline.json"
--size es el diseño CSS y --dpr lo multiplica: 1600 x 900 a 1.2 da 1920 x 1080. La captura imprime la GPU que obtuvo; un renderizador por software significa que las banderas de GPU en paths.mjs no funcionaron.
Bucle de revisión
No puedes ver el video; puedes mirar cuadros. Nunca reportes una máquina como terminada sin leer hojas del renderizado actual.
--stills beats: un mosaico por tiempo de la línea de tiempo, 0.3 s después, cada uno etiquetado cont=.- Lee cada mosaico contra la lista de verificación a continuación.
- Acerca:
--stills every:0.2 --from A --to Bpara una transición,--still Tpara un cuadro completo. - Arregla, recompila, vuelve al paso 1. Detente cuando una pasada completa no encuentre nada.
- Final:
node "$SKILL_DIR/sheet.mjs" --video <final.mp4> --timeline <timeline.json>y léelo también: muestra lo que escribió el codificador.
Lista de verificación:
- El mecanismo. Cada pieza que debe tocar toca; nada atraviesa nada; los números en los paneles coinciden con la pose.
- Composición. Un punto focal por cuadro. Nada importante a menos de 60 px de un borde. Las etiquetas nunca cubren la pieza que nombran.
- Tipografía. Nada recortado. La lectura clave se lee a lo ancho de un teléfono.
- 3D. El bloom se come los detalles: mantén las capas emisivas por debajo de 1.0. Las superficies casi blancas se sobreexponen. El metal brillante con poca luz se raya: rugosidad ≥ 0.45 y envMapIntensity ≈ 0.5 en superficies grandes.
- Tiempo. Cada estado se mantiene el tiempo suficiente para leer: 1 s para una frase corta más 0.25 s por palabra más allá de tres.
Trampas de three.js que los ejemplos ya encontraron
smoothstep(a, b, x)cona > bdevuelve 0 en Windows (ANGLE/D3D). Escribe1.0 - smoothstep(b, a, x).- Un
ShadowMaterialo un decal de una fracción de milímetro sobre el suelo pierde la prueba de profundidad dentro de un render target de postprocesado. DalepolygonOffset. - Un cuadrilátero colocado en el suelo con un mapeo reflejado se descarta como una cara trasera. Usa
DoubleSide. - Un píxel NaN oscurece todo el cuadro a través del bloom. Aplica un límite a cada base de
pow()en un shader. - El GTAO en un modelo recortado (corte) necesita los mismos planos de recorte en
gtao.normalMaterial, o la mitad cortada regresa como un fantasma pálido.
Sonido
sfx/ sintetiza cada sonido en Python puro (48 kHz, reproducible) y lee la línea de tiempo de la película, por lo que el sonido no puede desincronizarse. python "$SKILL_DIR/run_sfx.py" --list muestra los generadores. sample() agrega los sonidos CC0 grabados en sfx/samples/. sfx/score_kit.py tiene las capas de fondo y acentos que usan las 3 bandas sonoras: un acorde de fondo, un golpe suave por carrera de potencia, un motor, aire bajo resistencia.
- Cada tiempo proviene de la línea de tiempo, nunca un número escrito.
- La masterización es automática: −14 LUFS, pico verdadero ≤ −2 dBFS.
- Acento, nunca narración: el movimiento continuo permanece en silencio. Un sonido clave para cada sonido tonal.
Buen gusto, con números
- Suavizado: entradas
cubic-bezier(0.16, 1, 0.3, 1), salidascubic-bezier(0.6, 0, 0.9, 0.35)a la mitad de la duración de la entrada, estado a estadocubic-bezier(0.65, 0, 0.35, 1). Lineal solo para movimiento continuo. - La máquina llena el 60-70% de su viewport. Las etiquetas nunca por debajo de 34 px en un lienzo de 1080.
- Un acento, y significa algo: la pieza que está trabajando ahora.
- Nada completamente estático: un elemento siempre vivo.
Créditos
Puerto de Cursor por bugeng777/teardown-cursor de elvarofficial/teardown (MIT). El kit, la captura de cuadros, el motor de efectos de sonido y las reglas de revisión provienen de whaleyxbt/claude-motion (MIT). Las muestras grabadas son CC0 de Freesound, recortadas por Barty-Bart/motion-graphics (MIT); fuentes en sfx/samples/SOURCES.md.