Deepgram Core Workflow B: Live Streaming Transcription
Overview
Real-time streaming transcription using Deepgram's WebSocket API. The SDK manages the WebSocket connection via listen.live(). Covers microphone capture, interim/final result handling, speaker diarization, UtteranceEnd detection, auto-reconnect, and building an SSE endpoint for browser clients.
Prerequisites
@deepgram/sdk installed, DEEPGRAM_API_KEY configured
- Audio source: microphone (via Sox/
rec), file stream, or WebSocket audio from browser
- For mic capture:
sox installed (apt install sox / brew install sox)
Instructions
Step 1: Basic Live Transcription
import { createClient, LiveTranscriptionEvents } from '@deepgram/sdk';
const deepgram = createClient(process.env.DEEPGRAM_API_KEY!);
const connection = deepgram.listen.live({
model: 'nova-3',
language: 'en',
smart_format: true,
punctuate: true,
interim_results: true,
utterance_end_ms: 1000,
vad_events: true,
encoding: 'linear16',
sample_rate: 16000,
channels: 1,
});
connection.on(LiveTranscriptionEvents.Open, () => {
console.log('WebSocket connected to Deepgram');
});
connection.on(LiveTranscriptionEvents.Close, () => {
console.log('WebSocket closed');
});
connection.on(LiveTranscriptionEvents.Error, (err) => {
console.error('Deepgram error:', err);
});
connection.on(LiveTranscriptionEvents.Transcript, (data) => {
const transcript = data.channel.alternatives[0]?.transcript;
if (!transcript) return;
if (data.is_final) {
console.log(`[FINAL] ${transcript}`);
} else {
process.stdout.write(`\r[interim] ${transcript}`);
}
});
connection.on(LiveTranscriptionEvents.UtteranceEnd, () => {
console.log('\n--- utterance end ---');
});
Step 2: Microphone Capture with Sox
import { spawn } from 'child_process';
function startMicrophone(connection: any) {
const mic = spawn('rec', [
'-q',
'-r', '16000',
'-e', 'signed',
'-b', '16',
'-c', '1',
'-t', 'raw',
'-',
]);
mic.stdout.on('data', (chunk: Buffer) => {
if (connection.getReadyState() === 1) {
connection.send(chunk);
}
});
mic.on('error', (err) => {
console.error('Microphone error:', err.message);
.();
});
mic;
}
mic = (connection);
process.(, {
mic.();
connection.();
( process.(), );
});
Step 3: Live Diarization
const connection = deepgram.listen.live({
model: 'nova-3',
smart_format: true,
diarize: true,
interim_results: false,
utterance_end_ms: 1500,
encoding: 'linear16',
sample_rate: 16000,
channels: 1,
});
connection.on(LiveTranscriptionEvents.Transcript, (data) => {
if (!data.is_final) return;
const words = data.channel.alternatives[0]?.words ?? [];
if (words.length === 0) return;
let currentSpeaker = words[0].speaker;
let segment = '';
for (const word of words) {
if (word.speaker !== currentSpeaker) {
console.log(`Speaker ${currentSpeaker}: `);
currentSpeaker = word.;
segment = ;
}
segment += ;
}
.();
});
Step 4: Auto-Reconnect with Backoff
class ReconnectingLiveTranscription {
private client: ReturnType<typeof createClient>;
private connection: any = null;
private reconnectAttempts = 0;
private maxReconnectAttempts = 10;
private baseDelay = 1000;
constructor(apiKey: string, private options: Record<string, any>) {
this.client = createClient(apiKey);
}
connect() {
this.connection = this.client.listen.live(this.options);
this.connection.on(LiveTranscriptionEvents.Open, () => {
console.log('Connected');
this.reconnectAttempts = 0;
});
this..(., {
.();
});
..(., {
.(, err.);
.();
});
.;
}
() {
(. >= .) {
.();
;
}
delay = . * .(, .)
+ .() * ;
.++;
.();
( .(), delay);
}
() {
(.?.() === ) {
..(chunk);
}
}
() {
. = ;
.?.();
}
}
Step 5: SSE Endpoint for Browser Clients
import express from 'express';
import { createClient, LiveTranscriptionEvents } from '@deepgram/sdk';
const app = express();
app.get('/api/transcribe/stream', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const deepgram = createClient(process.env.DEEPGRAM_API_KEY!);
const connection = deepgram.listen.live({
model: 'nova-3',
smart_format: true,
interim_results: true,
encoding: 'linear16',
sample_rate: 16000,
channels: 1,
});
connection.on(LiveTranscriptionEvents.Transcript, (data) => {
const transcript = data.channel.alternatives[0]?.transcript;
(transcript) {
res.();
}
});
req.(, {
connection.();
});
});
Step 6: KeepAlive for Long Sessions
connection.on(LiveTranscriptionEvents.Open, () => {
const keepAliveInterval = setInterval(() => {
if (connection.getReadyState() === 1) {
connection.keepAlive();
}
}, 8000);
connection.on(LiveTranscriptionEvents.Close, () => {
clearInterval(keepAliveInterval);
});
});
Output
- Live WebSocket transcription with interim/final results
- Microphone capture pipeline (Sox -> Deepgram)
- Speaker diarization in streaming mode
- Auto-reconnect with exponential backoff and jitter
- SSE endpoint for browser integration
- KeepAlive handling for long sessions
Error Handling
| Issue | Cause | Solution |
|---|
| WebSocket closes immediately | Invalid API key or bad encoding params | Check key, verify encoding/sample_rate match audio |
| No transcripts received | Audio not being sent or wrong format | Verify connection.send(chunk) is called with raw PCM |
| High latency | Network congestion | Use interim_results: true for perceived speed |
rec command not found | Sox not installed | apt install sox or brew install sox |
| Connection drops after 10s | No audio + no KeepAlive | Send connection.keepAlive() every 8s |
| Garbled output | Sample rate mismatch | Ensure audio sample rate matches sample_rate option |
Resources
Next Steps
Proceed to deepgram-data-handling for transcript processing and storage patterns.