1 Punkte von GN⁺ 2024-05-09 | 1 Kommentare | Auf WhatsApp teilen
  • xkcds Machine ist ein riesiges, im Stil einer Rube-Goldberg-Kugelmaschine aufgebautes Spiel, das aus von Leserinnen und Lesern erstellten Kachel-Geräten zusammengesetzt ist; die Idee wurde in nur 3 Wochen als tatsächlicher interaktiver Comic umgesetzt
  • Die Erfahrungen aus früheren Projekten mit Nutzerbeteiligung führten zu dem Designprinzip, dass eine gemeinsame Leinwand nur mit gemeinsamem Kontext und Ziel gut funktioniert
  • Um die Ausdrucksfreiheit der Spieler zu bewahren und zugleich die Kompatibilität der Kacheln sicherzustellen, wurden die Ein- und Ausgaben stark eingeschränkt, und jede Vorrichtung musste innerhalb von 30 Sekunden einen stabilen Zustand erreichen
  • Statt die gesamte Maschine in Echtzeit zu simulieren, läuft mit Rapier nur der sichtbare Bereich; über einen Snapshot zum Freigabezeitpunkt wirken die Geräte so, als seien sie bereits in Betrieb
  • Für Freigabe- und Veröffentlichungsabläufe der Einsendungen kombinierte das Team React und DOM-Rendering, ein Haskell-Backend, Redis, OpenAPI, TanStack Query und eine Moderations-UI

Der Ausgangspunkt von Machine

  • xkcd veröffentlichte Machine am 5. April
  • Machine ist ein gigantischer Builder für Rube-Goldberg-Maschinen im Stil des klassischen Spiels The Incredible Machine
  • Die gesamte Maschine besteht aus kleinen Geräte-Kacheln, die jeweils von einzelnen xkcd-Leserinnen und -Lesern erstellt wurden
  • Das Team baute Machine in 3 Wochen, ausgehend von dem kollaborativen GIF Blue Ball Machine aus dem Jahr 2005
  • Die zentralen Fragen im frühen Brainstorming waren: Woher kommen die Kugeln, ob alle dieselbe Maschine sehen, was das Ziel der Maschine ist, wie Spieler interagieren und was sie zur Teilnahme motiviert

Erkenntnisse aus xkcd-Projekten mit Nutzerbeteiligung

  • Bei Lorenz, einem früheren interaktiven xkcd-Comic, in dem User-generated Content im Mittelpunkt stand, schrieben Leserinnen und Leser den Text der Panels und entwickelten so Witze und Handlung weiter; das blieb als gute Erfahrung in Erinnerung
  • Bei Collector’s Edition von 2020 suchten Spieler Sticker im xkcd-Archiv und klebten sie jeweils einmal auf eine global geteilte Leinwand, doch das funktionierte nicht so gut wie erhofft
    • Alle Spieler starteten in der Mitte einer leeren Karte, und bald war ein chaotischer Bildschirm der erste Eindruck
    • Es gab zu wenig Anreiz, Sticker sorgfältig zu platzieren, und es war schwierig, mit einzelnen Aktionen die Handlung voranzubringen
    • Ohne gemeinsame Geschichte oder gemeinsames Ziel war nicht klar, wie sich jeder Sticker mit den anderen Elementen auf der Seite verband
  • Damit eine kollektive Leinwand gut funktioniert, müssen User anhand von Beispielen lernen können, was sich gut erstellen lässt
  • Um kreative Ergebnisse in eine Richtung zu lenken, braucht es gemeinsamen Kontext und ein Ziel, die synchronisieren, was erstellt werden soll

