Projects
Software and utilities I've built over the years. Most of it is accessibility tooling for blind users: screen reader add-ons, editor speech, and a few tools I wrote because I needed them myself. Everything here is free and open source on GitHub.
Some of these projects are no longer maintained. Those are marked Abandoned so you know what you're getting into.
Contents
Neovim speech
nvim-speaks is a family of projects that make Neovim talk. It's inspired by Emacspeak: rather than having a screen reader guess what changed on the screen, the editor itself describes what happened.
nvim-speaks
A Neovim plugin that gives the editor its own speech, braille, and more.
nvim-speaks watches editor state and turns it into structured events: cursor movement (including what you asked for versus what actually happened, and when you hit a boundary), mode changes, pending operators like delete and yank, folds, diff hunks, diagnostics, and completion menus. It phrases speech for each event inside the plugin and sends both the event and the speech to a backend process as newline-delimited JSON. That backend can be a local speech server, a TCP bridge, an SSH tunnel, or a server running inside your screen reader. Other plugins can also send announcements through it.
nvim-speaks NVDA server
An NVDA add-on that speaks for nvim-speaks.
Runs a small TCP server inside NVDA on 127.0.0.1:7533. Point nvim-speaks at it through the TCP bridge and NVDA speaks whatever Neovim sends. Press NVDA+shift+s to stop or restart the server.
nvim-speaks Windows server
A standalone Windows speech server for nvim-speaks, written in Rust.
Reads the nvim-speaks protocol on stdin and speaks it through NVDA (via the NVDA controller client) or Windows SAPI, falling back to logging when neither is available. Useful when you want Neovim speech on Windows without installing an add-on.
nvim-speaks TCP bridge
Glue for sending nvim-speaks output over TCP or SSH.
Small helper scripts: speech-to-tcp connects nvim-speaks to a TCP speech server, and tcp-dump-server prints whatever it receives for debugging. Includes notes on tunneling speech over SSH, so you can edit on a remote machine and hear it locally.
My Neovim config
My personal Neovim setup, wired up for nvim-speaks.
A lazy.nvim based config with LSP (via Mason), Tree-sitter, completion, and nvim-speaks loaded from a local checkout. Shared mostly as a working example of how the pieces fit together. Windows support is partial.
NVDA add-ons
Add-ons for the NVDA screen reader. To install one, download the .nvda-addon file and open it, or use Tools, Manage add-ons, Install in NVDA.
Notepad++ for NVDA
Makes the Notepad++ text editor accessible with NVDA. Latest version: 2026.06.1.
Adds spoken bookmarks, a beep when lines run past a length you choose (with NVDA+g to jump to the first overflowing character), context when moving to a matching brace, accessible autocomplete with sounds and braille, and spoken results while you use incremental search. Originally written by me and Tuukka Ojala, with later features from Ken Chang, Robert Hänggi, and Andre9642.
Document Formatting Rotor
Change NVDA's document formatting settings without digging through menus. Latest version: 1.2.
Press NVDA+g to open the rotor. Up and down arrows move through settings, left and right switch categories, space cycles a setting's value, and enter saves. You can also type part of a setting's name to search for it.
Accent Manager
Type accented characters without switching to the international keyboard. Latest version: 1.0.
Press NVDA+/ to enter Accent Manager, pick acute, tilde, or grave with the arrow keys, then type the letters you want accented. NVDA+c toggles capitals. When you press escape, the characters you built up are typed into the app you were in.
Crash Hero
Saves NVDA crash dumps, with a note about what you were doing. Latest version: 2.0.4.
After NVDA crashes and restarts, Crash Hero asks what you were doing and saves your answer together with the crash dump in a timestamped folder. That makes it much easier to file a useful bug report later.
Console Timer
Stops NVDA from reading the wrong character in laggy terminals. Version 1.0.
Over SSH, NVDA can give up waiting for the cursor to move and read the wrong letter. In a terminal, press NVDA+shift+page up to make NVDA wait longer, or NVDA+shift+page down to make it wait less.
NVDA now has a similar setting built into its advanced settings, so most people won't need this. It can still help in the occasional case the built-in setting doesn't cover.
TDSR NVDA server
Lets NVDA speak for TDSR, a console screen reader that runs on the remote machine.
Abandoned. No longer maintained, but kept up as a reference for anyone building a similar bridge.
TDSR runs on the machine you SSH into, so it sees exactly what changed in the terminal instead of NVDA having to guess across a laggy connection. This add-on turns NVDA into TDSR's speech server.
LaTeX Access matrix browser
Reads LaTeX math in NVDA and lets you explore matrices cell by cell.
Abandoned. I haven't worked on this since 2017, and it bundles an old copy of latex-access. It may not work with current versions of NVDA.
Built on the latex-access engine. I wrote it while taking calculus and linear algebra: it reads LaTeX as math in speech and braille, and lets you save matrices to a browser where you move through cells with the arrow keys and between matrices with tab.
Linux and desktop tools
aproc
An accessible process manager for Linux terminals.
Tools like top and htop redraw constantly and lay information out in tables and graphs, which is hard to use with a screen reader. aproc takes a snapshot of your processes and leaves it still until you press Ctrl-R. You can sort, filter, view details, send signals, or run a command like strace -p {pid} against the selected process, all from the keyboard. I designed it, and Claude wrote the code.
Virtual Clock Tower
A clock tower for your computer: it gongs on the hour and plays a jingle every 15 minutes.
Abandoned. Written in 2014 for Python 2.7 and not updated since.
You can swap in your own bell sounds, and a night mode keeps it quiet between 10 PM and 8 AM.
Mobile
Bold Explorer for Android
Offline GPS trail navigation for blind hikers and off-road explorers.
Audio is the main interface. Bold Explorer follows trails and guides you along them with spatial (panned) tones, a compass, and speech, all without a data connection. It imports and exports GPX files. The navigation logic lives in a Kotlin Multiplatform module, so an iOS version is possible later.
Web and self-hosted tools
shortlinks
Personal short links (like go/something) managed through Caddy's API.
A small Rust tool that adds and removes redirect routes in a running Caddy server through its admin API, so you can type a short name in your browser and land on the full URL.
email_form
A simple contact form backend written in Flask.
Accepts form submissions, checks them with Cloudflare Turnstile to keep out spam, and emails them to you through Mailgun. Includes a Dockerfile and an example systemd unit. A version of it runs the contact page on this site.
Image describer
A prototype that uses AI to describe event photos for blind participants.
Adaptive sports groups take lots of photos, but blind participants can't tell which ones they're in or which are worth keeping. You give it a folder of images and a short file describing the people and places to look for ("Derek: a climber wearing all black, with a beanie"). It then asks an AI model (Anthropic, Gemini, or OpenAI) to describe each photo and say who is in it.
tzCon
Converts event times on a web page to each visitor's local time zone.
Drop tzcon.js into a page and mark times with class="tzcon". Written for online conferences with attendees around the world. Visitors without JavaScript see the original UTC times, along with a note you provide.
remove-aria
A browser extension that strips ARIA attributes from a page.
Abandoned. Written in 2017. Modern sites depend heavily on ARIA, so removing it is likely to make things worse rather than better.
Some sites used to have ARIA markup so broken that a page read better with all of it removed. This Firefox extension did that with one click.
Games
Audio Whack-a-Mole
Whack-a-mole played entirely by sound.
When you hear static, press the key in the number row that matches where the sound is coming from. You get 60 seconds, with randomly generated 8-bit arcade music in the background, and a bonus round if you score over 80. I wrote it in high school, and a web version is in progress.