Pocket TTS Browser Engine

July 4, 2026 ยท View on GitHub

Pocket TTS Browser Engine is a fully local text-to-speech extension stack for Chrome and Edge. It exposes Pocket TTS voices through the browser TTS API so extensions and websites that already use chrome.tts can speak through a local audio.cpp server without any cloud API, subscription, or token cost.

What We Built

This project has four main parts:

  1. A Manifest V3 browser extension in extension that registers Pocket voices with chrome.ttsEngine.
  2. A local audio.cpp inference server that serves Pocket TTS audio over http://127.0.0.1:8080.
  3. A native messaging bridge installer in scripts that registers a per-user native host in Windows for Chrome and Edge.
  4. A browser UI for status, testing, bridge validation, side panel controls, and installer ID management.

Current Capabilities

  • Registers 8 Pocket voices for Chrome and Edge.
  • Verifies local server health from the extension UI.
  • Verifies native messaging registration in HKCU for both Chrome and Edge.
  • Supports popup controls and a full side-panel UI based on options.html.
  • Plays speech entirely locally using Pocket TTS through audio.cpp.
  • Supports Microsoft Read Aloud style chunked onSpeak traffic with queued fallback playback.
  • Supports one-click native bridge self-update from the side panel after the first bridge install.
  • Supports custom local reference voices cloned from a short WAV sample, with good results from about 10 seconds of clean audio.

Prerequisites

Before this extension can speak, you need a working local audio.cpp build on the same Windows machine.

Required pieces:

  • audiocpp_server.exe
  • audiocpp_cli.exe
  • Pocket TTS model files
  • a valid server.json

The current extension defaults expect these paths:

C:\Projects\audio.cpp\build\windows-cuda-release\bin\audiocpp_server.exe
C:\Projects\audio.cpp\build\windows-cuda-release\bin\audiocpp_cli.exe
C:\Projects\audio.cpp\server.json
C:\Projects\audio.cpp\models\pocket-tts

Default local server endpoint:

http://127.0.0.1:8080

Important:

  • audio.cpp produces both the server and the CLI from the same build tree.
  • Built-in Pocket voices use the HTTP server on 127.0.0.1:8080.
  • Custom reference voices use audiocpp_cli.exe through the native bridge.
  • This repo does not build audio.cpp for you. It expects those binaries and models to already exist.
  • The local server URL can be changed in the extension UI if your audio.cpp server runs on a different localhost port.
  • The server EXE path, CLI EXE path, server.json path, and Pocket model path can all be changed in the extension UI.

Runtime Model

There are two local synthesis paths:

  1. Built-in Pocket voices
  • Browser TTS request -> extension -> local audiocpp_server.exe over HTTP
  1. Custom reference voices
  • Browser TTS request -> extension -> native messaging host -> local audiocpp_cli.exe --voice-ref
  • You can add a custom local voice in the UI by uploading a short clean WAV sample; about 10 seconds is a practical reference length.

Both paths are local-only when you are using Pocket voices from this extension.

Architecture

Extension

The extension service worker in background.js handles:

  • Voice registration and voice mapping.
  • Native messaging calls for bridge status and server launch.
  • Browser TTS requests through both:
    • chrome.ttsEngine.onSpeakWithAudioStream
    • chrome.ttsEngine.onSpeak

The stream path is present for browsers that route TTS through onSpeakWithAudioStream.

The production fallback path currently matters most: Chrome/Edge often routes requests through onSpeak, especially from consumers like Microsoft Read Aloud. In that case the extension fetches WAV audio from the local Pocket TTS server and plays it through an offscreen document.

Offscreen Playback

Because MV3 service workers do not have direct DOM audio playback, the extension uses:

This offscreen document receives WAV bytes from the service worker and plays them using Web Audio.

Native Messaging Bridge

The native bridge consists of:

The installer:

  • Compiles the native host into %LOCALAPPDATA%\PocketTTSEngine\native-host
  • Writes the manifest for com.pockettts.engine
  • Registers it in:
    • HKCU\Software\Google\Chrome\NativeMessagingHosts\com.pockettts.engine
    • HKCU\Software\Microsoft\Edge\NativeMessagingHosts\com.pockettts.engine

The host supports:

  • ping
  • startServer
  • stopServer
  • verifyRegistration
  • health

Why The Final Design Looks Like This

During integration we confirmed a few important browser behaviors:

  • The extension is correctly registered when voices appear in chrome.tts.getVoices.
  • Chrome/Edge can route speech through onSpeak, not just onSpeakWithAudioStream.
  • Microsoft Read Aloud sends many small onSpeak requests rapidly, so fallback playback must be queued instead of handled in parallel.
  • Offscreen playback is the reliable MV3-compatible solution for local audio playback from extension code.

That is why the extension now includes:

  • stream support
  • queued fallback playback
  • offscreen audio
  • side panel support
  • bridge registration verification

Installation

1. Build or obtain audio.cpp

Make sure your audio.cpp checkout already contains:

  • build\windows-cuda-release\bin\audiocpp_server.exe
  • build\windows-cuda-release\bin\audiocpp_cli.exe
  • models\pocket-tts\...
  • server.json

If your current workflow is:

(venv) (base) PS C:\Projects\audio.cpp> .\build\windows-cuda-release\bin\audiocpp_server.exe --config server.json