Design von Einschränkungen: Ausdruckskraft, Kompatibilität und stabiler Zustand nach 30 Sekunden

  • Selbst nachdem die Entscheidung für eine große kollaborative Kugelfall-Vorrichtung gefallen war, blieben die Gesamtgröße der Maschine, die Simulationsmethode und die Integration der Kacheln offene Probleme
  • Bei einer angenommenen Maschinengröße von 100x100 galten 10.000 Kacheln in Echtzeit im Client plus Dutzende Kugeln pro Kachel als riskantes Ziel
  • Ausdruckskraft vor Korrektheit

    • Es wurde geprüft, die gesamte Maschine auf dem Server laufen zu lassen oder einzelne Kacheln zur Verifikation zu simulieren
    • Da sich im Prototyp-Editor leicht chaotische Kugelkollisionsmuster erzeugen ließen, kam das Team zu dem Schluss, dass eine vorhersehbare Maschine die Freiheit der Spieler einschränken würde
    • Das endgültige Design priorisierte spielerische Flexibilität, bis hin zu hochgradig nichtdeterministischen oder kaputten Vorrichtungen
    • Diese Entscheidung machte aktive Moderation nötig, um zu prüfen, ob Kacheln die Vorgaben erfüllen, und um anstößige Inhalte zu entfernen
  • Ein-/Ausgabe-Beschränkungen für Kompatibilität zwischen Kacheln

    • Anfangs wurde erwogen, dass der nächste Spieler frei an den Ausgabepositionen der vorherigen Kachel weiterbauen kann
    • Falls eine früh platzierte Kachel später ersetzt werden müsste, könnte dadurch jedoch ein großer davon abhängiger Bereich kaputtgehen
    • Deshalb wurden Ein- und Ausgabe stark eingeschränkt, damit mehrere Spieler innerhalb desselben Kachelraums kompatible Designs erstellen konnten
    • Dieser Ansatz berührt das Robustness principle: „Sei konservativ beim Senden und großzügig beim Empfangen“
    • Kevins map generator beginnt mit einfachen Rätseln mit einem Eingang und einem Ausgang, wird in der Mitte mit 4 Eingängen und 4 Ausgängen komplexer und kehrt am Ende zu 2 Ausgängen pro Kachel zurück
    • Der Editor gibt den Spielern beim Erstellen einer Kachel Echtzeit-Feedback
      • Eine Kachel sollte Kugeln im Durchschnitt mit ungefähr derselben Rate ausgeben, mit der sie welche erhält
      • Vorrichtungen, die Kugeln verschlucken oder große Verzögerungen verursachen, sollten reduziert werden
      • Um Schwankungen der Eingangssignale aus vorgelagerten Kacheln abzubilden, wurde Chaos Testing eingesetzt, indem die Geschwindigkeit eingehender Kugeln im Editor randomisiert wurde
  • Ein stabiler Zustand muss innerhalb von 30 Sekunden erreicht werden

    • Um zu reduzieren, wie lange Moderatorinnen und Moderatoren hinschauen müssen, wurde als willkürlicher Grenzwert festgelegt, dass eine Vorrichtung innerhalb von 30 Sekunden in einen stabilen Zustand gelangen muss
    • Maßgeblich war die Rechnung, dass 10.000 Kacheln à 30 Sekunden insgesamt rund 83,3 Stunden Moderationszeit bedeuten würden
    • Auch Kugeln wurden so geändert, dass sie nach 30 Sekunden verfallen
      • Ohne Verfall bestand die erste Erfahrung unerfahrener Spieler darin, dass sich Kugeln auf dem Bildschirm aufstauten
      • Die Zahl aktiver rigid bodies stieg stark an, wodurch auch die Physiksimulation langsamer wurde
    • Der Verfall von Kugeln verhindert, dass sich Fehler über die Zeit aufsummieren, und vereinfacht die Moderation, weil in 30 Sekunden die meisten Zielorte der Kugeln sichtbar werden

