Cloudflare Workers AI
Text-to-speech on Workers AI: Deepgram Aura streaming over a WebSocket or per sentence over HTTP, and MeloTTS, over the AI binding or the REST API.
Overview
@pipedog/cloudflare has three text-to-speech services on Workers AI:
WorkersAIAuraTTSService: Deepgram Aura (@cf/deepgram/aura-2-en,aura-2-es,aura-1), streamed over a WebSocket. Workers AI carries Deepgram's/v1/speakprotocol, so this isDeepgramTTSServiceopening its socket through Workers AI.WorkersAIAuraHttpTTSService: Aura, one request per sentence, the PCM streamed back.WorkersAIMeloTTSService: MeloTTS (@cf/myshell-ai/melotts), one request per sentence. It answers in MP3, so it needs a decoder you pass in.
This package is pipedog's own: Pipecat has no Workers AI services.
Source
packages/cloudflare/src/tts.ts and melotts.ts.
Workers AI models
Speakers, inputs and pricing of each model.
Installation
bun add @pipedog/cloudflareimport { WorkersAIAuraHttpTTSService, WorkersAIAuraTTSService } from "@pipedog/cloudflare/tts";
import { WorkersAIMeloTTSService } from "@pipedog/cloudflare/melotts";Prerequisites
Inside a Worker, pass the AI binding as ai: env.AI. Elsewhere, pass accountId and apiToken
(an API token with the Workers AI permission). See
the speech-to-text page for both paths.
WorkersAIAuraTTSService
Configuration
Prop
Type
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
model | string | "@cf/deepgram/aura-2-en" | WorkersAIAuraModel.AURA_2_EN, .AURA_2_ES or .AURA_1. |
voice | string | the model's default | The speaker: luna (aura-2-en), aquila (aura-2-es), angus (aura-1). See the model pages for the others. |
Usage
import { WorkersAIAuraModel, WorkersAIAuraTTSService } from "@pipedog/cloudflare/tts";
const tts = new WorkersAIAuraTTSService({
ai: env.AI,
sampleRate: 24000,
settings: new WorkersAIAuraTTSService.Settings({
model: WorkersAIAuraModel.AURA_2_EN,
voice: "asteria",
}),
});Over the REST API:
const ttsRest = new WorkersAIAuraTTSService({
accountId: process.env.CLOUDFLARE_ACCOUNT_ID ?? "",
apiToken: process.env.CLOUDFLARE_API_TOKEN ?? "",
settings: new WorkersAIAuraTTSService.Settings({
model: WorkersAIAuraModel.AURA_2_ES,
voice: "celeste",
}),
});
Change the voice mid-call with a TTSUpdateSettingsFrame (it reconnects):
// Queue it on the worker, or push it from a processor upstream of the service.
const update = new TTSUpdateSettingsFrame({
delta: new WorkersAIAuraTTSService.Settings({ voice: "orion" }),
});Notes
- Text is sent with
Speakas it arrives and flushed (Flush) at the end of each LLM response; an interruption sendsClear. Output is 16-bit PCM at the service's sample rate. - The session parameters are
speaker,encodingandsample_rate. Deepgram'sspeedisn't in Workers AI's schema and isn't sent.
WorkersAIAuraHttpTTSService
import { WorkersAIAuraHttpTTSService } from "@pipedog/cloudflare/tts";
const ttsHttp = new WorkersAIAuraHttpTTSService({ ai: env.AI, sampleRate: 24000 });
Each sentence is a request for encoding: "linear16", container: "none" at the service's sample
rate, and the body is streamed back as audio frames. Use it when you'd rather not hold a socket open.
WorkersAIMeloTTSService
Configuration
Prop
Type
Usage
import { WorkersAIMeloTTSService } from "@pipedog/cloudflare/melotts";
// Any MP3 decoder that returns per-channel Float32Arrays and a sample rate,
// e.g. mpg123-decoder's MPEGDecoder (bun add mpg123-decoder):
// const decoder = new MPEGDecoder(); await decoder.ready;
// decodeAudio: (mp3) => { decoder.reset(); return decoder.decode(mp3); }
declare function decodeMp3(mp3: Uint8Array): Promise<{
channelData: Float32Array[];
sampleRate: number;
}>;
const melo = new WorkersAIMeloTTSService({
ai: env.AI,
decodeAudio: decodeMp3,
settings: new WorkersAIMeloTTSService.Settings({ language: Language.FR }),
});Notes
- MeloTTS answers with MP3 only. pipedog ships no MP3 decoder (Workers have no Web Audio, and a
WebAssembly decoder has to be imported as a module there), so pass
decodeAudio. The decoded audio is mixed to mono and resampled to the service rate. languagepicks the voice: English, Spanish, French, Chinese, Japanese or Korean.
Runtime notes
| Runtime | Works | What it needs |
|---|---|---|
| Bun | ✅ yes | Nothing. |
| Node.js | ✅ yes | Nothing. |
| Deno | ✅ yes | Nothing. |
| Cloudflare Workers | ✅ yes | Nothing. |
Generated from pipecat.runtimes and pipecat.compat in packages/cloudflare/package.json: ✅ means the compat smoke test passes on that runtime.
- Tested against a local fake of Workers AI over both paths, not against Workers AI itself. Whether
Aura's WebSocket opens through the binding (
websocket: true) is documented for Flux but not for Aura: if it doesn't for you, useWorkersAIAuraHttpTTSServiceor the REST path.
pipedog is an unofficial community TypeScript port of Pipecat. It is not affiliated with or endorsed by Daily or the Pipecat project.
Portions adapted from the Pipecat documentation, Copyright (c) 2024–2025 Daily, BSD-2-Clause (see apps/docs/LICENSE-pipecat-docs).