that is fine for manual launch and validation.

2. Verify the server can run locally

Manual validation command:

cd C:\Projects\audio.cpp
.\build\windows-cuda-release\bin\audiocpp_server.exe --config server.json

Expected listening endpoint:

http://127.0.0.1:8080

If your server is running on a different local port, you can change it later in:

  • Server Info -> Set Server URL

If your audio.cpp checkout lives somewhere else, you can also update:

  • Server Info -> Runtime Paths -> Save Runtime Paths

3. Load the extension unpacked

Load extension as an unpacked extension in Chrome or Edge.

4. Install the native bridge

First install note:

  • the very first native bridge install is still a one-time manual bootstrap
  • after a current bridge is installed, the side panel button can update it automatically

Run:

powershell -ExecutionPolicy Bypass -File "C:\Projects\pocket-tts-engine\scripts\install-native-bridge.ps1" -ExtensionId "<your-extension-id>"

You can also use the extension UI by entering either:

  • Install / Update Bridge
  • a raw extension ID
  • a full chrome-extension://.../ URL

Behavior:

  • if no compatible bridge is installed yet, the button copies the manual bootstrap command
  • once a current bridge is installed, the same button performs a one-click self-update

5. Reload the extension

Reload the unpacked extension after installing or updating the native bridge.

6. Open the side panel and verify status

The options UI should confirm:

  • server health
  • bridge install status
  • Chrome and Edge native host registration
  • loaded Pocket voices

Using The UI

The popup provides:

  • quick speech tests
  • voice selection
  • side panel opening
  • a collapsible Add Installer Extension ID section
  • a compact Runtime Setup summary for the active local endpoint and executable paths

Side Panel / Options UI

The side panel uses options.html and provides:

  • server health checks
  • bridge readiness checks
  • registry verification for Chrome and Edge
  • voice listing
  • speech testing
  • native bridge install/update flow
  • server start via native bridge
  • configurable local server URL
  • configurable server, CLI, config, and model paths
  • runtime path reset back to defaults

Start Server Button

The extension includes a Start Server button in the options UI.

What it does:

  • calls the native messaging host
  • launches audiocpp_server.exe --config <server.json>
  • does not require an activated Python venv
  • does not require an activated Conda base environment

Why that works:

  • the extension is starting the compiled .exe, not a Python entry point
  • the native host uses Process.Start(...) directly

The startup flow now validates the configured runtime paths before launch and writes the results into the UI log, so missing EXEs, config files, or model directories show up before a launch attempt is made.

Important caveat:

  • the native host currently sets the process working directory to the server executable folder, not the audio.cpp repo root

That means the button is reliable when:

  • server.json is valid at the configured absolute path, and
  • any paths inside server.json are absolute, or otherwise resolve correctly from the server executable location

Manual launch from C:\Projects\audio.cpp may still be safer when:

  • server.json uses relative model paths that assume the repo root as the working directory
  • you are still iterating on your audio.cpp layout

In other words:

  • no, the server does not need (venv) (base) specifically for runtime
  • yes, working-directory-sensitive configs can still matter
  • the current UI button is useful, but path handling should be considered part of the prerequisite setup

Custom Local Port

By default the extension assumes:

http://127.0.0.1:8080

If your local audio.cpp server listens on a different localhost port, open the side panel and update:

  • Server Info -> Local Server Endpoint -> Set Server URL

Supported values are intentionally limited to local HTTP endpoints such as:

  • http://127.0.0.1:8080
  • http://127.0.0.1:9000
  • http://localhost:8080

This keeps the extension aligned with the local-only privacy model.

Configurable Runtime Paths

The extension no longer requires the default C:\Projects\audio.cpp layout.

Open the side panel and update:

  • Server Info -> Runtime Paths

You can override:

  • audiocpp_server.exe
  • audiocpp_cli.exe
  • server.json
  • models\pocket-tts

Use:

  • Save Runtime Paths to store a custom local layout
  • Reset Defaults to return to the built-in C:\Projects\audio.cpp assumptions

These saved paths are used by:

  • Start Server
  • Stop Server
  • custom reference voice synthesis through audiocpp_cli

Verification Checklist

When the stack is working, the UI should show logs like:

  • Loaded 8 Pocket voices
  • Server is healthy
  • Bridge ready: 1.x.x
  • Bridge registered in Chrome and Edge

And speech requests should produce playback instead of immediate Speech stopped.

Repo Layout

pocket-tts-engine/
|-- README.md
|-- docs/
|   |-- bridge-setup.md
|   `-- notes.md
|-- extension/
|   |-- background.js
|   |-- manifest.json
|   |-- offscreen.html
|   |-- offscreen.js
|   |-- options.html
|   |-- options.js
|   |-- popup.html
|   |-- popup.js
|   `-- icons/
`-- scripts/
    |-- install-native-bridge.ps1
    `-- PocketTtsNativeHost.cs

Notes

  • This repo contains the browser-side engine and native bridge pieces.
  • The Pocket TTS model files and audio.cpp server binary live outside this repo.
  • The current implementation is optimized for fully local use on Windows with Chrome/Edge.
  • The extension can now be pointed at non-default local audio.cpp layouts through the side panel runtime path settings.