A free, open-source text-to-speech web reader for Firefox (Android-first) and Chrome. It reads web pages aloud — generic HTML articles, reader-mode pages, and PDFs — with a page-integrated accordion overlay, prev/next chunk navigation with smart-back, a char-weighted skip-to-section slider with heading markers, saved voice/speed settings, and configurable as-you-read highlighting (off | paragraph | sentence).
Built from scratch in TypeScript. Logic is ported from Read Aloud (MIT) with attribution — this is not a fork.
Greenfield, under active development.
Svelte-equivalent of Bulletproof React, under src/lib/:
features/<feature>/— feature slices, each with anindex.tspublic barrel (+page.svelte/+component.sveltefor UI); internal files are private to the feature.components/ui/— shadcn-svelte primitives (added via the shadcn-svelte CLI).server/— background / offscreen logic.shared/— cross-feature types and utilities (src/lib/utils.tsholds the shadcncnhelper).
| Command | What it does |
|---|---|
pnpm dev |
extension dev — load the extension in a fresh browser profile. |
pnpm build |
extension build — production artifacts. |
pnpm check |
vp check — format + lint + type-aware checks. |
pnpm test |
vp test — Vitest. |
pnpm test:coverage |
vp test --coverage — Vitest with the coverage gate. |
pnpm test:e2e |
Playwright E2E — loads the built extension. |
pnpm lint / pnpm lint:fix |
standalone Oxlint. |
Vitest (bundled in Vite+) is the unit + integration runner; Playwright drives E2E against the built extension. Vitest is configured in the test block of vite.config.ts; Playwright in playwright.config.ts.
- Unit tests live next to the source as
src/lib/**/*.test.ts. Each tests one module through its public surface; browser globals (chrome,speechSynthesis,SpeechSynthesisUtterance) are faked onglobalThisso the test stays fast and deterministic in the jsdom environment without a real browser. - Integration tests live in
tests/and assert cross-module or build-output contracts — e.g.tests/manifest.test.tsguards the cross-browser manifest produced by Extension.js.
pnpm test—vp test, runs the whole unit + integration suite once.pnpm test watch—vp test watchfor red→green iteration.pnpm test:coverage—vp test --coverage, runs the suite with the coverage gate.pnpm test:e2e— Playwright E2E; loads the built extension and drives the overlay (seeplaywright.config.tsande2e/).
Coverage is measured over src/lib (product code only; test files and public barrels are excluded) with the v8 provider. The gate, set in vite.config.ts test.coverage.thresholds, requires ≥80% of lines, branches, functions, and statements. The gate runs only when coverage is enabled (pnpm test:coverage); pnpm test alone stays green.
A single src/manifest.json drives every target. Browser-specific fields use Extension.js prefixed keys (chromium: for Chrome/Edge, firefox: for Firefox/Gecko) that are filtered per build into dist/chrome and dist/firefox.
| Target | Build | Dev load |
|---|---|---|
| Chrome | pnpm build:chrome |
pnpm exec extension dev --browser chrome |
| Firefox | pnpm build:firefox |
pnpm exec extension dev --browser firefox |
Manifest notes:
- Chrome (MV3) declares
offscreen+storagepermissions and<all_urls>host permissions; the offscreen document hosts the WASM Piper audio pipeline. Firefox has nochrome.offscreen— its event-page background already has DOM access — sooffscreenis Chromium-only. - Firefox (MV3) declares
tabs,<all_urls>,storagepermissions and abrowser_specific_settings.gecko_android.strict_min_version(120.0) so AMO lists the add-on for Firefox for Android as well as desktop.gecko.idis@openwebtts. - The toolbar icon activates the in-page overlay (the sole UI surface) on both targets:
action.onClicked→tabs.sendMessage({ type: "openwebtts:activate" })→ the content script renders the overlay. There is no side panel or sidebar.
Firefox for Android is the primary target. Development on a connected Android device/emulator is via manual web-ext sideload (Playwright does not drive Android):
- Build the Firefox artifact:
pnpm build:firefox. - Install
web-ext(pnpm add -g web-ext). - Push and load it on a USB-debug-enabled device/emulator:
web-ext run --target=firefox-android --source=dist/firefox --adb-device <device-id>.
Release install on Android is via AMO signing: submit dist/firefox to addons.mozilla.org, then install from the device's Firefox Add-ons listing.
OpenWebTTS reuses the following free/open-source software:
- Read Aloud — by Hai Phan, MIT License, Copyright (c) 2016 Hai Phan. Foundational reference; TTS/extraction logic ported with attribution.
- Mozilla Readability (
@mozilla/readability) — article text extraction. Apache-2.0. - pdf.js (
pdfjs-dist) — PDF text extraction. Apache-2.0. - Piper / VITS — on-device neural TTS (WASM fallback engine). Piper voice models are CC-BY; each model is attributed individually.
- shadcn-svelte — overlay UI primitives. MIT.
- wikimedia sentencex — sentence segmentation (WASM build) for sentence-granularity highlighting. MIT.
- Extension.js — cross-browser extension builder (
extension dev/extension build). - Vite+ (
vite-plus) — unified lint/format/test toolchain (vp check,vp test). - Oxlint — linter (replaces ESLint).
- Oxfmt — formatter (replaces Prettier), with
sortImports,sortTailwindcss,sortPackageJson.
MIT — see LICENSE. Reused components retain their original licenses (noted above); Piper voice models are CC-BY.