Die gesamte Maschine nicht in Echtzeit ausführen

  • Die erste große Annahme der Machine-Architektur war, dass sich unter Einhaltung dieser Einschränkungen unterschiedliche Kacheln so aneinanderfügen lassen, dass sie wie eine Gesamtmaschine wirken
  • Diese Annahme wurde überprüft, indem einige kleine Karten erzeugt und gelöst wurden
  • Da sich die gesamte Maschine weder auf dem Server noch im Client in Echtzeit ausführen ließ, war ein Ansatz nötig, der nur den Bereich um die Sicht des Users simuliert
  • Ziel war, dass man einer einzelnen Kugel von oben bis unten durch die Maschine folgen kann
  • Eine physische Welt, in der nur der sichtbare Bereich existiert

    • Der frühe Karten-Viewer simulierte nur den sichtbaren Bereich, aber beim Scrollen starteten neu eingeblendete Kacheln leer, wodurch Lücken im Fluss sichtbar wurden
    • Damit neu erscheinende Kacheln nicht leer wirken, sondern bereits aktiv, fiel die Wahl darauf, Snapshots von Kacheln zu speichern, die einen stabilen Zustand erreicht hatten, und diese kurz vor dem Eintreten in den Bildschirm zu laden
    • Im finalen Comic existieren in der eigentlichen Physiksimulation nur die Kacheln, die gerendert werden
    • Damit es so wirkt, als setze sich die Maschine oberhalb des Bildschirms fort, erzeugen die Kacheln in der obersten Simulationsreihe Kugeln entsprechend der erwarteten Rate aus den Eingangsbeschränkungen
  • Snapshots zum Freigabezeitpunkt

    • Die Snapshot-Erzeugung ist an die Moderations-UI gekoppelt
    • Moderatorinnen und Moderatoren müssen vor der Freigabe einer Kachel mindestens 30 Sekunden warten; der Zustand beim Klick auf den Freigabe-Button wird als Snapshot gespeichert
    • Sie haben außerdem Ermessensspielraum, etwas länger zu warten, bis eine Vorrichtung besonders gut aussieht
    • Der Snapshot-Ansatz setzt kumulierte Fehler faktisch zurück, sodass User beim ersten Sichtbarwerden einer neuen Kachel einen sauberen Zustand sehen, den ein Moderator als gut bewertet hat
    • Wenn man lange genug zusieht, bleiben viele Vorrichtungen stehen oder geraten in einen kaputten Zustand, aber bei weiterem Erkunden trifft man wieder auf neue Snapshots
    • Die gesamte Maschine wird nie vollständig simuliert und nähert sich dadurch eher Hyperrealität an

Rendering-Struktur mit React, DOM und Rapier

  • Machine basiert auf der Physik-Engine Rapier
  • Rapier wurde wegen Dokumentation, API, nützlicher Grundbausteine und der Browser-Performance von WASM dank der Rust-Implementierung gewählt
  • Anfangs war auch die zugesicherte Deterministik von Rapier interessant, letztlich wurde aber keine serverseitige Simulation umgesetzt
  • Über Rapier wurde ein eigener React-Context namens <PhysicsContext> geschrieben
    • Innerhalb des React-Komponenten-Lebenszyklus werden Rapier-Physikobjekte erzeugt und verwaltet
    • Dadurch ließ sich jedes platzierbare Objekt und jede Kollisionsfläche leichter als „widget“-Komponente entwickeln
    • React funktioniert hier wie ein schneller, grober scene graph
    • Wenn eine Kachel unmounted wird, werden die zugehörigen Physikobjekte und das DOM bereinigt
    • Dank Hot Reloading per Fast Refresh war es leicht, Kollisionsformen anzupassen
  • Physik-Hooks wurden so gebaut, dass sie außerhalb von <PhysicsContext> nicht funktionieren, was in der Moderations-UI für statische Vorschauen genutzt wurde
  • Im Nachhinein wäre es besser gewesen, Rapier-Objekte als Komponenten statt als Hooks zu bauen
    • react-three-rapier nutzt diesen Ansatz und passt besser zu React-Diffing
    • Ein auf useEffect basierender Ansatz löscht bei geänderten Abhängigkeiten die alte Instanz und erstellt eine neue
  • Reines DOM-Rendering

    • Machine wird vollständig im DOM gerendert
    • Anfangs wurde angenommen, dass man bei Performance-Grenzen zu PixiJS oder canvas wechseln könnte, aber der DOM-Ansatz wurde so weit wie möglich ausgereizt, weil dafür weniger gebaut werden musste
    • Für die Rendering-Performance schreibt der Frame-Loop die Styles der von der Physiksimulation betroffenen Widgets direkt
    • React-Diffing läuft nur, wenn sich die Struktur des Szenegraphen ändert
    • Anfangs wurden auch Kugeln mit React gerendert, doch die häufige Erzeugung und Löschung erhöhte die Diff-Kosten, weshalb ein eigener optimized renderer gebaut wurde
    • Für Kugeln und Widgets außerhalb des Bildschirms kommt Draw Culling zum Einsatz
    • Dieser Ansatz funktionierte gut mit 4.000 simulierten Kugeln und mehreren hundert Kugeln auf dem Bildschirm, sodass man beim reinen DOM-Rendering blieb

