The Semantic Firewall
Abstract
This paper describes a reading companion built for one person: a grandfather with Alzheimer's disease who loves books but can no longer follow the page. The system processes EPUBs through a two-pass AI pipeline (Gemini for structural analysis, Claude for warm prose), generates per-scene cognitive metadata, and enforces a Semantic Firewall that is constitutionally incapable of revealing content beyond the reader's current position. A three-strategy position tracking system (percentage range, Jaccard text similarity, linear estimation) matches the visible page to narrative context without modifying the original book file. A CSS injection layer called the Trudy Protocol overrides publisher typography to enforce left-aligned, animation-free, footnote-hidden layouts optimised for cognitive accessibility. A prompt-level spoiler prevention system bans adversarial vocabulary ('Antagonist', 'Villain', 'Hero') and constrains character descriptions to first-impression social roles. The architecture is offline-first: after a single download, the entire reading experience, including AI-generated summaries, character timelines, and chapter maps, works without internet. 7,556 lines of TypeScript and Python. Two books processed. One user. This paper reports on the neuroscience that informed the design, the architectural decisions that emerged from it, and what building for an audience of one teaches you about building for anyone.
My grandfather has Alzheimer’s disease. He loves reading. Or, loved. He can still read the words on the page, he just can’t remember what happened on the previous one.
This is a specific kind of loss. The hippocampus, one of the first structures damaged by Alzheimer’s, is the gateway between short-term and long-term memory. When it degrades, new information stops consolidating. You can hold a sentence in working memory long enough to parse it, but by the time you reach the next paragraph, the previous one has dissolved. You can read a chapter and retain nothing. You can meet a character on page twelve and not recognise them on page forty.
The clinical literature describes this as a dissociation between basic oral reading ability and reading comprehension. The mechanical skill of decoding words remains intact well into the middle stages of the disease. But comprehension, which depends on episodic memory, working memory, and the executive function required to integrate information across paragraphs, degrades early and progressively. Research from Frontiers in Psychology shows that inference-making, the ability to understand what a text implies rather than what it states, is particularly sensitive to early cognitive decline, because it requires both verbal episodic memory and semantic processing to work simultaneously.
In practical terms (the only terms I understood it in when I started diving into research): my grandfather can sit in his chair, open a book, and read. But he can’t follow the story. He loses the thread, forgets who the characters are, turns back pages to look for something he half-remembers and can’t find it. Eventually, he closes the book. Not because he doesn’t want to read.
Because reading has become a series of disconnected moments that refuse to cohere into a narrative.
I decided to build him something that would help.
I’d already watched this happen once. My father has Primary Progressive Multiple Sclerosis. He was vibrant when I was sixteen. Something was off. By eighteen, more so, but I left for the Air Force. He got diagnosed with PPMS while I was in recruits. I found out halfway through. Then every year I went home to visit him, he had a little less left. They’re different diseases, PPMS and Alzheimer’s, but the shape is the same: progressive, irreversible, and you can see where it’s going before the person living it can. By the time most people start building, the window has closed. I started building because I’d already seen the window close once, and I knew how fast it moves.
This system might only help for a year. Maybe less. The cognitive decline that makes reading hard will eventually make reading impossible, and no amount of engineering changes that. But a year of reading is worth building for. A month of reading is worth building for. One afternoon where he finishes a chapter and knows what happened in it is worth building for.
What the research says
Before I wrote any code, I spent weeks reading about how Alzheimer’s affects cognition. Not the popular accounts. The neuroscience. I needed to understand what was breaking, mechanically, in order to design around it.
The hippocampus is the first casualty. It sits in the medial temporal lobe and acts as the brain’s indexing system: it doesn’t store memories directly, but it coordinates the process of converting short-term experiences into long-term representations distributed across the cortex. When Alzheimer’s begins destroying neurons there, new memories stop forming. This is why someone with early-stage Alzheimer’s can recall their wedding in vivid detail but can’t remember what they had for lunch.
For reading, this creates a specific cascade of failures:
Working memory overload. Following a narrative requires holding multiple threads simultaneously: who is speaking, what happened earlier in the chapter, what the character’s motivation is, what the setting looks like. Working memory has a limited capacity even in healthy cognition. In Alzheimer’s, that capacity shrinks. Complex sentences become impossible to parse. Multi-character dialogue becomes a blur of unattributed voices.
Inference collapse. Most narrative meaning is implicit. “She slammed the door” implies anger, departure, unresolved conflict. This kind of inferencing requires the reader to retrieve relevant context from earlier in the text (she was arguing with someone), combine it with semantic knowledge (slamming doors signals anger), and synthesise an interpretation. When episodic memory and executive function are both impaired, this chain breaks. The reader sees a door being slammed but can’t reconstruct why.
Temporal disorientation. Stories are experienced linearly but understood non-linearly. You’re on page 200, but your understanding of the scene depends on something established on page 30. A healthy reader maintains a compressed mental model of the entire narrative, updating it with each new scene. An Alzheimer’s patient may retain the immediate scene but lose the model. They’re always on page one of their own comprehension.
The paradox of preserved reading. The cruel part is that the mechanical ability to read persists. The eyes track. The words resolve. The phonological loop still works. It looks like reading. It feels like reading. But it’s reading without comprehension, which is not reading at all. It’s the appearance of an activity that has already been lost.
flowchart TD
A["Hippocampal Neurodegeneration"] --> B["Episodic Memory Loss"]
A --> C["Working Memory Shrinks"]
A --> D["Executive Function Decline"]
B --> E["Can't recall earlier scenes"]
C --> F["Can't hold multiple threads"]
D --> G["Can't integrate or infer"]
E --> H["Character recognition fails"]
E --> I["Plot continuity dissolves"]
F --> J["Complex sentences collapse"]
F --> K["Multi-character dialogue blurs"]
G --> L["Implicit meaning lost"]
G --> M["Temporal disorientation"]
H --> N["Reader closes the book"]
I --> N
J --> N
K --> N
L --> N
M --> N
style A fill:#8B0000,color:#fff
style N fill:#4a4a4a,color:#fff
This research shaped every design decision I made.
The architecture
Reminisce is a web-based e-reader, primarily designed for an iPad, with a Python processing pipeline. The reader is a Vite/React application using epub.js for rendering. The processing pipeline, called Cortex, is a Python CLI that ingests EPUB files and generates cognitive metadata using large language models.
The system has three layers:
-
Cortex (offline, pre-processing): Takes an EPUB, extracts text in 100-word chunks, runs a two-pass AI pipeline, outputs a JSON sidecar with per-scene summaries, character tracking, and structural metadata.
-
The Reader (runtime, offline-capable): Renders the EPUB, tracks the reader’s position, matches visible text to pre-computed scenes, and surfaces contextual help through a Memory Aid interface.
-
The Semantic Firewall (invariant, enforced at data layer): Ensures the system never reveals content beyond the reader’s current position.
graph TB
subgraph Cortex ["Cortex (Pre-processing)"]
EPUB["EPUB File"] --> Extract["Text Extractor<br/>100-word chunks"]
Extract --> Pass1["Pass 1: Gemini 2.5 Pro<br/>Full-book structure"]
Extract --> Pass2["Pass 2: Gemini Flash<br/>Per-scene summaries"]
Pass1 --> |"characters, chapters,<br/>themes, setting"| Pass2
Pass2 --> JSON["Cognitive Metadata<br/>JSON Sidecar"]
end
subgraph Cloud ["Supabase (Cloud)"]
EPUB2["EPUB Storage"]
META["Metadata Storage"]
end
JSON --> META
EPUB --> EPUB2
subgraph Device ["Reader (iPad / Offline)"]
IDB[("IndexedDB<br/>EPUB + Metadata")]
Reader["epub.js Renderer"]
Tracking["Position Tracking<br/>% → Jaccard → Linear"]
Firewall["Semantic Firewall<br/>getScenesUpTo()"]
Aid["Memory Aid<br/>4 Tabs"]
Trudy["Trudy Protocol<br/>CSS Injection"]
Reader --> Tracking
Tracking --> Firewall
Firewall --> Aid
Reader --> Trudy
IDB --> Reader
end
EPUB2 --> |"one-time download"| IDB
META --> |"one-time download"| IDB
The two-pass AI pipeline
Cortex processes a book in two passes.
Pass 1: Structural analysis (Gemini 2.5 Pro). The entire book text (up to 500,000 characters) is fed to Gemini’s million-token context window. The model returns a structural map: chapters with summaries, character profiles with descriptions and relationships, major themes, and the primary setting. Temperature is set to 0.3 for consistency. The output is constrained to JSON via response_mime_type.
This pass answers: What is this book about? Who is in it? What happens?
Pass 2: Scene-by-scene summaries (Gemini Flash). For each 100-word chunk, Cortex generates three things:
-
A summary beat: one warm, present-tense sentence describing what is physically happening right now. “Santiago is sitting with Manolin, sharing coffee and quiet hope as dawn approaches.”
-
A story so far: exactly three bullet points of the most important plot points leading to this moment, starting from the beginning of the book. Past tense. Under fifteen words each.
-
Entities: every character present in this scene, with their current emotional state and their perceived role.
Each scene prompt includes the previous scene’s summary, creating a chain of narrative continuity. The model sees what just happened before it describes what’s happening now.
Present tense was a deliberate choice. For someone whose short-term memory is impaired, “Santiago is sitting with Manolin” is more grounding than “Santiago sat with Manolin.” Present tense implies observation. Past tense implies recall. The system should feel like a companion pointing at the page and saying “look, this is happening now,” not like a teacher quizzing you on what you should remember.
The cost of comprehension
Processing a single novel costs approximately $20 in API calls. Two hundred to three hundred scenes, each requiring a Gemini Flash inference, plus one full-book structural pass through Gemini Pro.
The original architecture used Claude Sonnet for Pass 2 because Claude writes warmer, more human prose. I switched to Gemini Flash because the cost difference was an order of magnitude and the quality was sufficient. This is a recurring tension in AI-assisted tools: the model that writes best is rarely the model you can afford to run at scale. For a system with one user and a library of a dozen books, $20/book is acceptable. For a product with a thousand users and growing libraries, it’s untenable.
This is not a product. The economics wouldn’t work. The legal exposure (processing copyrighted EPUBs, generating derivative summaries of copyrighted text) wouldn’t survive a terms-of-service review, let alone a courtroom. I built it for my grandfather. That’s the entire addressable market.
But the cost structure reveals something about where AI is right now: cheap enough to prototype a personal tool, too expensive to productise one. That gap is where a lot of the most interesting software lives, and where almost none of it gets built, because nobody funds tools for audiences of one.
The Semantic Firewall
The core invariant of Reminisce is that the system must never reveal content beyond the reader’s current position.
This is not a UI preference. It’s a safety constraint. For someone with Alzheimer’s, spoilers aren’t just annoying; they’re disorienting. If the reader loses their place and the Memory Aid shows them a scene from chapter 12 when they’re reading chapter 4, they won’t understand it’s future content. They’ll try to integrate it with what they’re reading now. It will create confusion that they can’t resolve, because they lack the cognitive resources to recognise and discard out-of-sequence information.
The firewall is enforced at the data layer, not the UI layer:
export function getScenesUpTo(
metadata: CognitiveMetadata,
sequenceId: number
): SceneMetadata[] {
return metadata.scenes.filter(s => s.sequence_id <= sequenceId);
}
One function. One filter. Every component that renders narrative content, the Memory Aid modal, the character timelines, the chapter summaries, calls getScenesUpTo() as its data source. There is no code path that bypasses it. The firewall doesn’t depend on any individual component remembering to check; it’s baked into how data flows through the system.
This is temporal access control. Most access control systems are identity-based: who are you? This one is position-based: where are you in the narrative? The firewall creates a moving window of permissible knowledge that advances strictly monotonically with reading progress. It cannot go backwards. It cannot skip ahead. The reader’s comprehension boundary is the system’s information boundary.
flowchart LR
subgraph Book ["Full Book (200+ scenes)"]
S1["Scene 1"] --- S2["Scene 2"] --- S3["..."] --- S47["Scene 47"] --- S48["Scene 48"] --- S49["..."] --- S200["Scene 200"]
end
Reader["Reader Position:<br/>Scene 47"] --> Firewall["getScenesUpTo(47)"]
Firewall --> Visible["Scenes 1-47<br/>VISIBLE"]
Firewall -.- Blocked["Scenes 48-200<br/>BLOCKED"]
Visible --> Aid["Memory Aid"]
Visible --> Chars["Character Cards"]
Visible --> Timeline["Chapter Timeline"]
style Visible fill:#2d5a27,color:#fff
style Blocked fill:#8B0000,color:#fff
style Firewall fill:#b8860b,color:#fff
Spoiler prevention at the prompt level
The Semantic Firewall prevents showing future scenes. But there’s a subtler problem: how the AI describes characters.
If the AI knows the full plot (it does, from Pass 1), it might describe a character using information the reader hasn’t encountered yet. It might call a character an “antagonist” before the reader knows there’s a conflict. It might say “the murderer” before the reader knows someone has been murdered.
The prompts contain an explicit banned words list:
BANNED ROLE WORDS (NEVER use these as a character's "role"):
- "Antagonist", "Villain", "Evil", "Murderer", "Killer", "Traitor", "Suspect"
- "Protagonist", "Hero", "Heroine" (too meta)
- Instead use their SOCIAL ROLE: "Father", "Neighbor", "Farmer", "Teacher"
Characters are described as they appear at their point of introduction, not as they truly are. Boo Radley is “Reclusive neighbor.” Tom Robinson is “Accused man.” The system maintains a first-impression ontology that evolves with the reader’s progress, because the reader’s understanding of a character should evolve at the same pace as the author intended.
This is adversarial prompt engineering in reverse. Instead of defending against jailbreaks (preventing the model from saying harmful things), the system defends against its own tendency to be helpful. A language model that knows the full plot wants to give you the most complete description. The prompts constrain it to give you the most appropriate description for where you are in the story.
Each character’s role field is per-scene, not global. The Memory Aid’s character timeline aggregates these snapshots:
const characterTimelines = useMemo(() => {
const timelineMap = new Map<string, CharacterTimeline>();
for (const scene of visibleScenes) { // firewall already applied
for (const entity of scene.entities) {
// Latest known role and status update with each scene
}
}
});
The result: a character card that shows who this person seems to be based on everything the reader has read so far. Not who they are.
Position tracking without modifying the book
The system needs to answer a deceptively hard question: What scene is the reader looking at right now?
The naive approach is to inject invisible anchor elements into the EPUB at known positions and track which anchor is visible. I built this. It’s still in the codebase (anchor_injector.py, 221 lines). It worked. Then it broke in three ways:
- Modifying the EPUB’s XHTML required choosing between
html.parserandlxml. The wrong choice corrupted the document structure and broke epub.js rendering. - Modified EPUBs created a second file (
_augmented.epub) that had to be managed alongside the original. - DRM-protected books couldn’t be modified at all.
I abandoned anchor injection and built a non-destructive alternative. The current system treats the EPUB as immutable and uses a three-strategy matching cascade:
flowchart TD
PageTurn["Page Turn Event"] --> LocCheck{"epub.js locations<br/>generated?"}
LocCheck -- "No (percentage = 0)" --> Unreliable["Treat percentage<br/>as undefined"]
LocCheck -- "Yes" --> Pct["Get percentage<br/>from epub.js"]
Pct --> S1{"Percentage falls within<br/>a scene's range?"}
Unreliable --> S2
S1 -- "Yes" --> Match1["Strategy 1: Percentage Match<br/>Confidence: 0.9"]
S1 -- "No" --> S2{"Jaccard similarity<br/>> 0.05?"}
S2 -- "Yes" --> Match2["Strategy 2: Jaccard Match<br/>Confidence: variable"]
S2 -- "No" --> Match3["Strategy 3: Linear Estimate<br/>Confidence: 0.03"]
Match1 --> Update["Update current scene"]
Match2 --> Update
Match3 --> Update
Update --> Firewall["Semantic Firewall<br/>filters to this position"]
style Match1 fill:#2d5a27,color:#fff
style Match2 fill:#b8860b,color:#fff
style Match3 fill:#8B0000,color:#fff
Strategy 1: Percentage range (confidence: 0.9). Each scene has percentage_start and percentage_end from Cortex, computed as the word offset divided by total words. epub.js provides location.start.percentage on every page turn. If the reader’s percentage falls within a scene’s range, that’s the match. Fast, deterministic, no text comparison needed.
Strategy 2: Jaccard text similarity (confidence: variable). Fallback for metadata that lacks percentage fields. The hook extracts visible text from the epub.js iframe, tokenises both texts into word sets (filtering words under 3 characters), and computes |intersection| / |union|. Threshold: 0.05.
Strategy 3: Linear estimate (confidence: 0.03). If all else fails: scene_index = floor(percentage x total_scenes). Wrong, but better than no tracking at all.
There’s a critical guard against a subtle bug: epub.js returns percentage = 0 before locations have been generated (a background operation). Without the guard, the system would lock onto scene 1 on every page turn until epub.js finished computing. The hook detects this:
const locationsReady = (rendition?.book?.locations?.total ?? 0) > 0;
const percentage = (rawPercentage === 0 && !locationsReady)
? undefined
: rawPercentage;
The Trudy Protocol
Every EPUB comes with its own CSS. Publisher stylesheets vary wildly: justified text, decorative indents, inline footnotes, animated chapter headings. These are fine for a typical reader. For someone with Alzheimer’s, they’re obstacles.
The Trudy Protocol is a 90-line CSS file injected into the epub.js iframe on every chapter load. It overrides publisher styles to enforce a consistent, accessible reading environment:
* { line-height: 1.6 !important; letter-spacing: 0.02em !important; }
p { text-align: left !important; text-indent: 0 !important; }
aside { display: none !important; }
* { animation: none !important; transition: none !important; }
Each rule traces to a specific cognitive accessibility concern:
Left-aligned, no indents. Justified text creates uneven word spacing that makes it harder to track across lines, a task that requires sustained visual attention. Left alignment creates a consistent left edge that serves as an anchor point when the reader’s eyes return from the end of a line. Text indentation creates visual irregularity at the start of paragraphs, which can cause a reader with impaired executive function to lose their position.
No animations or transitions. Moving elements compete for attention. For someone who is already using all available cognitive resources to process the text, any visual distraction can break the fragile thread of comprehension.
Footnotes hidden. Inline footnotes and endnotes create ambiguity about what is “the story” and what is “commentary.” For a reader who may already be struggling to maintain a coherent model of the narrative, this distinction is one more thing to process. Removing it reduces cognitive load.
Generous line height and letter spacing. Research on cognitive accessibility supports increased spacing for readers with impaired visual attention. The 1.6 line height and 0.02em letter spacing create more visual breathing room between text elements.
The Trudy Protocol handles layout only. Colours, font family, and font size are controlled dynamically through epub.js themes, so the user’s preferences take effect without conflicting with the injected styles. The font options include Atkinson Hyperlegible and OpenDyslexic alongside traditional serif faces, because cognitive impairment and visual processing difficulties often co-occur.
The protocol is named after its user’s wife
The Memory Aid
The Memory Aid is a modal interface with four tabs, each answering a different question:
Just Now. “What is happening right now?” Shows the current scene’s summary beat in large, centred text, followed by “A moment ago”: the previous two to three scene summaries at decreasing opacity. This is the tab for someone who puts the book down, picks it up again, and can’t remember where they were.
Chapter So Far. “What has happened in this chapter?” A vertical timeline of every scene in the current chapter, with the current scene highlighted at the top. This is the tab for someone who can feel the chapter has been building to something but can’t recall the steps.
Story So Far. “What has happened in the whole book?” The current scene’s three-bullet story_so_far, followed by summaries of every completed chapter. This is the tab for someone who has been reading for days and needs to reconstruct the narrative arc.
Characters. “Who is this person?” Every character encountered so far, with their latest known role, their current emotional state, and an expandable timeline showing how they’ve appeared across chapters. Characters present in the current scene are highlighted and sorted first.
Every tab calls getScenesUpTo(). Every tab is firewalled. The tabs are designed around the specific memory failures that the research describes:
flowchart LR
subgraph Failures ["Cognitive Failure Mode"]
F1["Lost immediate context"]
F2["Lost chapter sequence"]
F3["Lost cumulative plot"]
F4["Lost character recognition"]
end
subgraph Tabs ["Memory Aid Tab"]
T1["Just Now"]
T2["Chapter So Far"]
T3["Story So Far"]
T4["Characters"]
end
subgraph Data ["What It Shows"]
D1["Current summary +<br/>previous 2-3 scenes"]
D2["Timeline of every<br/>scene in this chapter"]
D3["3 key plot bullets +<br/>completed chapter summaries"]
D4["Character cards with<br/>role, status, timeline"]
end
F1 --> T1 --> D1
F2 --> T2 --> D2
F3 --> T3 --> D3
F4 --> T4 --> D4
style F1 fill:#8B0000,color:#fff
style F2 fill:#8B0000,color:#fff
style F3 fill:#8B0000,color:#fff
style F4 fill:#8B0000,color:#fff
style T1 fill:#2d5a27,color:#fff
style T2 fill:#2d5a27,color:#fff
style T3 fill:#2d5a27,color:#fff
style T4 fill:#2d5a27,color:#fff
Character names in summaries are highlighted with interactive chips. Tapping a character name shows their role and current status in a popover. This addresses a specific behaviour I observed: my grandfather would encounter a name in the text and not remember who it was. He’d flip back through pages looking for where the character was introduced. Now he can tap the name and get a reminder without leaving the page.
Offline-first
After the initial download, the entire reading experience works without internet. The EPUB blob and the full cognitive metadata JSON are downloaded to IndexedDB in a single transaction:
Cloud (Supabase) Local (IndexedDB via Dexie)
books table, epub files ──▶ EPUB blob + full AI metadata
metadata JSON files Reading progress (CFI + sequence)
This matters because the user is an elderly man who may read on a device without wifi. He doesn’t understand connectivity issues. He can’t troubleshoot. If the app fails because of a network condition, nobody will be there to explain why. The app must never fail for a reason the user can’t comprehend.
The download flow fetches the EPUB, fetches the metadata JSON, wraps both into an IndexedDB record, and saves it in one atomic operation. After that, the device has everything: the book, every scene summary, every character profile, every chapter map. The only feature that requires internet is a live AI query mode (“Ask the book”), which is optional and peripheral.
The evolution of the architecture
The codebase contains archaeological evidence of its own history. Both the abandoned anchor injector and its replacement coexist in the Cortex module. Reading them in sequence tells a story about what I tried, what failed, and why.
Version 1: Anchor injection. Inject invisible <span> elements into the EPUB every 150 words. Track reading position by detecting which anchor is visible in the viewport. This worked in principle but fragile in practice. XHTML corruption. DRM incompatibility. A sanitization function (_sanitize_toc) to handle books with malformed table-of-contents metadata that would crash the writer. A dual-file problem where the augmented EPUB and the original had to be kept in sync.
Version 2: Text matching. Treat the EPUB as immutable. Extract text chunks during processing and store them in the metadata sidecar. At runtime, compare visible text against stored chunks using Jaccard similarity. This eliminated every problem with anchor injection but introduced a new one: Jaccard matching is slow and imprecise in paginated mode, because epub.js exposes the full chapter text, not just the visible page.
Version 3: Percentage tracking. Compute the word-count percentage of each chunk during processing. At runtime, compare epub.js’s reported percentage against the pre-computed ranges. Fast, deterministic, and accurate once epub.js has finished generating locations. The Jaccard matcher remains as a fallback.
Each version was born from the failure of the previous one. The current three-strategy cascade is the result of learning that no single tracking method is reliable across all books, all devices, and all states of the epub.js rendering lifecycle.
What building for one person teaches you
There are no market dynamics here. No personas, no segments, no competitive analysis. The addressable market is one man sitting in a chair in Melbourne.
This changes every decision:
There are no baselines. Nobody has built an Alzheimer’s e-reader. There’s no existing product to benchmark against. Every requirement comes from observation, not from market research. I watched my grandfather read. I watched where he stopped. I watched what confused him. I watched him flip back through pages. The requirements are: he should be able to tap a button and be told what’s happening right now, who the characters are, and what happened before. That’s it. Everything else is engineering.
The user can’t give you feedback. Not in the traditional sense. He can’t fill out a survey. He can’t articulate what’s wrong with the interface. He can tell me he enjoyed reading today, or he can tell me he didn’t. The signal is binary and delayed. Design decisions are made from empathy and neuroscience, not from metrics.
The constraints are the moat. Nobody builds this because the market is too small, the legal exposure is too large, the unit economics are hostile ($20/book in API calls), and the user can’t advocate for the product. Every one of these is a reason a product company would kill the project at the pitch stage. And every one of these is why the solutions I invented, the Semantic Firewall, the Trudy Protocol, the spoiler-safe prompt constraints, don’t exist anywhere else. The moat isn’t the code. The moat is that nobody else had a reason to solve these problems.
You optimise for the user, not the market. When you can’t monetise, you can’t be distracted by monetisation. Every design decision in Reminisce, the banned words list, the present-tense summaries, the hidden footnotes, the OpenDyslexic font option, exists because I watched one person struggle and designed for that. Not for a persona. For a person. The best products come from this kind of specificity, even if most of them never become products at all.
Limitations
No user testing data. The system was built for one person. There is no formal usability study, no clinical metrics, no control group. The claims about cognitive accessibility are grounded in published neuroscience research, but the design decisions have not been validated in a clinical setting.
No evaluation of AI output quality. The banned words list is enforced at the prompt level, not verified at the output level. A future version could run the generated metadata through an automated spoiler detection test, checking that no character role contains banned vocabulary and that no scene summary references events from later in the book.
Two books processed. Both are American literary fiction (The Catcher in the Rye, To Kill a Mockingbird). The chunking strategy (100 words per scene) and the summary prompts are calibrated for narrative prose. The system has not been tested on non-fiction, poetry, dialogue-heavy drama, or non-English texts.
Legal constraints. Processing copyrighted EPUBs and generating derivative summaries of copyrighted text raises legal questions that I have not resolved, because this is a private tool for a family member, not a commercial product. Scaling it would require licensing agreements or limiting it to public domain texts.
The question underneath
Every e-reader on the market is built for neurotypical readers. The Kindle, Apple Books, Kobo, Google Play Books: they assume you can follow the page. They assume you remember what happened in the previous chapter. They assume that if you lose your place, you can find it again.
Fifty-five million people worldwide have dementia. Research consistently shows that reading remains a mentally stimulating activity even in the middle stages of cognitive decline, and that shared reading programs provide cognitive benefits, emotional connection, and sustained engagement. But the tools assume an unimpaired reader.
I built Reminisce because my grandfather is one of the fifty-five million, and because the thing he loves most is becoming the thing he can’t do. I know this system has a shelf life. The disease is progressive. The window where he can still read, still follow a story with help, still sit in his chair and feel like himself, that window is closing. I’ve watched a window like this close before.
But the system will outlast the window. The architecture, the patterns, the questions it asks, those are transferable. Maybe someone else has a grandmother who loves reading. Maybe someone else has already noticed the pages being turned backwards. Maybe they’re a developer, and they’re looking for a place to start.
7,556 lines of TypeScript and Python. Two books. One user. The question underneath it is not small: what happens when the tools we build assume a brain that works the way ours does?
The Semantic Firewall is not a feature. It’s an answer.