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
- css
- elevenlabs
- fastapi
- next.js
- python
- react
- scikit-learn
- sqlite
- tailwind
- typescript
Log in or sign up for Devpost to join the conversation.