Speed-read anything on your screen, with the voice locked to the same words per minute.
Select text in any app, press Ctrl+Shift+R, and LightRead flashes it one word at a time with a red focus letter while reading it aloud at exactly the speed you set. The red letter and the voice run off one clock, so the word you hear is the word you see.
Windows only. The app lives in the tray and is named READ.exe.
Grab READ.exe from the latest release and run it. Nothing to install.
- Windows 10 or 11, 64-bit. Self-contained: no .NET install needed.
- Voices come from Windows itself (Settings → Time & language → Speech). The bundled David and Zira voices work out of the box.
- The exe is not code-signed, so SmartScreen will show "Windows protected your PC" the first time. Click More info → Run anyway, or build it yourself from source below.
Needs the .NET 9 SDK.
pwsh -NoProfile -File run.ps1
Or publish a single exe:
pwsh -NoProfile -File run.ps1 -Publish
Then run dist\READ.exe. It sits in the tray.
| Input | Action |
|---|---|
| Select text, then Ctrl+Shift+R | Grab and play |
| Ctrl+Shift+R with nothing selected | Falls back to the clipboard |
| Ctrl+V in the window | Paste and play |
| Tray → Read clipboard | Same |
| Space | Play / pause |
| [ ] or mouse wheel | WPM |
| ← → | Sentence |
| , . | Word |
| V | Voice on/off |
| R | Restart |
| Esc | Hide to tray |
| PIN | Keep the overlay on the desktop |
Voice ON: RSVP is driven by the stretched audio. The number you set is the actual spoken+shown WPM.
Voice OFF: pure visual RSVP, 80–800 WPM, with punctuation pauses and a short start ramp.
Locking speech to an exact WPM means getting two things right that SAPI reports misleadingly.
Word positions.SpeakProgress.CharacterPosition is an offset into whatever was handed to
Speak. Passing a PromptBuilder means SAPI speaks an SSML document, so every position comes
back shifted by the length of the SSML preamble and maps to the wrong word. LightRead speaks the raw
string, and drops any mark that does not index into it.
Word timing.SpeakProgress.AudioPosition is measured against the voice's own sample rate,
not the stream being written. The bundled Windows desktop voices are 16 kHz but render at
22.05 kHz, so every timestamp arrives inflated by 22050/16000 — enough for the display to drift a
quarter of the way ahead by the end of a passage. LightRead writes to a stream that records how much
audio existed when each mark fired, fits the single scale factor relating the two, and corrects
every timestamp. A voice whose rates already agree fits a factor of 1.
With honest timings, the rest is arithmetic: measure the natural WPM, pick the SAPI rate that gets closest on its own, and let SoundTouch stretch away the remainder. Choosing the rate first keeps the stretch near 1.0, which is where it sounds best — a 16 kHz voice at 800 WPM would otherwise need a 5x stretch.
Tokens SAPI never reports — bare punctuation, symbols it expands into a different number of words — are interpolated between their neighbours so each still gets its own slice of the clock.
dotnet test
Unit tests cover the ORP split, the visual clock and the voice binding. SpeechSyncTests drives
the real SAPI stack, because both defects above lived in the seam between SAPI and the engine and
neither was visible from a mock.
pwsh -NoProfile -File run.ps1 -SelfTest
The self-test additionally renders audio at 200, 500 and 800 WPM and asserts the result actually
runs at that rate, that every word mark lands on the token it belongs to, and that walking the
audio clock visits every word in order and ends on the last one. It writes
%TEMP%\read-selftest.log and exits non-zero on failure.
Unhandled exceptions are appended to %APPDATA%\READ\crash.log rather than silently taking the
tray icon down.
The hotkey depends on other processes cooperating, so run with tracing to see how far a press got:
dist\READ.exe --debug
That writes %APPDATA%\READ\debug.log with a line for the registration result, each WM_HOTKEY
received, and what capture returned. The usual causes, in order:
- LightRead is not running. It has no taskbar button — look for the tray icon.
- Another app owns Ctrl+Shift+R.
RegisterHotKeyfails, and LightRead opens its window at startup to say so rather than relying on a tray balloon you might miss. - The app you copied from ignored the synthetic Ctrl+C (PDF viewers, canvas apps, remote desktops). LightRead falls back to whatever is already on the clipboard and says so in the status bar.
A press always opens the window, even when it captures nothing, so it never looks like nothing happened. Launching a second copy raises the running one rather than exiting silently.
WPF, .NET 9, System.Speech, NAudio, SoundTouch. No sidecar processes, no per-word WAV cache.
MIT — see LICENSE.