Vocalizer is a native PHP extension by Akram Zerarka that runs text-to-speech synthesis locally, with no API calls and no runtime dependencies. It embeds two inference backends — sherpa-onnx (ONNX Runtime) and audio.cpp (ggml) — and ships as a prebuilt .so binary, so there's nothing to compile.
Here's what the extension gives you:
- Eight model families, one API — Chatterbox, Supertonic, Piper/VITS, Pocket, Kokoro, Kitten, Matcha, and ZipVoice, auto-detected from the model directory
- Voice cloning — Chatterbox clones any voice from a 3–10 second reference WAV across 23 languages
- Anti-hallucination guard — Chatterbox output is checked for skipped text, loops, and silence, and re-synthesized with a fresh seed when it looks suspicious
- Crash isolation — models run in fork mode by default, so an engine crash is retried and reloaded instead of taking down the PHP worker
- Model cache — models load once per PHP worker (LRU eviction,
vocalizer.max_models) and stay warm for subsequent requests - Async synthesis —
speakAsync()returns a job you canwait()on, with per-call timeouts - WAV or raw PCM output — save to a file, get the WAV as a string, or grab float32 PCM
One API Across Eight Model Families
Engine::load() points at a model directory and figures out the backend from its contents. Synthesis is a single speak() call:
use Vocalizer\Engine; $engine = Engine::load('/opt/voices/sherpa-onnx-supertonic-3-tts-int8-2026-05-11'); $res = $engine->speak('Votre commande est prête.', [ 'lang' => 'fr', // required for Supertonic 'voice' => 0, // 0–9 preset voices 'speed' => 1.0, 'timeout_ms' => 30_000,]); $res->save('/var/www/audio/notice.wav');echo $res->seconds, " s in ", $res->generationMs, " ms\n";
Which model to load depends on what you're after:
| Goal | Model | Latency (CPU) |
|---|---|---|
| Best realism, voice cloning | Chatterbox | Slow (~20× real time) |
| Fast multi-language (31 languages) | Supertonic 3 | Real-time |
| Lightweight FR/EN cloning | Pocket TTS | Fast |
| Fastest, one model per locale | Piper/VITS | Very fast |
Voice Cloning with Chatterbox
Chatterbox covers 23 languages with a single ~7.5 GB model and clones a voice from a short reference WAV in the target language:
$engine = Engine::load('/opt/voices/chatterbox', [ 'threads' => 4, 'opts' => ['weight_type' => 'f16'], // default: q8_0]); $res = $engine->speak('Bonjour, votre commande est prête.', [ 'lang' => 'fr', 'reference' => '/opt/voices/refs/fr.wav', 'opts' => [ 'temperature' => 0.6, 'repetition_penalty' => 1.2, 'seed' => 42, ], 'timeout_ms' => 600_000,]); echo $res->qualityRetries; // guard re-syntheses (0 = first output accepted)$res->save('/tmp/out.wav');
Autoregressive TTS models can skip text, loop, or produce silence, which is where the anti-hallucination guard comes in. Vocalizer checks every Chatterbox output against the text length and signal energy, and re-synthesizes suspicious audio with a new seed — two extra attempts by default, configurable via verify_retries. If every attempt fails, it throws a Vocalizer\Exception rather than returning corrupt audio, which makes a fallback to a faster model straightforward:
try { $res = $engine->speak($text, ['lang' => 'fr', 'reference' => $ref]);} catch (\Vocalizer\Exception $e) { $res = Engine::load('/opt/voices/sherpa-onnx-supertonic-3-tts-int8-2026-05-11') ->speak($text, ['lang' => 'fr']);}
Crash Isolation and Async
Native inference engines can crash, and a segfault inside a PHP extension normally kills the FPM worker with it. Vocalizer's default fork isolation mode runs synthesis in a forked child process, so a crash is caught, retried (up to vocalizer.max_retries), and the model reloaded — surfacing as a Vocalizer\CrashException only when recovery fails. Chatterbox is the exception: its ggml thread pool is not fork-safe, so it always runs in direct mode.
For longer texts, speakAsync() moves synthesis off the request path:
$job = $engine->speakAsync($paragraph);$res = $job->wait(30_000) ?? throw new RuntimeException('still running');
Behavior is tuned through php.ini directives: vocalizer.isolation (fork vs. direct), vocalizer.timeout_ms, vocalizer.max_models for the per-worker model cache, and vocalizer.max_concurrency for the async pool. One production note from the README worth repeating: model RAM is per FPM worker, and Chatterbox alone needs several GB.
Installation
Vocalizer requires Linux x86-64 (glibc ≥ 2.28) and PHP 8.4 or 8.5 NTS — Alpine/musl, ARM, and ZTS builds are not supported. The install script downloads the prebuilt extension (~44 MB) and verifies it via SHA256:
curl -fsSL https://raw.githubusercontent.com/akramzerarka/vocalizer/main/install.sh | bash
Models are downloaded separately with the bundled script:
./scripts/download-model.sh chatterbox # ~7.5 GB./scripts/download-model.sh sherpa-onnx-supertonic-3-tts-int8-2026-05-11 # ~120 MB./scripts/download-model.sh vits-piper-en_US-amy-low # ~65 MB
The extension is MIT-licensed and statically links its dependencies, including sherpa-onnx (Apache-2.0), audio.cpp/ggml (MIT), ONNX Runtime (MIT), and espeak-ng (GPL-3.0 phonemization data).
You can find the full API reference, configuration details, and model catalog on GitHub.