Das Ziel
Beim Zuhoeren den Faden verlieren – das kennt jeder. Man scrollt nebenbei, der Blick wandert, und ploetzlich weiss man nicht mehr, wo der Sprecher gerade ist. Die Loesung: Der aktive Absatz wird automatisch hervorgehoben, waehrend man zuhoert. Der Text folgt der Stimme.
So sieht das im Browser aus, wenn jemand einen OKAYPLAY-Artikel abspielt:
Mulchen: Der stille Held im Market Garden
Mulchen klingt nicht spektakulaer. Kein fancy Werkzeug, keine Hightech-Loesung – einfach nur eine Schicht organisches Material auf dem Boden. Und doch war Mulch im FREILANDWERK einer unserer wichtigsten Verbuendeten.
Was Mulch wirklich leistet
Eine gute Mulchschicht aus Stroh, Grasschnitt oder gehaeckseltem Laub haelt die Feuchtigkeit im Boden, unterdrueckt Unkraut und fuettert die Bodenlebewesen. In heissen Sommern war der Unterschied zwischen gemulchten und offenen Beeten dramatisch.
Die richtige Schichtdicke
Wir haben mit 5 bis 10 Zentimetern gearbeitet …
Der bereits gelesene Absatz ist leicht ausgeblendet. Die aktive Stelle – Ueberschrift und Absatz – bekommt einen sanften gelben Hintergrund und einen Akzent-Strich links. Der Rest wartet. Kein Raten, kein Suchen. Einfach mitlaufen.
Zwei Systeme, ein Ergebnis
Im letzten Post haben wir beschrieben, wie OKAYPLAY Absatz-Timestamps waehrend der Aufnahme erzeugt – per Web Speech API direkt im Browser des Sprechers. Das funktioniert, hat aber eine Einschraenkung: Die Granularitaet ist absatzweise, und die Qualitaet haengt vom Browser ab.
Deshalb gibt es eine zweite Schicht: Deepgram Nova-2, ein Speech-to-Text-Modell, das die fertige Audiodatei serverseitig analysiert und praezise Wort-Timestamps zurueckliefert. Aus diesen Wort-Timestamps baut OKAYPLAY automatisch Satz-Timestamps – die Basis fuer das Highlighting im Player.
im Browser
serverseitig
per Overlap
per currentTime
Deepgram: Von Audio zu Wort-Timestamps
Sobald ein Sprecher seine Aufnahme hochlaedt, wird die Audiodatei asynchron an die Deepgram-API geschickt. Das Modell: Nova-2, optimiert fuer deutsche Sprache. Zurueck kommen einzelne Woerter mit Start- und Endzeit – auf Hundertstelsekunden genau.
// Deepgram-Antwort (vereinfacht)
{
"transcript": "Mulchen klingt nicht spektakulaer...",
"confidence": 0.99,
"words": [
{ "word": "mulchen", "start": 1.20, "end": 1.68 },
{ "word": "klingt", "start": 1.76, "end": 2.14 },
{ "word": "nicht", "start": 2.16, "end": 2.40 },
// ... hunderte weitere Woerter
]
}Jetzt kommt der entscheidende Schritt: Diese Wort-Liste muss den Absaetzen des Originalartikels zugeordnet werden. Denn Deepgram kennt den Artikeltext nicht – es liefert nur, was es hoert.
Fuzzy Matching: Gesprochenes trifft Geschriebenes
Die Zuordnung laeuft in drei Schritten: Pausen erkennen, Chunks bilden, Chunks den Absaetzen zuordnen.
Schritt 1: Natuerliche Pausen finden
Zwischen Absaetzen macht jeder Sprecher eine kurze Pause. Wenn der zeitliche Abstand zwischen zwei Woertern groesser als 0,8 Sekunden ist, wird an dieser Stelle ein neuer Chunk gestartet. So entstehen Audio-Segmente, die typischerweise einem gesprochenen Absatz entsprechen.
// Pause Detection
for ($i = 1; $i < count($words); $i++) {
$gap = $words[$i]['start'] - $words[$i-1]['end'];
if ($gap >= 0.8) {
// Neuer Chunk: Sprecher hat pausiert
$chunks[] = [
'start' => $words[$chunkStart]['start'],
'end' => $words[$i-1]['end'],
];
$chunkStart = $i;
}
}Schritt 2: Wort-Overlap berechnen
Jeder Chunk wird gegen alle Artikelabsaetze verglichen. Die Woerter werden normalisiert – Kleinschreibung, Umlaute aufloesen, Satzzeichen entfernen – und dann per Set-Intersection abgeglichen. Der Absatz mit dem hoechsten Overlap gewinnt. Mindest-Schwelle: 20%.
Das klingt simpel, ist aber erstaunlich robust. Selbst wenn Deepgram einzelne Woerter falsch erkennt (was bei Fachbegriffen oder Eigennamen vorkommt), reichen die restlichen Woerter locker fuer eine eindeutige Zuordnung.
Schritt 3: Chunks zusammenfuehren
Mehrere Chunks, die demselben Absatz zugeordnet werden, werden zeitlich zusammengefuehrt. Das Ergebnis: Pro Absatz ein Zeitfenster mit Start- und Endzeit. Genau die Datenstruktur, die der Player braucht.
// Fertige Timestamps pro Absatz
{
"sentences": [
{ "start": 1.20, "end": 3.70, "text": "Mulchen: Der stille Held..." },
{ "start": 4.88, "end": 17.60, "text": "Mulchen klingt nicht..." },
{ "start": 18.70, "end": 20.20, "text": "Was Mulch wirklich leistet" },
{ "start": 21.34, "end": 42.97, "text": "Eine gute Mulchschicht..." },
{ "start": 44.60, "end": 45.90, "text": "Die richtige Schichtdicke" },
// ...
],
"confidence": 0.991
}Im Player: Text folgt Audio
Wenn jemand auf Play drueckt, laedt der OKAYPLAY-Player die Timestamp-Daten per API. Dann passiert Folgendes:
Der Player sucht auf der Publisher-Seite nach Absaetzen und Ueberschriften (p, h1–h4, li) und ordnet sie den Timestamp-Saetzen zu – wieder per Fuzzy Matching, diesmal direkt im Browser. Alle 200 Millisekunden vergleicht ein Intervall die aktuelle Abspielzeit mit den Timestamps und setzt die CSS-Klasse okp-reading auf den passenden Absatz.
// Player-Highlighting (vereinfacht)
function _tsUpdate(currentTime) {
for (var i = 0; i < sentences.length; i++) {
if (currentTime >= sentences[i].start
&& currentTime < sentences[i].end) {
// Alten Absatz ausblenden
old.classList.remove('okp-reading');
// Neuen Absatz hervorheben
paragraphMap[i].classList.add('okp-reading');
paragraphMap[i].scrollIntoView({
behavior: 'smooth',
block: 'center'
});
}
}
}Das Scrollen passiert sanft und nur wenn noetig – wenn der aktive Absatz ausserhalb des Viewports liegt. Der Uebergang zwischen Absaetzen wird per CSS-Transition animiert: Der gelbe Hintergrund blendet in 0,4 Sekunden ein und in 0,6 Sekunden wieder aus. Kein harter Sprung, sondern ein fliessender Wechsel.
Ein paar Zahlen
Das sind die Werte fuer den Mulchen-Artikel von FREILANDWERK – einen typischen Blogpost mit Titel, vier Absaetzen und zwei Zwischenueberschriften. Deepgram erkennt 99,1% der Woerter korrekt, das Matching ordnet alle sieben Abschnitte den richtigen Stellen im Text zu. Auch die Ueberschriften – obwohl sie nur wenige Woerter lang sind – werden sauber erkannt und getimestampt.
Warum nicht einfach Whisper?
OpenAIs Whisper ist eine starke Alternative. OKAYPLAY nutzt Deepgram aus einem pragmatischen Grund: Es laeuft auf Shared Hosting. Kein GPU-Server, kein Docker, kein Python-Stack. Deepgram ist eine API – Audio rein, JSON raus, fertig. Das passt zu einem Laravel-Projekt auf klassischem Webhosting.
| Kriterium | Whisper (Self-hosted) | Deepgram Nova-2 |
|---|---|---|
| Infrastruktur | GPU-Server noetig | API-Call |
| Latenz | Je nach Hardware | Unter 5 Sekunden |
| Deutsche Sprache | Gut | Sehr gut |
| Wort-Timestamps | Ja | Ja |
| Shared Hosting | Nicht moeglich | Problemlos |
Qualitaetssicherung: Text Match Score
Als Nebenprodukt des Matchings entsteht ein Text Match Score: Wie viel Prozent des Artikeltexts hat der Sprecher tatsaechlich eingelesen? Der Score vergleicht das Deepgram-Transkript wortweise mit dem Originaltext – inklusive Levenshtein-Alignment fuer Reihenfolge und Overlap-Berechnung als Fallback.
Das ist gleichzeitig eine Qualitaetssicherung: Wenn ein Sprecher nur die Haelfte des Textes liest oder stark abweicht, faellt das auf. Nicht als Blocker, sondern als Signal.
Wo wir hinwollen
Das Highlighting funktioniert. Aber es gibt noch Raum nach oben.
Alle TextSync-Funktionen sind jetzt live: Satz-Highlighting, Klick-to-Seek, Kapitel-Sprungmarken und Leseposition merken. Jede Funktion baut auf den Wort-Timestamps von Deepgram auf. Als Naechstes: Eine kurze Sprecher-Vorschau direkt am Play-Button, damit Zuhoerer die Stimme kennenlernen, bevor sie starten Alle drei Funktionen bauen auf den Wort-Timestamps von Deepgram auf. Als Naechstes: Leseposition dauerhaft merken, damit Zuhoerer auch nach Tagen genau dort weitermachen koennen, wo sie aufgehoert haben Beide Funktionen bauen auf den Wort-Timestamps von Deepgram auf. Als Naechstes: Kapitel-Sprungmarken, die Ueberschriften als visuelle Marker direkt auf der Player-Timeline anzeigen – die Wort-Timestamps von Deepgram sind praezise genug, um einzelne Saetze innerhalb eines Absatzes zu isolieren. Klick-to-Seek ist dann nur noch eine Lookup-Tabelle: Absatz-Index zu Audio-Sekunde.
Fazit
Die Kombination aus Browser-seitigem Forced Alignment (waehrend der Aufnahme) und serverseitigem Deepgram-Matching (nach dem Upload) liefert das Beste aus beiden Welten: Echtzeit-Feedback fuer den Sprecher und praezise Timestamps fuer den Zuhoerer. Die Technik verschwindet im Hintergrund – was bleibt, ist der gelbe Balken, der durch den Text wandert.