Ein Stacker. Offline-first. Ohne E-Mail.
Browser-Arcade mit Cloud-Sync, Events und globalem Leaderboard — Vanilla JS, LocalStorage als Source-of-Truth, Supabase Free-Tier als additive Ebene. Nickname + PIN statt E-Mail-Konto.

Warum es das gibt.
Die Ketchapp-Schule des Stack-Towers hat das Genre definiert und dann in Werbe-Interstitials vergraben. Wer heute „Stack“ spielen will, bekommt proprietäre App-Store-Titel mit intrusiver Monetarisierung, keinem Gerätewechsel-Support und Leaderboards, die kaum mehr als Dekoration sind.
Stack Surge bringt die Mechanik zurück in den Browser — instant spielbar via URL, als PWA installierbar, ohne E-Mail und ohne App-Store-Zwang. Fortschritt synct optional, Achievements, Daily Challenges und Quest Chains laufen lokal, Leaderboard global. Das Design-Ziel: zwei Taps entfernt von der ersten Runde, der Rest passiert im Hintergrund.
Der Rahmen, in den es passen musste.
Wie es läuft.
◦ Schematische Darstellung, Zahlen illustrativ.
Vier bewusste Entscheidungen.
Vanilla JS + Canvas statt React / Three.js / Phaser.
GameLoop mit Delta-Time, modulare Systems/EntitiesSupabase + RLS statt eigenes Node-Backend.
supabase/security.sqlLocalStorage als Source-of-Truth, Cloud nur additiv.
storage als primärer State, SyncService reconciled später gegen Supabase — Records via Math.max, Collections als UnionNickname + PIN statt E-Mail-Auth.
Was nicht offensichtlich war.
GradientCache gegen GC-Stottern
BackgroundSystem pro Frame dutzende createLinearGradient-Aufrufe — auf alten Androids mördert das den GC, FPS kollabieren.Lösung: ein
GradientCache mit Key aus Farben + Dimensionen. Invalidierung nur bei Theme-Wechsel. Zusammen mit einem ParticleSystem-Object-Pool (Swap-and-Pop statt Array.splice: O(1) statt O(n)) hält der Fever-Mode auf meinen Low-End-Testgeräten die 60 FPS.Anti-Cheat ohne autoritativen Server
(s, b, p, d, t) + ein Salt aus dem Bundle. Die Supabase-RPC submit_score speichert die Prüfsumme und führt eigene Plausibilitätschecks durch: Score-Cap pro Block, min. Zeit pro Block, perfects ≤ blocks, streak ≤ perfects, Rate-Limit 5 Submissions / 5 min pro Nickname.Keine perfekte Lösung — der Salt liegt im Bundle, Reverse-Engineering ist möglich. Aber: genug, um den Fire-and-Forget-Cheat vom öffentlichen Board fernzuhalten, ohne Game-State auf einen Server zu zwingen. Pragmatisch kalibriert gegen die Angreifer, die tatsächlich auftauchen.
Seasonal-Events als Daten, nicht Code
src/data/themes/{standard,premium,seasonal}/ — pro Theme ein ESM-Modul mit Color-Phases, Effect-Flags und -Counts. Der SeasonalEventManager detektiert das Datum, aktiviert Themes + ThemeReactions automatisch — fliegende Fledermäuse zu Halloween, Schneeflocken zu Weihnachten.Neues Event = neue Theme-Datei + ein Eintrag in
SeasonalEvents.js mit Datumsfenster. Kein Code-Change am Renderer, keine Deploy-Koordination. Die Grenze zwischen Content-Change und Code-Change bewusst gezogen und bewahrt: was Autoren und was Entwickler anfassen, ist seit Tag 1 getrennt.Reconcile mit max() und Union
SyncService zieht beim Login den Cloud-State, mergt gegen LocalStorage und schreibt das Ergebnis zurück. Strategie pro Feld: bestScore und Statistik-Counter via Math.max, inventory und achievements als Union, equipped und settings beim ersten Sync aus der Cloud, danach local-first. coins beim Restore-Sync via Math.max, im aktiven Spiel local — sonst würde Cloud bereits ausgegebene Coins zurückspielen.Konsequenz: wer auf drei Geräten spielt, verliert keinen Fortschritt — Hochwasser-Marken bleiben konsistent, Käufe und freigeschaltete Inhalte addieren sich, die zuletzt aktive Session bestimmt den Loadout-State. Schmal genug, dass die Konflikt-Cases im Code direkt sichtbar bleiben — keine versteckte Last-Write-Wins-Magie, die später jemand versehentlich „optimiert“.
Was läuft.
Was ich gelernt habe.
Offline-first ist ein Feature, kein Fallback.
LocalStorage als Source-of-Truth hat den ganzen UX-Flow entkoppelt — kein Loading-Spinner, keine „Verbindung wird hergestellt“-Blocker, kein Geräusch, wenn das Netz wackelt. Der Sync-Pfad ist eng, getestet und hat klare Fehler-Grenzen. Das nehme ich in jede App mit, die irgendwo zwischen „rein lokal“ und „rein vernetzt“ lebt.
Backend-as-a-Service, bis es wehtut.
Free-Tier-Supabase hat mich komplett von Ops befreit — keine VMs, kein nginx, kein pg_dump-Cronjob. RLS ersetzt einen API-Server, den ich sonst selbst hätte bauen müssen. Die Grenze ist klar: sobald ich custom Realtime-Game-State brauche, geht das nicht mehr. Aber bis dahin: jeder Solo-Dev sollte das als Default evaluieren, bevor er wieder einen Node-Server containerisiert.