Fast reading: Bionic, The Science, and How to Build It
Bionic Reading promises to make you read faster by bolding the first letters of every word. But does the research hold up? And how to actually implement it?
What is Bionic Reading?
In 2022, Swiss typographer Renato Casutt released a technique called Bionic Reading. The premise is elegant: bold the first portion of each word - the "fixation point" - so your eyes skip to it automatically and your brain fills in the rest. Less eye movement, faster comprehension, less cognitive load.
The original Bionic Reading is a registered trademark with a paid API. That's precisely why the open-source ecosystem spawned a dozen clones within weeks - and why understanding the algorithm yourself is more useful than paying for the license.
Does it actually work?
A 2022 study by the Nielsen Norman Group tested reading speed and comprehension with and without Bionic Reading on a sample of participants. The result: no statistically significant improvement in reading speed for neurotypical readers. In some cases, the visual noise actually slowed people down slightly.
A follow-up crowd-sourced experiment on Reddit's r/slatestarcodex reached similar conclusions - participants who believed they were reading faster showed the same WPM as the control group.
So why do so many people swear by it?
Two likely explanations:
- The placebo effect - if you believe you're reading faster, the experience feels faster. Perception ≠ measurement.
- Neurodivergent benefit - users with ADHD and dyslexia consistently report genuine improvement. The bold anchors give scattered attention something to lock onto. This is anecdotally strong, even if RCT data is thin.
What about vowel-bolding?
An alternative approach bolds all vowels instead of the first letters.
For neurotypical readers, it probably doesn't speed things up. For readers with ADHD or dyslexia, worth trying.
The Variable Nobody Talks About: Font
Here's something the Bionic Reading discourse almost entirely ignores: the font you're reading in may matter more than any typographic trick you apply on top of it.
A 2022 ACM study (Towards Individuated Reading Experiences) tested 16 fonts on 352 participants. The headline finding: switching a reader to their optimal font produced an average 35% change in reading speed - with stable comprehension. No bionic bolding, no special algorithm. Just the right typeface.
The serif vs. sans-serif debate turned out to be mostly noise. What mattered more was individual fit. For the same reader, Arial might beat Georgia by 40% - or the reverse. There's no universal winner.
That said, on a group average, passages in Roboto and Arial were read significantly faster than other tested fonts. Both are grotesque sans-serifs with open apertures and high x-height - properties that consistently correlate with screen legibility. The Nielsen Norman Group's research echoes this: no single best font exists, but a handful of well-designed sans-serifs (Roboto, Inter, Arial, Helvetica) tend to cluster near the top.
What the research agrees on:
- x-height - taller lowercase letters (relative to caps) improve legibility at small sizes
- Letter spacing - slightly looser than default aids readers with dyslexia significantly
- Line height - 1.5–1.7× font size is the sweet spot for body text on screen
- Line length - 65–75 characters per line. Longer lines increase saccade cost; shorter lines break rhythm.
The Algorithm - Three Approaches
1. Fixed Ratio (40%)
Bold the first 40% of each word's letters. Simple, language-agnostic, and what most open-source tools use.
function bionicWord(word) {
const letters = word.replace(/[^a-zA-Z]/g, '')
if (letters.length <= 1) return word
const boldLen = Math.ceil(letters.length * 0.4)
return `<b>${word.slice(0, boldLen)}</b>${word.slice(boldLen)}`
}
2. Length-Based Steps
This is closer to Casutt's original patent - the bold length jumps at discrete word-length thresholds rather than scaling continuously. Slightly more "natural" reading rhythm.
function boldLength(wordLen) {
if (wordLen <= 2) return 1
if (wordLen <= 5) return 2
if (wordLen <= 9) return 3
if (wordLen <= 12) return 4
return 5
}
function bionicWord(word) {
const letters = word.replace(/[^a-zA-Z]/g, '')
if (letters.length === 0) return word
const n = boldLength(letters.length)
return `<b>${word.slice(0, n)}</b>${word.slice(n)}`
}
3. Vowel Highlighting
Instead of position-based bolding, emphasize every vowel. Interesting alternative - works well for romance languages.
function bionicVowels(word) {
return word.replace(/[aeiouAEIOU]/g, '<span class="bold">$&</span>')
}
For the rest of this article, we'll use the fixed ratio approach - it's the most portable and easiest to reason about.
Interactive Demo
Toggle the button to see bionic reading applied to a sample paragraph in real time.
Reading is a skill we rarely question. We assume the way we have always done it is the way it must be done. But what if bolding just the first few letters of every word could trick your brain into reading faster? That is the premise behind Bionic Reading — a technique that has sparked genuine debate among researchers, developers, and typographers alike. The idea sounds almost too simple to be real.
Notice how the bold anchors naturally pull your eye across each line.
RSVP & Spritz: One Word at a Time
While Bionic Reading works within normal reading flow, a completely different family of techniques eliminates the flow itself.
RSVP (Rapid Serial Visual Presentation) shows words one at a time, in place, at a controlled rate. No scanning, no saccades. Your eyes stay fixed and the text comes to you.
Spritz (launched 2014, patented) refined RSVP with a key insight: the eye doesn't land at the center of a word - it targets the Optimal Recognition Point (ORP), roughly 30–35% from the word's start. In Spritz, the ORP character is always displayed at the exact same pixel position on screen (highlighted in red), and the word is positioned around it asymmetrically. The result: zero eye movement even between words of different lengths.
This is almost certainly what you've seen circulating on social media - one word flashing in the center of the screen, a single letter lit up in a different color.
Does RSVP actually work?
Better than Bionic Reading on raw speed metrics - but with a serious catch.
A 2015 ScienceDirect study on Spritz found that literal comprehension degrades significantly above ~400 WPM.
The reason: normal reading isn't purely linear. Readers constantly re-fixate on previous words (regressions) for disambiguation. RSVP makes regression impossible by design.
The sweet spot is 250–350 WPM - marginally faster than average reading speed (~200–250 WPM), but with retained comprehension. Above 400 WPM you're training a parlor trick, not building a reading skill.
Interactive Demo
Hit play and drag the WPM slider. Notice how 300 WPM feels manageable, but 500+ WPM turns comprehension into a blur. The vertical red tick marks show where the ORP is always anchored - your eyes never need to move.
Adding a Toggle to Your Website
Here's a complete, dependency-free implementation you can drop into any HTML page.
function bionicWord(word) {
const letters = word.replace(/[^a-zA-Z]/g, '')
if (letters.length <= 1) return word
const boldLen = Math.ceil(letters.length * 0.4)
return `<b>${word.slice(0, boldLen)}</b>${word.slice(boldLen)}`
}
function toBionic(text) {
return text.split(' ').map(bionicWord).join(' ')
}
// Walk the DOM and apply bionic reading to all text nodes
function applyBionicToNode(node) {
const SKIP_TAGS = new Set(['SCRIPT', 'STYLE', 'NOSCRIPT', 'CODE', 'PRE', 'B', 'STRONG'])
if (node.nodeType === Node.TEXT_NODE) {
const parent = node.parentNode
if (!parent || SKIP_TAGS.has(parent.tagName)) return
if (!node.textContent.trim()) return
const span = document.createElement('span')
span.setAttribute('data-bionic', 'true')
span.innerHTML = toBionic(node.textContent)
parent.replaceChild(span, node)
} else {
// Clone childNodes list — live NodeList changes as we modify the DOM
Array.from(node.childNodes).forEach(applyBionicToNode)
}
}
// Reverse: remove all [data-bionic] spans and restore original text
function removeBionic(root) {
root.querySelectorAll('[data-bionic]').forEach(span => {
span.replaceWith(document.createTextNode(span.textContent))
})
}
// Hook up a toggle button
const btn = document.getElementById('bionic-toggle')
let active = false
btn.addEventListener('click', () => {
active = !active
if (active) {
applyBionicToNode(document.querySelector('article'))
} else {
removeBionic(document.querySelector('article'))
}
btn.textContent = active ? 'Disable Bionic Reading' : 'Enable Bionic Reading'
})
<button id="bionic-toggle">Enable Bionic Reading</button>
<article>
<p>Your content lives here...</p>
</article>
A few things worth noting:
- We skip
<code>,<pre>, and<script>tags - you don't want bionic reading applied to code blocks. - We store a
data-bionicattribute so we can cleanly reverse the transformation without refreshing the page. - We clone
childNodesinto anArraybefore iterating - modifying a liveNodeListwhile walking it causes nodes to get skipped.
<b>, it's better to use <span class="bold"> or something similar to preserve semantics - the <b> element isn't meant for bolding contentBuilding a Chrome Extension
A browser extension is the most powerful delivery mechanism - it works on any website without touching the site's source code.
File structure
bionic-reader/
├── manifest.json
├── content.js
├── popup.html
└── popup.js
manifest.json
{
"manifest_version": 3,
"name": "Bionic Reader",
"version": "1.0",
"description": "Apply bionic reading to any webpage with one click.",
"permissions": ["activeTab", "storage"],
"action": {
"default_popup": "popup.html",
"default_title": "Bionic Reader"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"run_at": "document_idle"
}
]
}
content.js
let active = false
function bionicWord(word) {
const letters = word.replace(/[^a-zA-Z]/g, '')
if (letters.length <= 1) return word
const boldLen = Math.ceil(letters.length * 0.4)
return `<b>${word.slice(0, boldLen)}</b>${word.slice(boldLen)}`
}
const SKIP_TAGS = new Set(['SCRIPT', 'STYLE', 'NOSCRIPT', 'CODE', 'PRE', 'B', 'STRONG', 'INPUT', 'TEXTAREA'])
function applyBionicToNode(node) {
if (node.nodeType === Node.TEXT_NODE) {
const parent = node.parentNode
if (!parent || SKIP_TAGS.has(parent.tagName)) return
if (!node.textContent.trim()) return
const span = document.createElement('span')
span.setAttribute('data-bionic', 'true')
span.innerHTML = node.textContent.split(' ').map(bionicWord).join(' ')
parent.replaceChild(span, node)
} else {
Array.from(node.childNodes).forEach(applyBionicToNode)
}
}
function removeBionic() {
document.querySelectorAll('[data-bionic]').forEach(span => {
span.replaceWith(document.createTextNode(span.textContent))
})
}
chrome.runtime.onMessage.addListener((msg) => {
if (msg.action !== 'toggle') return
active = !active
active ? applyBionicToNode(document.body) : removeBionic()
// Persist state so new tabs remember user preference
chrome.storage.local.set({ bionicActive: active })
})
// Restore state on page load
chrome.storage.local.get('bionicActive', ({ bionicActive }) => {
if (bionicActive) {
active = true
applyBionicToNode(document.body)
}
})
popup.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<style>
body { width: 200px; padding: 16px; font-family: sans-serif; background: #0d1117; color: #c8cdd6; }
h2 { font-size: 14px; margin: 0 0 12px; color: #fff; }
button {
width: 100%;
padding: 8px;
background: #eb3c48;
color: white;
border: none;
border-radius: 6px;
font-weight: bold;
cursor: pointer;
}
button:hover { background: #ff6570; }
</style>
</head>
<body>
<h2>Bionic Reader</h2>
<button id="toggle">Toggle Bionic Reading</button>
<script src="popup.js"></script>
</body>
</html>
popup.js
document.getElementById('toggle').addEventListener('click', () => {
chrome.tabs.query({ active: true, currentWindow: true }, ([tab]) => {
chrome.tabs.sendMessage(tab.id, { action: 'toggle' })
})
})
To install locally: open chrome://extensions, enable Developer Mode, click Load unpacked, and point it at your bionic-reader/ folder. Done.
Comparison: which technique is the best?
| Technique | Avg. Speed Gain | Comprehension | Best For |
|---|---|---|---|
| Font optimization | 0–35% | Stable | Everyone — fix this first |
| Bionic Reading | ~0% neurotypical | Neutral/slight drop | ADHD, dyslexia |
| RSVP / Spritz | 20–50% at ≤350 WPM | Degrades above 400 WPM | Power users, dense material |
| Vowel highlighting | ~0% | Neutral | Interesting experiment |
The pecking order is clear: font and typography first, RSVP if you want measurable speed, Bionic Reading as an accessibility option rather than a universal speed hack.
These techniques go from "barely noticeable" to "completely reimagines reading." None of them is a universal fix.
Reading experience is highly individual - font preference alone accounts for a 35% swing in reading speed.
Continue Reading
It's just an array
Stacks, queues, heaps, trees - most reduce to arrays, numbers, and pointers. Here's what actually happens in memory.
Design patterns in 2026 vs Nuxt
Factory, Singleton, Observer, Builder, Strategy, Decorator, Adapter - seven classic patterns, honest verdict on each, and real examples in Nuxt and Nitro.