EasyRead is a Firefox WebExtension for Zen Browser that reads selected webpage text aloud through a locally hosted OpenAI-compatible text-to-speech service.
It is designed for a Linux + Zen Browser setup: select text, right-click Read with EasyRead, and listen immediately in the browser without manually downloading audio files.
EasyRead reading selected text from a GitHub repository.
EasyRead generating audio from selected Wikipedia content.
-
Linux or another desktop OS that can run Zen Browser.
-
Zen Browser with Firefox-compatible extension support.
-
A locally hosted OpenAI-compatible TTS endpoint.
-
Default endpoint expected by EasyRead:
http://localhost:8880/v1
EasyRead sends selected text only to the configured TTS endpoint. It does not collect telemetry, load remote scripts, or send text to any other service.
EasyRead calls:
POST {API_BASE_URL}/audio/speech
Content-Type: application/json
Accept: audio/*
Request body:
{
"model": "kokoro",
"input": "Selected text goes here",
"voice": "af_heart",
"speed": 1,
"response_format": "mp3"
}The response must be a non-empty audio response such as audio/mpeg or audio/wav.
Use this during development or for quick local installation:
-
Open Zen Browser.
-
Go to:
about:debugging#/runtime/this-firefox -
Click Load Temporary Add-on....
-
Select this file from the project:
easyread/manifest.json -
EasyRead should appear in the temporary extensions list and in the toolbar/extensions area.
Temporary add-ons are removed when the browser restarts. For normal installation, package the extension and sign it through Mozilla's add-on workflow or use a developer/unbranded Firefox-compatible build that permits unsigned extensions.
- Open EasyRead's popup from the toolbar.
- Click Options.
- Configure:
- API base URL: default
http://localhost:8880/v1 - TTS model: default
kokoro - Voice: default
af_heart - Speech speed: default
1.0 - Audio response format: default
mp3
- API base URL: default
- Click Save settings.
Settings are stored with Firefox extension storage on the local browser profile.
- Start the local TTS server.
- Open EasyRead Options.
- Click Test Connection.
EasyRead sends a short TTS request ("EasyRead test.") to the configured /audio/speech endpoint. A successful test reports the returned audio content type and byte count.
If the server is unavailable, EasyRead shows a human-readable error, for example:
EasyRead could not connect to your local TTS server. Check that your TTS service is running at http://localhost:8880/v1.
- Visit any normal webpage.
- Select text.
- Right-click the selected text.
- Choose Read with EasyRead.
- Select text on the page.
- Open the EasyRead toolbar popup.
- Click Read selected text.
Default shortcuts:
- Alt+Shift+R: read selected text.
- Alt+Shift+P: play/pause current EasyRead audio.
- Alt+Shift+S: stop playback.
Firefox/Zen may allow changing extension shortcuts from the browser's add-on shortcut settings if a shortcut conflicts with your desktop environment.
When EasyRead is active, a small floating player appears at the bottom-right of the page.
The player includes:
- Current playback state.
- Play/pause.
- Stop.
- Restart.
- Close.
- Playback progress when the browser can determine audio duration.
Closing the player only hides it. Selecting text and using EasyRead again brings it back. The popup's Show player button also brings it back on the active tab.
EasyRead trims unnecessary whitespace before sending text. Empty selections are rejected.
Long selections are split into chunks of about 3,500 characters and played sequentially. Very large selections over 60,000 characters are rejected with an explanation so text is not silently lost or skipped.
- Make sure text is selected before right-clicking.
- Reload the temporary add-on from
about:debugging#/runtime/this-firefox. - Some restricted browser pages do not allow content scripts. Test on a normal webpage.
- Confirm the server is running.
- Open the configured base URL in a terminal or browser if your service exposes a health page.
- Confirm EasyRead's API base URL includes
/v1if your server expects OpenAI-compatible paths. - Try the Options page Test Connection button.
- Check that your server implements
POST /v1/audio/speech. - Check the configured model and voice.
- Check server logs for unsupported
response_format,speed, model, or voice values.
- Try
mp3orwavas the response format. - If the browser blocks autoplay, press Play in the floating EasyRead player.
- Confirm the server returns an
audio/*content type and a non-empty response body.
- Another extension or desktop shortcut may already use it.
- Open the browser's extension shortcut settings and assign a different shortcut.
Firefox-compatible browsers do not allow extensions to run content scripts on some internal pages, add-on pages, and browser-controlled documents. Use EasyRead on normal webpages.
easyread/
├── manifest.json
├── README.md
├── LICENSE
├── icons/
├── src/
│ ├── background/
│ │ ├── background.js # Context menu, shortcuts, popup messages, settings bridge
│ │ └── tts-client.js # OpenAI-compatible TTS fetch, timeout, audio validation
│ ├── content/
│ │ ├── content.js # Text selection, chunking flow, page message handling
│ │ ├── player.js # Floating in-page audio player
│ │ └── player.css
│ ├── popup/
│ │ ├── popup.html
│ │ ├── popup.css
│ │ └── popup.js
│ ├── options/
│ │ ├── options.html
│ │ ├── options.css
│ │ └── options.js
│ └── shared/
│ ├── browser.js # Firefox WebExtension API compatibility shim
│ ├── settings.js # Defaults, validation, storage helpers
│ └── text.js # Whitespace cleanup and safe chunking
EasyRead uses Manifest V2 because it remains the most compatible choice for Firefox WebExtensions and Zen Browser. It uses persistent background scripts instead of Chrome-specific service workers.
Flow:
- The user selects text in a webpage.
- Context menu, shortcut, or popup sends
EASYREAD_READ_SELECTIONto the content script. - The content script normalizes and chunks text.
- The content script asks the background script to generate each audio chunk.
- The background script loads settings and calls
{baseUrl}/audio/speech. - Returned audio is converted to a
data:URL and sent back to the content script. - The floating player plays chunks sequentially.
Use the Options page and change TTS model or Voice. Values are passed directly to the local OpenAI-compatible service, so the valid names depend on the model/server that is running.
Examples:
- Model:
kokoro(default), or another model exposed by your local TTS server. - Voice:
af_heart(default), or any voice exposed by your local Kokoro server.
After changing model or voice, click Save settings, then Test Connection.
No build step is required. The source files are the extension.
To create a ZIP package from the repository root:
cd easyread
zip -r ../easyread-1.0.0.zip manifest.json README.md LICENSE icons srcFor normal Firefox/Zen installation outside temporary debugging mode, the package generally needs to be signed through Mozilla Add-ons unless your browser build/profile explicitly allows unsigned extensions. Upload the ZIP to Mozilla's add-on signing workflow or use the browser-supported developer installation path for unsigned extensions.
- No
eval. - No remote scripts.
- No telemetry.
- No API key storage.
- Selected text is sent only to the configured endpoint.
- The extension injects a local content script on normal webpages so it can read the user's selection and show the floating player.
- Required network permissions are limited to local HTTP/HTTPS hosts:
localhostand127.0.0.1. - If you configure a non-local endpoint, EasyRead asks the browser for permission to contact that endpoint's origin.
If you point EasyRead at a non-local URL, selected text will be sent there. Use a trusted endpoint.

