This note describes how the example web UI under examples/docker/server/webui/ talks to audiocpp_server, which endpoints are browser-facing, and how voice catalogs are populated for different TTS families.
The web UI is not served by audiocpp_server directly. A small Node server serves the static frontend and proxies browser API requests to the audio.cpp backend.
Browser-visible routes on the web UI server:
| Route | Purpose |
|---|---|
GET / |
Serves the web UI HTML. |
GET /api/* |
Proxies requests to the configured AUDIOCPP_API_ORIGIN. |
GET /__webui/config |
Returns the proxy target origin for diagnostics. |
GET /__webui/voice-catalog?model=<id> |
Web UI fallback metadata derived from server.json. |
This means:
http://<webui-host>:<webui-port>/api/v1/modelsreachesaudiocpp_serverhttp://<webui-host>:<webui-port>/v1/modelsdoes not; it is a web UI route and falls back to the app shell
The example web UI is a small Node-served app under examples/docker/server/webui/.
- Start it with
npm start - Lint it with
npm run lint
npm run lint uses the checked-in eslint.config.js plus npx eslint@9, so the example does not need a separate bundler or build pipeline.
The frontend currently relies on these proxied backend routes:
| Route | Used for |
|---|---|
GET /api/health |
Backend readiness and configured model count |
GET /api/v1/models |
Available model ids and family metadata |
GET /api/v1/audio/voices?model=<id> |
Voice catalog for the active TTS model |
POST /api/v1/audio/speech |
TTS requests, as JSON or multipart uploads |
POST /api/v1/audio/transcriptions |
ASR requests |
GET /v1/audio/voices?model=<id> is an audio.cpp extension used by the web UI to populate voice pickers. It is not part of the OpenAI API.
The normalized response shape is:
{
"voices": ["alba", "cosette", "speaker_a"],
"presets": [
{
"id": "speaker_a",
"voice_id": null,
"voice_ref": "/models/vibevoice/1.5B/voices/speaker_a.wav",
"reference_text": null,
"is_default": false
}
],
"samples": [
{
"id": "speaker_a",
"path": "/models/voices/speaker_a.wav"
}
]
}Field meanings:
voices: flat list for generic voice selectors and backward compatibilitypresets: configuredvoice_presetswith enough metadata to resolvevoice_idorvoice_refsamples: discovered shared.wavsample files from top-levelvoice_samples_base
Backend aggregation rules:
voice_presetsfromserver.jsonbecomevoices[]andpresets[]- model-native cached voices such as
model_root/embeddings/*.safetensorsbecomevoices[] - top-level
voice_samples_basebecomessamples[]
The frontend normalizes backend and fallback metadata into one catalog provider, then lets each TTS family choose its own UI and serializer rules:
- generic families use
voices[]and send JSON with the OpenAI-stylevoicefield pocket_ttsoffers either built-in/cachedvoices[]or clone mode with eithersamples[]or a local uploaded WAV sent asvoice_refvibevoicerenders an ordered speaker list and keeps sharedsamples[], preset-backedpresets[].voice_ref, and local uploaded WAVs as separate row sources; the request serializer sends those entries through orderedvoice_samples- families that do not consume raw WAV clone references ignore
samples[]
If the backend only returns the legacy flat shape:
{ "voices": ["scarlett", "ashley_johnson"] }the web UI fallback route GET /__webui/voice-catalog?model=<id> reads examples/docker/server/server.json and reconstructs:
presets[]from configuredvoice_presetssamples[]from top-levelvoice_samples_base, if configured
This keeps the browser code simple while still allowing the separate web UI to work against older backend builds that do not yet expose structured voice metadata.
For families that use named built-in or cached voices, no extra config is required beyond the model path and any optional voice_presets.
For families that use raw reference WAV libraries, configure one of:
voice_presetswithvoice_refentries- top-level
voice_samples_basepointing at a directory of.wavfiles
Example:
{
"voice_samples_base": "/models/voices",
"models": [
{
"id": "vibevoice_1.5b",
"family": "vibevoice",
"path": "/models/vibevoice/1.5B",
"task": "tts",
"mode": "offline",
"voice_presets": {
"scarlett": {
"voice_ref": "/models/voices/scarlett.wav"
}
}
}
]
}Common failure patterns:
- Calling
/v1/...on the web UI port instead of/api/v1/... - Running an older
audiocpp_serverbuild that still returns onlyvoices[] - Selecting
vibevoicewhen neithervoice_presets.voice_refnor top-levelvoice_samples_baseis available - Expecting
npm run build; this example web UI is served directly by Node and currently ships withnpm startandnpm run lint, but no separate build step - Serving the web UI without access to the intended
server.json, which disables the fallback catalog enrichment