Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ A collection of working examples showing how to use Deepgram SDKs with popular p
| [290](examples/290-aws-lambda-python-transcription/) | AWS Lambda Serverless Audio Transcription | Python | AWS Lambda |
| [300](examples/300-spring-boot-live-transcription-java/) | Spring Boot Real-Time Transcription with Deepgram | Java | Spring Boot |
| [310](examples/310-crewai-voice-agents-python/) | CrewAI Voice-Enabled Multi-Agent System with Deepgram | Python | CrewAI |
| [340](examples/340-tauri-live-transcription-rust-ts/) | Tauri Desktop Live Transcription | Python | Tauri |
| [340](examples/340-tauri-live-transcription-rust-ts/) | Tauri Desktop Live Transcription | Rust | Tauri |
| [350](examples/350-asterisk-freeswitch-deepgram-stt-python/) | Asterisk / FreeSWITCH PBX to Deepgram Streaming STT | Python | Asterisk/FreeSWITCH |
| [360](examples/360-kotlin-android-live-transcription/) | Kotlin Android Live Transcription | Kotlin | Jetpack Compose |
| [370](examples/370-swift-ios-live-transcription/) | Swift iOS Live Transcription | Swift | SwiftUI |
Expand Down
5 changes: 5 additions & 0 deletions examples/340-tauri-live-transcription-rust-ts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.env
src/dist/
src/node_modules/
src/src-tauri/gen/
src/src-tauri/target/
134 changes: 96 additions & 38 deletions examples/340-tauri-live-transcription-rust-ts/README.md
Original file line number Diff line number Diff line change
@@ -1,57 +1,115 @@
# Tauri Desktop Live Transcription
# Tauri desktop live transcription

A cross-platform desktop app built with Tauri v2 that captures microphone audio, streams it to Deepgram via WebSocket for real-time transcription, and displays live captions. Tauri's Rust backend handles the Deepgram connection using the official Rust SDK, while the TypeScript frontend captures audio and renders the UI.
Build a Tauri 2 desktop application that transcribes a running application,
all system audio, or a microphone with Deepgram Nova-3. PocketStation captures
audio in Rust on macOS, Windows, and Linux. The TypeScript frontend selects the
source and displays interim and final transcripts with speaker labels.

## What you'll build

A Tauri desktop application with a Rust backend that connects to Deepgram's live STT WebSocket using the Deepgram Rust SDK and a TypeScript frontend that captures microphone audio at 16 kHz, streams it to the backend via Tauri commands, and displays rolling live captions with interim and final results.
The application does not open a microphone unless you select **Default
microphone**.

## Prerequisites