API, Moderation und Betrieb der Einsendungen

  • Das Backend wurde von davean und Kevin in Haskell geschrieben, mit Redis als Datenspeicher
  • Zum Teilen von Typen zwischen den Codebasen wurden OpenAPI und OpenAPI fetch verwendet
    • Anfangs war es etwas unbequem, sich an die Haskell-Typen anzupassen
    • Es half aber dabei, API-Änderungen in der Schlussphase zu koordinieren
  • TanStack Query war nützlich, um ohne Server-Push Caching und automatisches Refresh zu handhaben
  • Moderations-UI und Priorisierung

    • Die von Ed White entworfene Moderations-UI war ein Engpass, durch den alle Einsendungen vor ihrer Veröffentlichung mussten
    • Moderatorinnen und Moderatoren konnten für eine bestimmte Kachel aus Hunderten von Kandidaten-Designs wählen müssen
    • Die Priorisierung der Queue erfolgte über einen interestingness score je Widget-Typ; durch Zählen jeder Instanz wurden Kandidaten-Kacheln sortiert
    • Dieser Ansatz bevorzugt Lösungen mit vielen Elementen, doch Moderatorinnen und Moderatoren konnten in der Mitte der Liste gezielt nach minimalistischeren Lösungen suchen
    • Das große Ungleichgewicht zwischen der Zahl eingereichter Designs und der Zahl tatsächlich in der Maschine veröffentlichter Designs blieb ein bedauerlicher Punkt
    • Vor dem Launch wurde nach Wegen gesucht, mehr Backlog zu veröffentlichen, aber innerhalb der zeitlichen Grenzen der Moderation ließ sich kein guter Kompromiss finden
    • Nach dem Ende der Live-Einsendungen möchte das Team Wege finden, mehr vom Einsendungs-Datensatz zu teilen
  • Freigabe-Cooldown und Temporegelung

    • Weil die Qualität der Kachel-Snapshots wichtig war, blieb der Freigabe-Button für Moderatorinnen und Moderatoren deaktiviert, bis die Simulation mindestens 30 Sekunden gelaufen war
    • Dieser Cooldown half dabei, Snapshots eines stabilen Zustands zu erstellen und zu prüfen, ob die Ausgaben Kugeln in der erwarteten Rate erhalten
    • Zunächst wurde erwartet, dass Moderatorinnen und Moderatoren das lästig finden würden, doch es wurde positiv aufgenommen, weil es überhastete Entscheidungen verhinderte
    • Nach dem Launch kam ein Slider hinzu, mit dem Moderatorinnen und Moderatoren die Simulation deutlich schneller als in Echtzeit laufen lassen konnten
    • Damit ließen sich die ersten 30 Sekunden einer Einsendung in unter 5 Sekunden ansehen, und auch längere Abläufe wurden einfacher zu prüfen

