pipedog is an unofficial community port of Pipecat, not affiliated with Daily.
pipedog
Text-to-speech

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/speak protocol, so this is DeepgramTTSService opening 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.

Installation

bun add @pipedog/cloudflare
import { 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

SettingTypeDefaultDescription
modelstring"@cf/deepgram/aura-2-en"WorkersAIAuraModel.AURA_2_EN, .AURA_2_ES or .AURA_1.
voicestringthe model's defaultThe speaker: luna (aura-2-en), aquila (aura-2-es), angus (aura-1). See the model pages for the others.

Usage

tts.ts
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 Speak as it arrives and flushed (Flush) at the end of each LLM response; an interruption sends Clear. Output is 16-bit PCM at the service's sample rate.
  • The session parameters are speaker, encoding and sample_rate. Deepgram's speed isn't in Workers AI's schema and isn't sent.

WorkersAIAuraHttpTTSService

tts.ts
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

tts.ts
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.
  • language picks the voice: English, Spanish, French, Chinese, Japanese or Korean.

Runtime notes

RuntimeWorksWhat it needs
Bun✅ yesNothing.
Node.js✅ yesNothing.
Deno✅ yesNothing.
Cloudflare Workers✅ yesNothing.

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, use WorkersAIAuraHttpTTSService or 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).

On this page