- [Rust](https://www.rust-lang.org/tools/install) 1.70+
- [Node.js](https://nodejs.org/) 18+
- System WebView (WebKitGTK on Linux, WebView2 on Windows, WebKit on macOS) — see [Tauri prerequisites](https://v2.tauri.app/start/prerequisites/)
- Deepgram account — [get a free API key](https://console.deepgram.com/)

## Environment variables
- The current stable [Rust toolchain](https://www.rust-lang.org/tools/install)
- [Node.js](https://nodejs.org/) 18 or later
- The [Tauri 2 prerequisites](https://v2.tauri.app/start/prerequisites/) for your operating system
- A [Deepgram API key](https://console.deepgram.com/)

| Variable | Where to find it |
|----------|-----------------|
| `DEEPGRAM_API_KEY` | [Deepgram console](https://console.deepgram.com/) |
## Run the application

## Install and run
Set your Deepgram API key in the shell that will start Tauri:

```bash
cp .env.example .env
# Add your DEEPGRAM_API_KEY to .env
export DEEPGRAM_API_KEY="your_api_key"
```

Install the frontend packages and start Tauri:

```bash
cd src
npm install
npm run tauri dev
pnpm install
pnpm tauri dev
```

## Key parameters
Choose one source in the application:

- **Running application** captures the application name or identifier you
enter, such as `Zoom`.
- **All system audio** captures the complete desktop mix.
- **Default microphone** asks for microphone access and captures the current
default input device.

Start the application you want to capture before clicking **Start**. If more
than one application matches the value you entered, PocketStation reports the
ambiguity instead of choosing one silently.

| Parameter | Value | Description |
|-----------|-------|-------------|
| `model` | `nova-3` | Deepgram's latest and most accurate STT model |
| `encoding` | `linear16` | 16-bit PCM audio from the microphone |
| `sample_rate` | `16000` | 16 kHz — good balance of quality and bandwidth |
| `interim_results` | `true` | Show partial transcripts as the user speaks |
| `smart_format` | `true` | Auto-capitalisation, numbers, and punctuation |
| `utterance_end_ms` | `1500` | Detect end of speech after 1.5 s of silence |
Use **Pause** to stop sending audio to Deepgram while keeping the selected
native source open. **Resume** continues with new audio; audio produced while
paused is intentionally not transcribed.

## Permissions

macOS asks separately for microphone access and for Screen & System Audio
Recording. Grant only the permission required by the source you selected, then
restart the example if macOS requests it.

Windows uses native process-loopback or system-loopback capture. Linux uses
PipeWire and may display a desktop portal depending on the compositor.

## How audio reaches Deepgram

```text
selected native source
PocketStation 10 ms frames at 48 kHz
mono downmix and linear16 conversion in a PocketStation Connector
Deepgram streaming transcription
interim and final transcript events in Tauri
```

## How it works
Audio stays in the Rust process. Only source selections and transcript events
cross Tauri IPC. PocketStation continues native capture while the Deepgram
worker sends frames over the network, and it reports delivery failure if the
network worker cannot keep up.

The Deepgram request uses these settings:

| Setting | Value |
|---|---|
| Model | `nova-3` |
| Encoding | `linear16` |
| Sample rate | `48000` |
| Channels | `1` |
| Frame duration | `10 ms` |
| Interim results | Enabled |
| Speaker diarization | Enabled |
| Smart formatting | Enabled |
| Utterance end | `1500 ms` |

## Verify the example

Run the source checks:

```bash
DEEPGRAM_API_KEY=test python tests/test_example.py
```

Build both halves of the application:

```bash
cd src
pnpm build
cargo test --manifest-path src-tauri/Cargo.toml --locked
```

1. The Tauri app starts with a Rust backend and a web-based frontend rendered in the system WebView
2. When you click **Start**, the TypeScript frontend requests microphone access via `getUserMedia` at 16 kHz
3. A `ScriptProcessorNode` captures raw PCM audio and converts float32 samples to signed 16-bit linear PCM
4. Audio chunks are sent to the Rust backend via Tauri's `invoke("send_audio", ...)` IPC
5. The Rust backend connects to Deepgram's live STT WebSocket using the official `deepgram` Rust crate with `transcription().stream_request_with_options(...).handle()`
6. A `tokio::select!` loop multiplexes audio forwarding and transcript receiving on the same `WebsocketHandle`
7. Transcript events (interim and final) are emitted back to the frontend via Tauri's event system (`app.emit("transcript", ...)`)
8. The frontend renders rolling captions with final text in white and interim text in grey
For the real service check, run the application with a valid Deepgram API key,
play speech in the selected source, and confirm that interim and final text
appears before stopping the session.

## Starter templates

[deepgram-starters](https://github.com/orgs/deepgram-starters/repositories)
See the [Deepgram starter repositories](https://github.com/orgs/deepgram-starters/repositories)
for additional application templates.
42 changes: 41 additions & 1 deletion examples/340-tauri-live-transcription-rust-ts/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,29 @@
align-items: center;
}

#source-controls {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
width: 100%;
max-width: 640px;
margin-bottom: 20px;
}

label { display: grid; gap: 6px; font-size: 13px; }

select, input {
width: 100%;
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.18);
border-radius: 8px;
color: #fff;
padding: 10px 12px;
font: inherit;
}

[hidden] { display: none !important; }

button {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
Expand Down Expand Up @@ -98,15 +121,32 @@
<div class="logo">
<h1>Deepgram Live Transcription</h1>
</div>
<p class="subtitle">Tauri + Rust + Deepgram Nova-3</p>
<p class="subtitle">PocketStation + Tauri + Rust + Deepgram Nova-3</p>

<div id="source-controls">
<label>
Audio source
<select id="capture-source">
<option value="application">Running application</option>
<option value="system_audio">All system audio</option>
<option value="microphone">Default microphone</option>
</select>
</label>
<label id="application-row">
Application name or identifier
<input id="application" placeholder="Zoom" autocomplete="off" required>
</label>
</div>

<div id="transcript-container">
<div id="transcript">Click Start to begin transcription...</div>
</div>

<div id="controls">
<button id="btn-start">Start</button>
<button id="btn-pause" disabled>Pause</button>
<button id="btn-stop" disabled>Stop</button>
<button id="btn-copy">Copy transcript</button>
<span id="status" class="status">disconnected</span>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
"tauri": "tauri"
},
"dependencies": {
"@tauri-apps/api": "2.2.0"
"@tauri-apps/api": "2.11.1"
},
"devDependencies": {
"@tauri-apps/cli": "2.2.0",
"@tauri-apps/cli": "2.11.4",
"typescript": "5.7.3",
"vite": "6.4.2"
}
Expand Down
Loading