Unbeabsichtigte Interaktionen zwischen Kacheln

  • Jamslunt Interfoggle“ war eine Vorrichtung, die in den ersten Stunden nach Veröffentlichung auftauchte und einen Mechanismus nutzt, der auf einem engen Bereich eines Lüfters beruht
  • Diese Vorrichtung sammelt blaue Kugeln in einem Kanal und lässt sie, sobald genug Gewicht zusammenkommt, zu beiden Seiten ausschütten
  • Das darüber platzierte „Bouncy“ ist eine Chaosmaschine, die Kugeln über einen dreifach verzweigten Kreuzweg abschießt
  • Bouncy schickt gelegentlich eine grüne Kugel an den falschen Ausgang, und diese Kugel löst den feststeckenden Haufen blauer Kugeln auf, wodurch in Interfoggle ein Kettenfluss entsteht
  • Da im Editor nur die korrekte Farbe eingespeist wurde, um die Eingänge verständlicher zu machen, konnte Interfoggle nicht mit diesem Verhalten der grünen Kugeln im Hinterkopf entworfen worden sein
  • Solche unbeabsichtigten Kombinationen wurden zu einem großen Reiz des Projekts: die kreative Art, wie Menschen auf einer geteilten Leinwand mit den Werkzeugen umgehen

Code und verbleibende Experimente

  • Der Quellcode von Machine ist im GitHub-Repository verfügbar
  • Eine Implementierung, die die gesamte Maschine global und vollständig simuliert, bleibt eine interessante Hacking-Aufgabe
  • Einen Link, um eigene Designs direkt zu Machine hinzuzufügen, gibt es bei xkcd 2916

