TTS
Charivo's TTS layer combines @charivo/tts with a concrete player.
For production browser apps, use the remote player with a server route backed
by @charivo/server/openai.
Recommended Stack
@charivo/tts
@charivo/tts/remote
your /api/tts route
@charivo/server/openai
Basic Setup
import { Charivo } from "@charivo/core";
import { createTTSManager } from "@charivo/tts";
import { createRemoteTTSPlayer } from "@charivo/tts/remote";
const charivo = new Charivo();
charivo.attachTTS(
createTTSManager(createRemoteTTSPlayer({ apiEndpoint: "/api/tts" })),
);
Player Choices
Remote
@charivo/tts/remote- production-oriented browser path
- sends text and voice options to your own API route
Direct OpenAI
@charivo/tts/openai- useful for local development and testing
- exposes credentials to the browser
Browser-Native
@charivo/tts/web- built on the Web Speech API
- useful for prototypes and zero-server flows
- voice behavior depends on browser and OS support
What @charivo/tts Owns
- playback lifecycle
- lip-sync analysis and event emission
- player capability normalization through
playbackModeand optionalaudioMimeType
TTSManager intentionally uses setEventEmitter(...), not the full event bus.
It emits TTS lifecycle and lip-sync events back into core, but it does not
subscribe to upstream Charivo events.
For "audio" playback mode, the manager analyzes the audio element it creates
from the generated bytes with the shared core lip-sync analyzer
(createLipSyncAnalyzer from @charivo/core) and emits tts:lipsync:update.
"audio" mode requires the player to implement generateAudio();
createTTSManager(player) throws an explicit error otherwise. Players that
only implement speak() (e.g. the Web Speech API) must use "web-speech"
mode, whose lip-sync comes from a text-driven simulation instead of real audio
analysis.
Call ttsManager.prepareAudio?.() from a user-gesture handler before the
first speak() so the lip-sync AudioContext starts cleanly on mobile
browsers. Call ttsManager.dispose?.() to release lip-sync audio resources if
your app tears a TTSManager down outside Charivo.dispose(), which already
calls it automatically.
Provider Route
The remote player usually pairs with @charivo/server/openai on the
server:
const provider = createOpenAITTSProvider({
apiKey: process.env.OPENAI_API_KEY!,
defaultVoice: "marin",
defaultModel: "gpt-4o-mini-tts",
});
const audio = await provider.generateSpeech(text, {
voice: "marin",
rate: 1,
});
Alternatives
- Use
@charivo/tts/webwhen you want no backend and browser variability is acceptable. - Use
@charivo/tts/openaiwhen you are debugging OpenAI TTS behavior directly. - Skip TTS when text chat is enough for the current experience.