Teardown
Eine Maschine, eine Seite. Der Besucher sieht das Ding laufen, drückt einen Knopf, und das arbeitende Teil öffnet sich, während die Messgeräte darum herum die echten Werte anzeigen. Bleibt die Seite sich selbst überlassen, spielt sie eine automatisierte Tour mit einem Mauszeiger ab. Derselbe Code zeichnet einen Film dieser Tour auf, Bild für Bild, mit einer Filmmusik, die auf der gleichen Zeitachse aufbaut.
SKILL_DIR ist der Ordner, in dem diese Datei liegt (bei einer Cursor-Installation normalerweise ~/.cursor/skills/teardown, ~/.agents/skills/teardown oder .cursor/skills/teardown in einem Projekt). Builds und Render-Ausgaben landen in ./teardown-out des Ordners, von dem aus du die Befehle ausführst.
Erster Start
cd "$SKILL_DIR" && npm install && npx playwright install chromium
Die HTML-Seiten brauchen nichts weiter. Für das Video wird ffmpeg im PATH benötigt und für die Filmmusik Python 3 (nur Standardbibliothek). Setze FFMPEG, PYTHON oder CHROMIUM, um auf andere Binärdateien zu verweisen.
Die 3 Beispiele
Baue eines, öffne die HTML-Seite, drücke Knöpfe. Kopiere das am nächsten liegende Beispiel, wenn du eine neue Maschine beginnst.
| Die Maschine | Was sie antreibt | Was man übernehmen kann | |
|---|---|---|---|
v8-* | Ein Cross-Plane-V8 mit Glaszylindern: Die Ladung verbrennt im Zylinder, den du verfolgst, Ventile heben auf ihren Nocken, 8 Kolben bewegen sich exakt nach der Schubkurbel. | Kurbelwinkel. Zündreihenfolge 1-8-4-3-6-5-7-2, ein Kammerdruckmodell für das Indikatordiagramm. | Schnittmodi (Glas, Schnitt, Vollmaterial), die Zündreihenfolge als Zylinderauswahl, Live-Diagramme synchron zur Kurbel. |
cessna-* | Das Hauptfahrwerk der Cessna 337, das beim Start in die Zelle einfährt und bei der Landung ausfährt. | Hub 0..1: Eine 201-mm-Zahnstange treibt ein 32-zähniges Ritzel (Modul 4 mm) um exakt 180°. | Ein Freiheitsgrad, der eine ganze Mechanik stellt, Flugphasen als Funktionen der Zeit, eine Landebahn, die unter dem Modell abrollt. |
strandbeest-* | Theo Jansens Strandbeest, 20 Beine auf einer Kurbelwelle, die seitlich über den Sand läuft. | Wind. Die Kurbeldrehzahl wird daraus integriert; jedes Gelenk folgt durch Kreisschnitt aus Jansens 11 Gliedlängen. | Ein exakter Gelenklöser in 67 Zeilen, instanziierte Rohre, Fußspuren, ein Kontaktdiagramm aller 20 Füße. |
Jedes Beispiel kommt in 3 Ordnern: <name>-shared/ enthält die Maschine, <name>-film/ die aufgezeichnete Tour, <name>-live/ die Seite, die du steuerst.
node "$SKILL_DIR/sims/build.mjs" v8-live # → teardown-out/dist/v8-live.html, offline, ~800 KB
node "$SKILL_DIR/sims/build.mjs" # alle 6
Wie eine Maschine aufgebaut ist
Ein Ordner pro Maschine, aufgeteilt nach Aufgabe. Die Beispiele folgen diesem Schema exakt:
| Datei | Aufgabe |
|---|---|
kin.js / gear.js / jansen.js | Die Mathematik. Echte Maße in echten Einheiten, eine Eingabe (Kurbelwinkel, Hub, Wind) und jede Position und jeder Messwert als Funktion davon. Kein three.js hier. |
engine.js / gear.js / beast.js | Die Geometrie, aus Code aufgebaut: gedrehte Teile, Rohre, instanziierte Glieder. update(input) setzt sie in Pose. |
stage.js | Studio, Lichter, Schatten, Nachbearbeitung (GTAO, Bloom). frame(state, cam) zeichnet einen Frame. |
console.js | Das Overlay: festes 1600 × 900 CSS-Layout mit Panels, Diagrammen, Schaltern, Beschriftungen mit Führungslinien. update(state) zeichnet es aus dem Zustand des Frames neu. |
film.js | Ein SCRIPT mit Bedienschritten (Ansichten, Isolieren, Schnitt, Gas) und render(t) als reine Funktion der Zeit. |
live.js | Dieselbe Maschine in Echtzeit: Knöpfe, Schieber, Orbit, Zoom, Tasten. Ohne Eingabe spielt es das film.js-Skript als automatische Tour mit Mauszeiger ab; jeder Klick, Zug, Mausrad oder Tastendruck übergibt die Kontrolle, nach 25 s ohne Eingabe wird sie zurückgegeben. |
score.py | Der Sound auf der Zeitachse des Films. |
Für eine Maschine außerhalb von SKILL_DIR erstellst du einen Ordner mit einer main.js und importierst das Kit mit Namen: import {runSim} from 'teardown-kit/runtime.js'. Baue sie mit node "$SKILL_DIR/sims/build.mjs" ./my-machine.
Regeln, die entscheiden, ob es funktioniert
- Beginne mit einer Referenz, niemals aus der Fantasie. Ein Foto, eine Schnittzeichnung aus einem Handbuch, ein Clip des echten Dings. Baue danach und vergleiche Frame für Frame. Aus einem Satz erfundene Maschinen wirken generisch; dasselbe Modell baut eine echte Maschine detailgetreu nach, wenn es etwas zum Abgleichen gibt.
- Echte Zahlen, eine Quelle. Besorge zuerst die echten Maße (Bohrung, Hub, Zähnezahl, Gliedlängen). Jeder Messwert, jedes Diagramm und jede Beschriftung liest aus derselben Funktion, die die Teile in Pose setzt – daher kann nichts auf dem Bildschirm mit der Bewegung in Konflikt stehen. Überprüfe eines von Hand: Bei der Cessna ergeben 32 Zähne × Modul 4 mm einen Teilkreisdurchmesser von 128 mm, und der halbe Umfang ist der Hub von 201 mm.
- Eine Eingabe steuert alles. Pose aus dem Kurbelwinkel, dem Hub oder dem Wind. Nichts ist per Hand keyframed, daher driften Film, Live-Seite und Filmmusik nie auseinander.
- Das Teil, das die Arbeit verrichtet, öffnet sich. Isoliere es, schneide es auf, durchleuchte es. Eine Kamera, die ins Innere geht, ist mehr wert als zehn Orbits.
- Lichtstudio, ein Akzent. Helle Hintergründe lesen sich gut im Feed; dunkle Hintergründe kosten die erste Sekunde. Der Akzent markiert das, was sich ändert. Achte auf fast weiße Teile vor hellem Hintergrund: Gib ihnen eine Tintenkontur oder einen dunkleren Untergrund.
- Wähle die Kamera für die Mechanik. Eine ebene Gelenkkette von vorn gesehen wird zum Gitter; eine 3/4-Ansicht entlang der Wirbelsäule hält jedes Bein lesbar.
- Behebe Kollisionen mit einer Zahl. Wenn ein Teil in ein anderes hineinragt, skaliere oder verschiebe es im Parameterblock. Verstecke es niemals mit der Kamera.
Live-Seite-Regeln
- Teste auf einer integrierten GPU. Eine Seite, die auf einer Desktop-Grafikkarte mit 60 fps läuft, kann auf der integrierten eines Laptops auf 15 fps fallen. Die Beispiele führen Qualitätsstufen (
TIERSinlive.js): kein AO, niedrigeres Pixelverhältnis, kleinere Schattenkarten, Panels werden nur jedes zweite Bild neu gezeichnet. Finde zuerst den teuren Shader: Eine Verschiebung, die 5 Mal pro Vertex berechnet wird, kostet mehr als alle Stufen zusammen. - Wärme die Shader auf.
kit/warm.jskompiliert jedes Programm parallel vor dem ersten Frame, sodass die Seite beim ersten Zeichnen nie einfriert. - Halte die Seite unter 1 MB. Link-Crawler (X, Slack) lesen die Teilen-Karte aus dem HTML und geben bei einer 15-MB-Seite auf. Große Modelle werden als Dateien neben der Seite ausgeliefert:
build.json→{"external": {"heavy.glb": "light.glb"}}.
render(t)-Vertrag
import {runSim} from '../kit/runtime.js';
const DEFAULTS = {duration: 20, fps: 60, hud: {enabled: false}, director: {enabled: false}};
runSim(DEFAULTS, async ({cfg, canvas, capture}) => {
// Szene aufbauen, alles Zustandsbehaftete vorberechnen
return {render(t) { /* zeichne Frame t komplett neu */ return {}; }, resize() {}};
});
render(t)ist rein: keinMath.random(verwenderng(seed)auskit/util.jsbeim Setup), keinDate, kein zwischengespeicherter Zustand zwischen Frames. Die Aufnahme ruft es zu beliebigen Zeitpunkten auf.- Alles, das sich nach eigenen Regeln bewegt (Agenten, Partikel), wird einmal beim Laden in typisierte Arrays simuliert;
renderliest sie nur noch aus. Eine über die Zeit integrierte Größe (die Kurbel des Strandbeests bei wechselndem Wind) wird in geschlossener Form integriert oder beim Laden tabelliert. - Jeder Beat landet in
<name>-film/timeline.jsonin Sekunden. Die Filmmusik liest dieselbe Datei. - Frame 0 ist das Vorschaubild. Es muss bereits die Maschine zeigen.
Befehle
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 ist das CSS-Layout und --dpr multipliziert es: 1600 × 900 bei 1.2 ergibt 1920 × 1080. Capture gibt die verwendete GPU aus; ein Software-Renderer bedeutet, dass die GPU-Flags in paths.mjs nicht gegriffen haben.
Prüfschleife
Du kannst das Video nicht ansehen; du kannst dir Einzelbilder ansehen. Melde eine Maschine niemals als fertig, ohne Blätter des aktuellen Renders gelesen zu haben.
--stills beats: ein Kachelbild pro Timeline-Beat, 0.3 s danach, jeweils mitt=beschriftet.- Lies jede Kachel gegen die folgende Checkliste.
- Zoome hinein:
--stills every:0.2 --from A --to Bfür einen Übergang,--still Tfür ein volles Einzelbild. - Korrigiere, baue neu, gehe zurück zu 1. Höre auf, wenn ein kompletter Durchlauf nichts findet.
- Final:
node "$SKILL_DIR/sheet.mjs" --video <final.mp4> --timeline <timeline.json>und lies auch das: Es zeigt, was der Encoder geschrieben hat.
Checkliste:
- Die Mechanik. Jedes Teil, das sich berühren sollte, berührt sich; nichts dringt durch etwas hindurch; die Zahlen auf den Panels stimmen mit der Pose überein.
- Komposition. Ein Blickpunkt pro Frame. Nichts Wichtiges innerhalb von 60 px vom Rand entfernt. Beschriftungen überdecken niemals das benannte Teil.
- Typografie. Nichts abgeschnitten. Die wichtigste Anzeige ist bei Smartphone-Breite lesbar.
- 3D. Bloom frisst Details: Halte emittierende Ebenen unter 1.0. Fast weiße Flächen brennen aus. Glänzendes Metall bei schwachem Licht streift: Rauheit ≥ 0.45 und envMapIntensity ≈ 0.5 auf großen Flächen.
- Timing. Jeder Zustand hält lange genug zum Lesen: 1 s für eine kurze Phrase plus 0.25 s pro Wort über drei hinaus.
three.js-Fallen, die die Beispiele bereits getroffen haben
smoothstep(a, b, x)mita > bgibt auf Windows (ANGLE/D3D) 0 zurück. Schreibe1.0 - smoothstep(b, a, x).- Ein
ShadowMaterialoder ein Decal einen Bruchteil eines Millimeters über dem Boden verliert den Tiefentest innerhalb eines Post-Processing-Render-Targets. Gib ihmpolygonOffset. - Ein in den Boden gelegtes Quad mit gespiegelter Zuordnung wird als Rückseite gecullt. Verwende
DoubleSide. - Ein NaN-Pixel färbt durch Bloom das gesamte Bild schwarz. Clampe jede
pow()-Basis in einem Shader. - GTAO auf einem beschnittenen (Schnittansicht-)Modell benötigt dieselben Schnittebenen auf
gtao.normalMaterial, sonst taucht die abgeschnittene Hälfte als blasser Geist wieder auf.
Sound
sfx/ synthetisiert jeden Ton in reinem Python (48 kHz, reproduzierbar) und liest die Timeline des Films, sodass der Sound nicht aus dem Takt geraten kann. python "$SKILL_DIR/run_sfx.py" --list zeigt die Generatoren. sample() fügt die aufgenommenen CC0-Sounds in sfx/samples/ hinzu. sfx/score_kit.py enthält die Grundklänge und Akzente, die die 3 Filmmusiken verwenden: ein Akkordbett, ein leises Bumm pro Arbeitstakt, ein Motor, Luft unter Zug.
- Jede Zeitangabe kommt aus der Timeline, niemals eine getippte Zahl.
- Mastering ist automatisch: −14 LUFS, True Peak ≤ −2 dBFS.
- Akzent, niemals Erzählung: Kontinuierliche Bewegung bleibt stumm. Ein Schlüssel für jeden tonalen Sound.
Geschmack, mit Zahlen
- Easing: Eingänge
cubic-bezier(0.16, 1, 0.3, 1), Ausgängecubic-bezier(0.6, 0, 0.9, 0.35)bei halber Länge des Eingangs, Zustand zu Zustandcubic-bezier(0.65, 0, 0.35, 1). Linear nur für kontinuierliche Bewegung. - Die Maschine füllt 60–70 % ihres Viewports. Beschriftungen niemals unter 34 px auf einer 1080-Leinwand.
- Ein Akzent, und er bedeutet etwas: das Teil, das gerade arbeitet.
- Nichts absolut still: ein Element immer lebendig.
Danksagungen
Cursor-Port von bugeng777/teardown-cursor von elvarofficial/teardown (MIT). Das Kit, die Frame-Aufnahme, die Sound-Engine und die Prüfregeln stammen von whaleyxbt/claude-motion (MIT). Die aufgenommenen Samples sind CC0 von Freesound, beschnitten von Barty-Bart/motion-graphics (MIT); Quellen in sfx/samples/SOURCES.md.