Skip to content
The reader

The same text gets quieter as the reader learns

Content is processed once; support is projected per reader, per request — and the reader is Hotwire, not a second application.

What a form family is

A learner who knows geht will likely read gehst without help — it looks like the word they already know. They will not read ginge or gegangen. Those are the same verb, but to a naive reader they are different words.

A form family is a cluster of inflected forms that resemble each other closely enough that knowing one carries you to the rest. It is always bound to a lemma (the dictionary form of the word), and a lemma normally has several: gehen holds one family around geht and gehst, another around ging, another around gegangen.

Tracking vocabulary on the lemma is too coarse — it would mark gegangen as known because the reader once met geht, and strand them. Tracking each surface form is too fine — it re-glosses gehst at someone who plainly does not need it. The useful unit sits between the two, and no dictionary contains it, because it is a fact about what looks familiar rather than about what is etymologically related.

Grouping the forms is the hard part

Since the unit is visual resemblance, it has to be measured. A form is tested against the families already under its lemma: exact match on the normalized form, then prefix containment for truncations and derivations, then Jaro-Winkler similarity above a configured threshold, scored as the mean of the closest few forms already in the family. If no family is close enough, the form starts one of its own.

German is unkind here in specific ways: umlaut breaks the stem (Haus / Häuser), strong verbs diverge outright, separable prefixes detach and travel to the end of the clause. Those are exactly the cases where a reader’s recognition breaks too, which is why an orthographic measure tracks the pedagogy better than a grammatical rule would.

One invariant governs it: a form belongs to exactly one family. Where several match, the transparent family — the one holding the lemma itself — wins, and remaining ties break on mean similarity, then maximum similarity, then identifier: ordering chosen so the answer is stable rather than merely plausible.

That stability is why this pass is ordinary code and not a fifth model call. A model asked the same question twice may cluster differently, and the cost of drift is not a wrong gloss: it is a reader’s vocabulary history detaching from the forms it was recorded against.

Rendering with that state

At request time, FIRE — the Form-family Interval Reading Engine — combines the passage with per-user, per-language knowledge state. Families the user has clicked on to mark as mastered are suppressed. Non-mastered support returns when its interval is due, using an SRS algorithm similar to a flashcard app. Reader signals update state at controlled persistence boundaries rather than writing on every interaction.

That buys four things: canonical artifacts stay reusable, user knowledge stays independent of any one book, support changes without reprocessing text, and the renderer stays deterministic given content and reader state.

Keeping a server-rendered reader immediate

The reader is server-rendered and still has to behave like an application. Turbo Frames replace the active chunk on navigation while Stimulus controllers prefetch the next one, so moving through a book does not feel like loading pages. Keyboard and swipe navigation, the settings drawer, gloss interactions, and activity tracking are all Stimulus — no client-side router, no second view layer to keep in sync with the first.

Audio synchronization is the demanding part. Word-level highlighting runs off the native HTMLAudioElement with requestAnimationFrame, matched against the same phrase windows the certifier validated. Nothing sits between the timing evidence and the highlight.

This is what makes the monolith argument concrete rather than rhetorical: the reader gets application-grade interaction without a second application to build, deploy, and keep consistent with the domain model.