1 Kommentare

 
GN⁺ 2024-05-09
Meinungen auf Hacker News
  • Was ich beim Lesen dieses Beitrags lustig fand: Damals hatte ich überhaupt nicht mitbekommen, dass so etwas passierte.
    Es schien keine Erklärung zu geben, was da vor sich ging, ich wusste nicht, dass es eine gemeinsame Erfahrung für alle war, und es fühlte sich einfach so an, als würden viele zufällige Dinge chaotisch passieren.
    Ich stellte ein paar Tiles fertig und reichte sie ein, weil ich dachte, das sei der Weg zur „nächsten Stufe“, und gab ihnen dumme Namen wie „test 1b“. Denn ich dachte, es sei Singleplayer und nur ich würde die Namen sehen.
    Nach ein paar Versuchen wurde es mir langweilig; beim Herumstöbern sah ich komplexe Dinge, wusste aber nicht, dass das Einreichungen waren, und hielt sie einfach für Startpunkte zum Lösen von Levels. Am Ende bin ich wohl auf den Aprilscherz hereingefallen.

    • Ich wusste nicht einmal, dass es interaktiv war, habe es nur angeschaut und gedacht: „cool“.
    • Man hätte von Anfang an editierbare Beispiele zeigen sollen, oder zuerst das Endergebnis zeigen und dann den Fokus auf freie Slots legen, um einen Call to Action zu setzen.
    • Zur Info: Einreichungen werden immer noch angenommen.
    • Bei mir war es noch viel schlimmer. Ich habe etwa zwei Minuten damit herumgespielt, überhaupt nicht verstanden, was los ist, und aufgegeben.
      Vielleicht lag es daran, dass ich das ursprüngliche Maschinen-Spiel, das als Vorlage diente, nie gespielt hatte :-)
    • Beim nächsten Mal wäre es gut, den Erklärungsbeitrag schon vor der Veröffentlichung zu posten.
  • Ich habe anscheinend rapier gekillt, als ich massenhaft „bonk“-Elemente hinzugefügt habe.
    Uncaught Error: recursive use of an object detected which would lead to unsafe aliasing in rust
    at jt (rapier_wasm2d_bg.js:4836:11)
    at 4ea5626ea4b1e4145572.module.wasm:0xf061c
    at 4ea5626ea4b1e4145572.module.wasm:0xf0638
    at 4ea5626ea4b1e4145572.module.wasm:0xb5e7b
    at H.remove (rapier_wasm2d_bg.js:1051:14)
    at l.remove (collider_set.js:87:18)
    at y.removeCollider (world.js:343:28)
    at PhysicsContext.tsx:258:15
    Trotzdem macht es wirklich Spaß, und ich finde es schade, dass ich nicht davon wusste, als es live offen war. Es wäre wirklich großartig, wenn man auch für einzelne von Leuten gebaute Maschinen Permalinks erstellen könnte.
    Mir ist klar, dass es Speicherprobleme geben könnte, aber ginge es nicht, das JSON base64-codiert in einen URL-Parameter zu packen? Ich möchte seltsame Maps bauen und mit anderen teilen.

    • Es ist noch offen: https://xkcd.com/2916/
      Für Maschinen, die in die öffentliche Gesamtversion aufgenommen wurden, gibt es Permalinks, aber nicht für einzelne Kreationen, die in der Moderationswarteschlange nicht ausgewählt wurden.
      Diese Entscheidung wurde bewusst getroffen, um das Risiko zu vermeiden, nicht moderierte nutzergenerierte Inhalte auf der Comic-Domain zu hosten.
  • Zur Info: Auf HN wurde dieses Thema auch am 6. April gepostet und hatte 14 Kommentare.
    https://news.ycombinator.com/item?id=39953514

  • „Es gab keinen Anreiz, sorgfältig zu überlegen, wohin man die Sticker klebt. Die Spieler hatten durch einzelne Aktionen nicht genug Handlungsmacht, um die Geschichte voranzutreiben. Dadurch beschränkte sich die Kreativität auf simple Muster wie das Wiederholen ähnlicher Sticker als Tiles oder das Ziehen von Linien.“
    Ach, das Spiel ist also zum Arbeitsleben in einem Großkonzern geworden.

  • Ich habe mitgemacht, als es veröffentlicht wurde. Ich glaube, ich habe etwa eine Stunde damit verbracht, meine Maschine so zuverlässig wie möglich zu machen, damit die richtigen Kugeln zu den richtigen Ausgängen gelangen.
    Nachdem ich sie eingereicht und die Seite neu geladen hatte, stand an der Stelle das Gerät einer anderen Person. Zugegeben, es war hübscher, aber weniger zuverlässig.
    Ich hätte mir gewünscht, dass früher klar gewesen wäre, dass es so funktioniert. Und offenbar war ich nicht der Einzige, der nicht wusste, dass die Liste der Bausteine scrollbar ist.

    • Ich habe gerade zwei eingereicht und hatte absolut keine Ahnung, dass man die Liste scrollen kann.
      Jetzt habe ich keine Energie mehr, zurückzugehen und nachzusehen :(
  • Wow, ein Freund und ich hatten 2014 dieselbe Idee und haben sie für Ludum Dare umgesetzt: https://nickfa.ro/wiki/CoinSlot
    Schön zu sehen, dass die Idee in einer ausgefeilteren und gut funktionierenden Form erschienen ist.

  • Das erinnert mich an meine Jugend. Ich habe damit wirklich sehr viel Zeit auf sehr vergnügliche Weise verschwendet.
    https://www.myabandonware.com/game/the-incredible-machine-1m...

    • Im Beitrag steht auch, dass dieses Projekt davon inspiriert wurde.
  • Ich habe wohl etwas verpasst: Warum scheinen bestimmte Elemente in der Maschine nur auf Kugeln bestimmter Farben zu wirken?
    Ich vermute, das soll verhindern, dass die Farben völlig durcheinandergeraten, aber im Beitrag scheint das nicht erklärt zu werden.

    • Jeder Kugel wurden unterschiedliche physikalische Eigenschaften gegeben.
      Gelbe Kugeln sind leicht und haben hohen Luftwiderstand, grüne Kugeln sind schwer, rote Kugeln springen sehr stark.
      So kann man physische Sortierer entwerfen.
  • Es wäre schön, wenn es eine einfache Möglichkeit gäbe zu prüfen, ob eine meiner Maschinen in der finalen Version gelandet ist.
    Für das nächste Design wäre es vielleicht gut, die Titel früherer Einreichungen irgendwo wie im Local Storage zu speichern und eine Benachrichtigung anzuzeigen.