Teil 7 · Gamifying Learning: 25 Badges, Idle-Gap Sessions und eine 90-Tage-Heatmap
Teil einer Serie über die Entwicklung von Gemma CogniVault. Zuvor: The mindmap renderer — hand-rolled SVG to React Flow.
Alle Abkürzungen werden im Anhang unten auf der Seite vollständig erklärt.
Ich habe acht Jahre lang ICT an weiterführenden Schulen unterrichtet, bevor ich zum Full-Stack-Developer gewechselt bin. Die zuverlässigste Lektion aus dieser Zeit war unangenehm einfach: Schüler, die beständig da waren, haben gelernt. Die anderen nicht. Talent, Vorwissen, sogar die Motivation an einem bestimmten Tag – all das war der bloßen Anwesenheit nachgelagert.
Der “Dashboard”-Tab in CogniVault ist ein kleiner Versuch, genau das zu fördern. Es ist keine Duolingo-Streak-Panikmaschine. Es sind drei Dinge:
- Hero-Statistiken – gesamte Lernzeit, gesamte Sessions, aktueller Streak.
- 25 Erfolgs-Badges – automatisch erfasst über Chat, Quizzes, Workshops, Flashcards und Mindmaps hinweg.
- Eine 90-Tage-Aktivitäts-Heatmap – im GitHub-Stil, mit fünf lila Intensitätsstufen.
Das Ganze besteht nur aus einem kleinen Satz von SQLite-Tabellen und ein paar React-Komponenten. Der interessante Teil ist aber nicht der Code – es sind die Designentscheidungen.
Idle-Gap Sessions
Die schwerste Frage klang eigentlich am einfachsten: Was zählt als eine Lern-Session?
Die naive Antwort ist: “Alles, was zwischen dem Öffnen und Schließen der App passiert.” Aber das ist falsch. Leute lassen Tabs offen. Leute gehen für eine Stunde weg und kommen dann wieder. Leute öffnen die App um 9 Uhr morgens, machen nichts und schauen um 14 Uhr wieder rein.
Die Antwort, bei der ich gelandet bin: Eine Session endet, wenn du 15 Minuten lang inaktiv warst. Stellst du eine Frage und bist dann 16 Minuten inaktiv – das ist eine Session. Kommst du zurück und stellst eine weitere Frage – beginnt eine neue. Der Schwellenwert ist über STUDY_SESSION_IDLE_GAP_SECONDS=900 konfigurierbar.
Die Uhr richtet sich nach den Chat-Nachrichten – dem konversationellen Kern des Lernens in CogniVault. Jede Nachricht verlängert entweder die offene Session (indem sie den ended_at-Zeitstempel und die Nachrichtenanzahl erhöht) oder, falls die Pause seit der letzten Aktivität den Schwellenwert überschreitet, schließt sie diese implizit ab und eröffnet eine neue:
# Simplified from backend/services/progress_tracker.py
def record_message(now: float, idle_gap: int):
last = most_recent_session()
if last and (now - last.ended_at) <= idle_gap:
extend(last, ended_at=now) # same session continues
else:
open_session(started_at=now, ended_at=now) # new session begins
Zwei Schreibvorgänge pro Nachricht. Die Dauer einer Session ist ended_at - started_at, was bedeutet, dass “Gesamtzeit” die aktive Zeit widerspiegelt, nicht “hatte einen Tab offen”. Das ist die einzige Zahl, die wirklich etwas aussagt. (Aktionen im Study Hub – Quizversuche, Karteikarten umdrehen, Mindmap-Exporte – werden als eigene Events erfasst und fließen in die Badge-Metriken unten ein; die Session-Uhr selbst bleibt nachrichtengetrieben und ehrlich.)
25 Badges, nicht 250
Die meisten gamifizierten Apps überfluten dich regelrecht mit Erfolgen. Es gibt einen Grund dafür: mehr Badges, mehr Dopamin, mehr täglich aktive Nutzer. Der Preis dafür ist, dass jedes Badge weniger bedeutet – irgendwann wird die ganze Schicht nur noch zur Tapete.
Ich habe CogniVault auf 25 limitiert, aufgeteilt auf die fünf Aktivitätsbereiche:
- 10 für Chat & Lerngewohnheiten (erste Frage, 10 Nachrichten an einem Tag, 100 insgesamt, eine Stunde Gesamtlernzeit, 3- und 7-Tage-Streaks, eine 30-minütige Deep-Dive-Session, Night-Owl- und Early-Bird-Sessions, erste Nutzung des Scope-Filters)
- 4 für Quizzes (erstes Quiz, perfektes Ergebnis, Bestehen auf fortgeschrittenem Schwierigkeitsgrad, 10 Quizzes)
- 4 für Workshops (erste Outline, erste abgeschlossene Lektion, erster abgeschlossener Workshop, 5 abgeschlossen)
- 4 für Flashcards (erstes Deck, 50 umgedrehte Karten, ein Deck komplett gemeistert, 5 Decks)
- 3 für Mindmaps (erste Mindmap, erster Export, 5 Mindmaps)
Jedes Badge hat ein einzeiliges Freischaltkriterium, das bei relevanten Events automatisch ausgewertet wird. Nichts Manuelles, nichts, was der Nutzer “einfordern” muss. Sie tauchen einfach auf.
Und die Definitionen sind gar kein Code – sie sind Daten. Alle 25 leben in einer JSON-Datei. Jeder Eintrag benennt die Metrik, die er beobachtet, und das zu erreichende Ziel:
{
"code": "card_reviewer",
"name": "Card Reviewer",
"icon": "🃏",
"metric": "total_card_flips",
"target": 50
}
Ein einzelner Evaluator liest die aktuellen Statistiken aus, vergleicht jede Definition mit ihrer Metrik, gleicht sie mit bereits verdienten Badges ab und fügt neue Freischaltungen in die progress.db ein. Ein 26. Badge hinzuzufügen, bedeutet, einen JSON-Eintrag hinzuzufügen, nicht neue Logik zu schreiben. Mehrere Badges bilden Leitern – jedes weiß, welches Badge das “nächste Level” ist, was den Schubs in der Detailansicht zum nächsten Ziel antreibt.
Die Heatmap
Auf die 90-Tage-Heatmap bin ich am meisten stolz, und sie ist gleichzeitig das Einfachste. Es ist ein 13×7-Raster aus Zellen, eine pro Tag, farblich markiert nach der Gesamtlernzeit an diesem Tag.
Fünf Intensitätsstufen:
level 0 — no activity
level 1 — under 15 minutes (a quick check-in)
level 2 — 15-60 minutes (a focused session)
level 3 — 1-3 hours (substantial study)
level 4 — 3+ hours (a marathon)
Die Daten sind konzeptionell eine einzige Aggregation über die Sessions-Tabelle:
SELECT date(started_at) AS day,
SUM(ended_at - started_at) AS seconds
FROM study_sessions
WHERE started_at >= date('now', '-90 days')
GROUP BY day;
Das Backend füllt die fehlenden Tage mit Nullen auf, sodass das Frontend immer genau 90 Einträge erhält. Eine kleine clientseitige Funktion ordnet die Tagessummen in die fünf Level ein. Wenn du auf eine beliebige Zelle klickst, öffnet sich ein DayDetailModal mit den Zahlen dieses Tages – Lernzeit, Sessions, Nachrichten – sowie allen Badges, die an diesem Tag verdient wurden.
Der Grund, warum ich diese Komponente liebe: Sie macht die Textur einer Lerngewohnheit sichtbar. Streaks sind toll, aber ein Streak ist nur eine Zahl. Eine Heatmap zeigt dir, dass du am Wochenende härter lernst, oder dass du den ganzen Monat über langsam abgebaut hast, oder dass die Lücke zwischen deinem letzten “Level 4-Tag” und heute größer ist, als du dachtest. Sie spiegelt etwas wider, worauf der Nutzer reagieren kann.
Was ich bewusst weggelassen habe
Drei Dinge, die du in den meisten gamifizierten Apps finden würdest, fehlen in CogniVault absichtlich:
Streak-Panik. Kein “Dein Streak ist in Gefahr!"-Popup. Keine Regeln für Streak-Freezes. Keine gelben Ausrufezeichen. Der Streak wird angezeigt – das ist der gesamte Feedback-Loop. Wenn ein Nutzer seinen Streak bricht, dann bricht er ihn eben. Erwachsene brauchen keine Shaming-UX.
Leaderboards. Das ist eine lokale Einzelnutzer-App. Es gibt keinen globalen Vergleich. (Und den sollte es auch nicht geben – Leaderboards optimieren beim Lernen das Falsche.)
Konfetti, Fanfaren, Push-Benachrichtigungen. Ein neu verdientes Badge taucht auf dem Quiz-Ergebnisbildschirm und im Dashboard-Raster auf. Das ist die ganze Feier. Alles, was größer ist, stiehlt dem Nutzer die Aufmerksamkeit zum Nutzen der App, nicht zu seinem eigenen.
Das allgemeine Prinzip: Miss, was wichtig ist, mach es sichtbar, aber ohne zu nerven. Registriere, dass du wiedergekommen bist. Spiegele dir das wider. Tu nicht so, als ob es dich mehr interessiert, als es das tatsächlich tut.
Was das Dashboard nicht zu optimieren versucht
Eine häufige Falle bei diesen Dashboards ist die umgekehrte Kausalität: Der Nutzer fängt an, die Metrik zu spielen, anstatt die eigentliche Sache zu tun. Ein täglicher Fragenzähler zum Beispiel führt dazu, dass Nutzer eine irrelevante Frage pro Tag stellen, um ihren Streak am Leben zu halten.
Deshalb ist die Hürde absichtlich an genau einer Stelle sehr niedrig und überall sonst hoch. Es gibt ein Badge, das keinen Aufwand erfordert – “Erste Frage”, das man für die allererste Nachricht bekommt –, weil jedes Spiel eine Einstiegsrampe braucht, die beweist, dass das System funktioniert. Danach werden die Metriken schwer zu manipulieren, ohne die eigentliche Arbeit zu tun:
- Gesamtlernzeit – sammelt sich nur während aktiver Nutzung an, mit Idle-Gap-Abschaltungen.
- Sessions – um mehr hinzuzufügen, muss man tatsächlich separate Arbeitsphasen starten.
- Badges – fast alle erfordern Tiefe (100 Nachrichten, ein Quiz meistern, ein Deck perfekt beherrschen, 5 Workshops abschließen), nicht nur oberflächliches Antippen.
- Heatmap-Intensität – erfordert anhaltendes Engagement an einem bestimmten Tag.
Implementierung: bewusst klein
Der Gamification-Kern besteht aus drei SQLite-Tabellen –
study_sessions (id, started_at, ended_at, message_count)
message_events (id, sent_at, session_id, had_scope_filter, had_attachments)
achievements_earned (code, earned_at)
— plus die JSON-Badge-Definitionen, ein Evaluator-Modul und eine Handvoll React-Komponenten (SummaryCards, AchievementGrid, ActivityHeatmap, DayDetailModal). Die gleiche progress.db-Datei hat mittlerweile weitere Tabellen für die gespeicherten Quizzes, Workshops, Decks und Mindmaps des Study Hubs bekommen – aber die Badge-und-Session-Maschinerie selbst ist nur ein paar hundert Zeilen lang geblieben.
Daran ist nichts Ausgefallenes. Das Dashboard funktioniert, weil die Designentscheidungen richtig sind, nicht weil die Implementierung raffiniert ist.
Fazit
Wenn du ein Lern-Tool baust – oder ein beliebiges Tool, das von den Gewohnheiten der Nutzer lebt –, dann setze Gamification bewusst ein. Wähle die Metriken, die das widerspiegeln, was du wirklich fördern willst. Begrenze die Anzahl der Erfolge. Verzichte auf die Streak-Panik-UX. Mach die Textur der Nutzung sichtbar, ohne dass die App verzweifelt wirkt.
Oder direkter gesagt: Bau kein Duolingo. Bau ein Dashboard, auf das der Nutzer ab und zu schaut und das er dann wieder schließt, mit dem leichten Gefühl, weitermachen zu wollen. Das ist der ganze Job.
Anhang: Abkürzungen in diesem Beitrag
| Abkürzung | Vollform | Bedeutung |
|---|---|---|
| UX | User Experience | Wie sich das Produkt anfühlt – genau das, was durch Streak-Panik-Mechaniken geopfert wird |
| ICT | Information and Communications Technology | Das Fach, das ich acht Jahre lang unterrichtet habe, bevor ich zum Full-Stack gewechselt bin |
| SQLite | (SQL = Structured Query Language) | Eine komplette relationale Datenbank in einer einzigen Datei, progress.db |
| JSON | JavaScript Object Notation | Das Datenformat, in dem die 25 Badge-Definitionen liegen |
| UI | User Interface | Die Dashboard-Oberfläche: Statistiken, Raster, Heatmap |
Als Nächstes: Testing a local-AI app — 351 tests, mocked Ollama, zero infrastructure.
