von Andy Pillip

Das Geräte-Puzzle

Mein erstes Node.js Projekt ist ein Puzzle aus mobilen Geräten

Mobile Geräte so aneinander legen, dass sich ein Bild ergibt – das war mein Beitrag zum Obacht! 2015, der Kunst-Ausstellung in Haidhausen. Entwickelt mit Node.js, Socket.io und einem Bin-Packing-Algorithmus.

Ich hab' viel dabei gelernt und möchte die Erkenntnisse gerne hier teilen.

Die teilnehmenden Geräte müssen eine Webseite besuchen – also den Node.js Server – mit der sie dann permanent in Verbindung bleiben.

Beim Verbinden jedes neuen Gerätes werden alle Geräte angewiesen, sich ein neues Puzzlebild abzuholen. So lässt sich das Puzzle theoretisch mit beliebig vielen Geräten spielen.

Für die Entwicklung und zum Überprüfen, ob man das Puzzle richtig gelöst hat, kann man z.B. mit einem Laptop die Lösungs-Seite besuchen, die die Puzzleteile auf dem Gesamtbild zeigt und durch­nummeriert. Tippt man jedes teilnehmende Gerät einmal an, zeigen alle Geräte ihre Nummer, was die Überprüfung der Lösung vereinfacht. Aber natürlich sollte das Puzzle-Motiv selbst schon gut zu erkennen sein.

Node.js und Socket.io

Für node.js als Server habe ich mich entschieden, weil es damit sehr einfach ist, in fast Echtzeit parallel mehrere Clients zu bedienen.

Socket.io erlaubt dem Server, HTTP-Clients über neue Ereignisse zu informieren. Meldet sich zum Beispiel ein neues Gerät an, werden alle Geräte angewiesen, sich ein neues Puzzleteil zu holen.

Normalisierung der Auflösung

Ein Pixel ist kein Pixel ist kein Pixel beschreibt das Problem sehr gut: Web­entwickler können die physikalische Größe z.B. eines Bildes auf verschiedenen Geräten gar nicht direkt kontrollieren.

Würde man einfach ein Bild in sagen wir 300 Pixel Breite auf einer Webseite einbinden, und würde sie auf zwei unter­schiedlichen Geräten vergleichen, dann wäre das Bild unterschiedlich groß.

Damit meine Puzzleteile aber aneinander passen, benötige ich gleich große Bilder. Ich muss die Auflösung normalisieren, bevor ein Algorithmus das komplette Puzzlemotiv auf die Geräte aufteilen kann.

Ausschlaggebend für die Korrektur sind die CSSPPI, die CSS Pixel per Inch, also dem Verhältnis der in CSS angegebenen Pixel zur physikalischen Größe auf dem Bild­schirm.

Die CSSPPI lassen sich berechnen aus den für viele Geräte bekannten Device Pixel per Inch (DPPI), geteilt durch die ebenfalls bekannte devicePixelRatio.

Das Samsung Galaxy S4 hat beispiels­weise eine Auflösung von 441 DPPI, und rechnet CSS-Pixel im Verhältnis von 3 um. 441:3 = 147 CSSPPI.

Leider lassen sich die DPPI im Browser nicht abrufen, deshalb muss ich diesen Wert für jedes teilnehmende Gerät nachschlagen, wofür ich in meinem Puzzle eine Tabelle angelegt habe.

Berücksichtigung des Geräterahmens

Auf dem Lösungsbild sind die teilnehmenden Geräte mit nummerierten, roten Rahmen gekennzeichnet.Smartphones und Tablets bestehen ja nicht nur aus Bildschirm, sondern haben immer auch einen Rahmen zum Festhalten drum herum.

Verwendet man sie als Puzzleteile, kann man die Bilder des Puzzles nicht direkt aneinander legen, weil der Ramen dazwischen ist.

Auch die Rahmen­dicke kann man im Browser nicht abfragen, weshalb ich alle teil­nemenden Gerätemodelle mit dem Meter­stab ausmesse.

Auch die Rahmen­stärke wird mit dem CSSPPI-Faktor belegt. Der Server kennt dann zu jedem angemeldeten Gerät ein Rechteck, das den Rahmen mit abbildet. Die Aufteilung des Puzzle­motivs auf die Geräte passiert dann mit diesem äußeren Rechteck.

Für mehr Varianz werden die Recktecke mit ½ Wahrscheinlichkeit um 90° gedreht, so dass die Spieler dann auch das Gerät drehen müssen.

Erst bei der Auslieferung des Teil-Bildes an das Gerät wird der Ramen wieder weg­gerechnet.

Vollbild mit der Fullscreen-API

Browser haben initial ja auch eine Title-Bar, die man wie den Rahmen vom Puzzle-Teil wegrechnen müsste. Für das Auge funktioniert das aber nicht mehr gut. Während sich der einfarbige Rahmen leicht wegsieht, stört die Browserbar gewaltig.

Meine Lösung ist, auf das erste Tippen den Browser in den Vollbild-Modus zu schalten. Automatisch geht das nicht, weil die Fullscreen-API den Aufruf nur durch eine Nutzerinteraktion erlaubt.

Das ist also eine Aktion, die ich einmalig beim Anmelden eines neuen Gerätes machen muss.

Gerätemodelle erkennen

Für Responsive Web-Apps oder Webseiten ist es keine gute Idee, vom Geräte­modell auf irgend­welche Funktionen oder Eigen­schaften zu schließen – diese hängen schließlich auch von der Software-Version ab, die Menge an Geräten ist schlichtweg unüberschaubar und es ist nicht möglich, so eine Ent­scheidungs­tabelle aktuell zu halten.

