Inspiration

1 in 5 people show symptoms of dyslexia — yet every digital reading experience serves the other 4. Static fonts, fixed line spacing, and unchanged contrast levels ignore a simple truth: every reader's brain is different. I asked: what if text could adapt to you?

Also the movie: Taare Zameen Par

The frustration is personal. Standard accessibility toggles ("large text" or "high contrast") are blunt instruments. They don't learn. Lume was built to change that — a reading companion that gets smarter every session.

What it does

Lume lets you paste any text and renders it with 7 research-backed typographic adaptations for dyslexia:

  • Letter spacing (+0.04 em) — reduces crowding between letters
  • Word spacing (+0.16 em) — improves word boundary perception
  • Knuth-Plass hyphenation — DP-based line breaking
  • Reading emphasis — frequency-aware bolding of word beginnings
  • Warm color overlay — WCAG-AA-tested parchment background
  • Chunked reading — ~80-word paginated segments
  • OpenDyslexic font — locally-bundled SIL-OFL typeface

After reading, Lume measures your reading speed (WPM) and comprehension (self-rating + MCQ), then feeds a blended reward signal into a 16-arm Thompson Sampling contextual bandit + per-user Ridge Regression to continuously discover your optimal configuration. After enough sessions, it locks in your personal Best Fit automatically.

How I built it

Frontend: Next.js 16 + React 19 + TypeScript + Tailwind CSS v4 + shadcn/ui

Backend: FastAPI + Python 3.11 + SQLite + scikit-learn + scipy + statsmodels

ML pipeline:

  • 16-arm Thompson Sampling bandit with continuous Beta(alpha, beta) posterior updates
  • Per-user Ridge Regression on a 47-dimensional feature vector (text complexity, Flesch-Kincaid grade, syllable density, arm config booleans)
  • Blended reward: R = 0.7 x normalize_wpm(WPM) + 0.3 x blend_comprehension(self_rating, MCQ)
  • Stagnation detection: mean(reward) >= 0.65 AND std(reward) <= 0.08 over last 3 sessions -> auto-locks Best Fit

8 custom data structures: Trie (prefix lookup for emphasis), Freq Index (heap-backed), Knuth-Plass hyphenator (DP), and 5 more.

EDA: Paired t-test (letter spacing -> WPM, p < 0.05), one-way ANOVA (arm group comprehension differences), OLS regression validates 47-dim feature importance, R^2 tracking per user.

Challenges I ran into

  • Reward blending: Fusing WPM (objective, high-variance) with comprehension (subjective, low-range) without one signal drowning the other required careful normalization against a rolling per-user baseline.
  • Ridge gating: Training a per-user model on fewer than 30 events produces garbage. I implemented a hard event gate and fall back to the bandit below threshold.
  • Knuth-Plass in Python: The classic TeX algorithm uses DP over break-point candidates — porting it faithfully to a web render pipeline without LaTeX infrastructure took real care.
  • Accessibility for the target audience: Every control is keyboard-navigable, every ARIA role is correct, every color pair passes WCAG AA — because our users depend on it.

Accomplishments that I'm proud of

  • Lighthouse 100 / 100 accessibility score (automated)
  • 0 axe-core violations
  • 145 passing pytest tests with a full CI pipeline (ruff + lint + pnpm build + notebook smoke + secret guard)
  • A reward formula that mathematically proves comprehension growth via paired t-test and ANOVA
  • Full keyboard navigation — every action reachable via Tab / Enter / Escape
  • OpenDyslexic font bundled locally — zero CDN dependency, works offline

What I learned

Personalization at this level is more engineering than design. The hardest problems were statistical: when do you trust a model trained on 12 data points? How do you blend two qualitatively different comprehension signals? How do you detect convergence without false positives? Each answer required a concrete mathematical decision, not a vibe.

What's next for Lume

  • Browser extension that applies your personal Best Fit to any webpage
  • OS-level rendering layer — intercept system text rendering to apply your config everywhere
  • Multi-passage longitudinal study with IRB-approved data to validate the bandit's convergence claims at scale
  • Multilingual support — the Knuth-Plass hyphenator and Trie already support Unicode; the bandit needs per-language arm sets

Built With

Share this project:

Updates