Quickstart
Build and run your first pipedog pipeline with a custom frame processor.
This quickstart builds the smallest useful pipedog program: a pipeline of two custom frame
processors, run by a PipelineWorker inside a WorkerRunner. No API keys, no network.
Want a voice bot?
The voice bot guide runs an assistant you talk to in the browser, with OpenAI speech-to-text, chat and text-to-speech over SmallWebRTC. Examples lists every runnable example, including the same bot over a WebSocket.
Set up a project
Follow Installation to get @pipedog/core and @pipedog/asyncio
into a project. Any of Bun, Node, Deno or Cloudflare Workers works; this page uses Bun.
Write the bot
Create quickstart.ts. It defines two processors: one upper-cases every TextFrame, the other
prints the text frames that reach it. Both pass every other frame through untouched.
import { , type , } from "@pipedog/core/frames/frames";
import { } from "@pipedog/core/pipeline/pipeline";
import { } from "@pipedog/core/pipeline/worker";
import { type , } from "@pipedog/core/processors/frame_processor";
import { } from "@pipedog/core/workers/runner";
/** Upper-cases every TextFrame and passes every other frame through. */
class extends {
override async (: , : ): <void> {
await super.(, );
if ( instanceof ) {
await this.(new ({ : ..() }), );
} else {
await this.(, );
}
}
}
/** Prints the text frames that reach it. */
class extends {
override async (: , : ): <void> {
await super.(, );
if ( instanceof ) {
.(`${this.}: ${.}`);
}
await this.(, );
}
}
const = new ([new (), new ()]);
const = new (, { : false });
.("on_pipeline_started", async () => {
await .([
new ({ : "hello" }),
new ({ : "from pipedog" }),
new (),
]);
});
.("on_pipeline_finished", async (: , : ) => {
.(`pipeline finished with ${.}`);
});
const = new ({ : false });
await .();
await .();Hover over the code to see the types: this sample is type-checked against the real
@pipedog/core sources when the docs are built.
Run it
bun quickstart.tsAmong the debug logs you will see:
PrintProcessor#0: HELLO
PrintProcessor#0: FROM PIPEDOG
pipeline finished with EndFrame#0What just happened
new Pipeline([...])linked the processors in order, between a source and a sink.new PipelineWorker(pipeline, ...)wrapped the pipeline with lifecycle management. When the runner started it, it pushed aStartFramethrough the pipeline; once that reached the end,on_pipeline_startedfired.- The handler queued two
TextFrames and anEndFrame. TheEndFrameis a control frame, so it is processed in order, after the text frames, and ends the pipeline gracefully once everything ahead of it is flushed. runner.run()returned once its only worker finished.
enableRtvi: false turns off the RTVI processor the worker adds by default, since this bot has no
client. handleSigint: false keeps the runner from installing a Ctrl-C handler; leave it at its
default (true) in a real bot.
Next steps
Examples
Runnable voice bots over WebRTC, WebSocket and Durable Objects.
Frames
System, data and control frames, and how interruptions treat them.
Frame processors
processFrame, pushFrame, directions, setup and event handlers.
Pipelines & workers
PipelineParams, worker events, ending and cancelling.
Interruptions & cancellation
How @pipedog/asyncio gives JavaScript asyncio's cancellation.
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).