Feature Detection ist das richtige Schlüssel­wort.

In meinem speziellen Fall muss ich aber zwei Größen in das Puzzle ein­fließen lassen, die sich nicht vom Browser ermitteln lassen: die CSSPPI und die Rahmen­stärke des Geräts.

Dafür muss ich leider jedes Modell, das teil­nehmen soll, einmalig aus­messen und die CSSPPI ermitteln. Die Werte trage ich dann in eine Tabelle ein. Über einen regulären Ausdruck erkennt der Server dann anhand des User Agent Strings das Gerät.

Dabei ist mir klar, dass man zum Beispiel die iPhone-Version so nicht erkennen kann, neuerdings haben ja auch die unterschiedliche Bild­schirm­größen.

Um überprüfen zu können, ob die Puzzle­teile auch in der richtigen Größe auf den Bild­schirmen landen, habe ich ein Prüfbild mit Schachmuster erstellt. So lassen sich alle Geräte aneinander legen, die Quadrate müssen gleich groß sein.

Aufteilung des Puzzles analog zum Behälter­problem

Abstrahiert lässt sich die Aufteilung eines Bildes auf verschiedene Geräte folgender­maßen beschreiben:

Wie kann man unterschiedlich große Rechtecke so aneinander legen, dass eine möglichst kleine Fläche ohne Lücken entseht?

Damit sind wir fast beim Behälterproblem oder bin packing problem, das sich in der Web­entwicklung auch beim Generieren von Sprites stellt, oder in Masonry-Layouts.

Zwar gab es deshalb verschiedenste NPM-Pakete, die irgendwie ein bin-packing implementieren, leider hat fast keines eine direkte Schnittstelle zum Algorithmus angeboten. Viele konnten sogar nur auf dem DOM arbeiten.

Nachdem bin-packing NP-schwer ist, fällt auch die Lösung der jeweiligen Algorithmen unterschiedlich gut aus. Das bedeutet für die Spieler auch unterschiedlich schwer zu lösende Puzzles. Am Ende hat boxpack für mich die besten Ergebnisse gebracht.

Lustig dabei ist natürlich, dass man selbst beim Entwickeln nicht weiß, wie das Puzzle zusammen gehört.

Praxistest

Im Juni 2015 haben wir das Puzzle auf Kultur im Quartier ausgestellt und ausprobiert. Um das Puzzle gut in die Ausstellung zu integrieren, habe ich die ausgehängten Gemälde von Thomas fotografiert und als Puzzle­motive verwendet.

Das hat das Puzzle auch einfacher gemacht, weil die Spieler manchmal mit den Geräten zu den Gemälden gelaufen sind, um den Ausschnitt zu finden.

Gespielt haben wir mit folgenden Geräten

  • Samsung Galaxy S4
  • Nexus 7 2012
  • iPhone 3
  • iPhone 4
  • HTC Windows Phone 8S
  • Samsung Galay S3 Neo
  • HTC Legend
  • HTC One M8
  • iPad
  • Galaxy Tab 1
  • Sony Xperia acro
  • LG G-Pad 8.3
  • Kobo Glo N613 (eReader)
  • Ubuntu Phone

Energie sparen vs. zig Minuten puzzeln

Der Web­browser ist nicht dafür gemacht, viele Minuten lang an zu bleiben. Die Zeit bis zum Bildschirm-abdunkeln lässt sich noch hoch genug stellen. Aber einige Browser/Plattformen haben eine Logik zum Energie sparen eingebaut, die die Verbindung zum Server trennt.

Ergebnis im Puzzle ist, dass sich plötzlich alle anderen Geräte ein neues Puzzle­bild holen. Das betroffene Gerät zeigt nach wie vor das alte Puzzleteil, spielt aber nicht mehr mit. Leider stört das den Puzzle­spaß extrem, die Teilnehmer verlieren das Interesse wenn so etwas passiert.

Verkabelt

Gerade in der Ausstellungs-Situation ist das Problem, dass immer wieder Leute vorbeischauen und kurz das Puzzle ausprobieren. Müsste man alle Geräte erst anschalten, wäre das für viele Besucher nicht interessant. Lässt man die Geräte den ganzen Tag an, braucht das Puzzle recht viel Energie, so dass man Geräte zum Wechseln braucht.

Ein riesiger Vorteil dafür war aber, dass ich beliebig Geräte aus dem Puzzle heraus­nemen kann, indem ich den Browser schließe. Der Server verteilt dann ja neue Teile an alle anderen Geräte.

Ausblick

Was hab' ich nicht mehr geschafft beziehungsweise welche Ideen gab es noch?

Geräte-Erkennung automatisieren

Es wäre noch cool wenn jeder Spieler sein eigenes Handy dazu legen könnte, oder?

Dafür müsste aber das Ausmessen von Rahmen und Bildschirm wegfallen – was sogar geht:

Der Internet-Dienst WURFL kann über den User Agent String anhand einer sehr um­fang­reichen Datenbank das Geräte­modell erkennen, und hat viele Details zum Gerät gespeichert – zum Beispiel auch die physikalischen Maße des Rahmens und des Bildschirms. Darüber ließen sich auch die CSSPPI bestimmen.

Bilder von Twitter laden

Um das Spiel noch mehr in die Gesamt­veranstaltung ein­zu­binden, die ja im ganzen Quartier statt fand, hätte man beim Neu-Generieren des Puzzles das jeweils letzte Bild zum Veranstaltungs-Hashtag von Twitter benutzen können. Allerdings gab es gar kein Hashtag.