Quickstart (Vue + WebSocket)
Use the Vue composables to capture audio, connect a WebSocket provider, and render partial/final transcripts.
1) Install
Section titled “1) Install”# Requiredpnpm add @saraudio/vue @saraudio/deepgram
# Optional stages (VAD + Meter)pnpm add @saraudio/vad-energy @saraudio/meterThis brings the browser runtime transitively via @saraudio/vue.
2) Create a provider (Deepgram)
Section titled “2) Create a provider (Deepgram)”import { deepgram } from '@saraudio/deepgram';
export const deepgramProvider = deepgram({ model: 'nova-3', auth: { apiKey: '<DEEPGRAM_API_KEY>' },});Note: In production browsers prefer short‑lived tokens from your backend. We’ll cover this in the Auth guide.
Secure variant (browser)
Section titled “Secure variant (browser)”Issue a short‑lived token on your server and provide it via auth.getToken in your provider module:
import { deepgram } from '@saraudio/deepgram';
type EphemeralTokenResponse = { access_token: string; expires_in: number; // seconds};
let tokenCache: { value: string; expiresAt: number } | null = null;const nowMs = () => Date.now();
async function getToken(): Promise<string> { if (tokenCache && tokenCache.expiresAt - nowMs() > 2000) { return tokenCache.value; }
const response = await fetch('/api/deepgram/token', { method: 'POST' }); if (!response.ok) { throw new Error(`Failed to obtain Deepgram token (status ${response.status})`); }
const body: EphemeralTokenResponse = await response.json(); const token = body.access_token; const ttlSeconds = body.expires_in; const safeTtlMs = Math.max(1, ttlSeconds - 2) * 1000;
tokenCache = { value: token, expiresAt: nowMs() + safeTtlMs }; return token;}
export const deepgramProvider = deepgram({ model: 'nova-3', auth: { getToken },});3) Vue component
Section titled “3) Vue component”<script setup lang="ts">import { computed } from 'vue'import { useTranscription } from '@saraudio/vue'import { deepgramProvider } from '@/provider/deepgram'import { vadEnergy } from '@saraudio/vad-energy'import { meter } from '@saraudio/meter'
const { start, stop, connect, disconnect, partial, transcript, status, isConnected,} = useTranscription({ provider: deepgramProvider, transport: 'websocket', autoConnect: true, // connect on mount and start internal recorder // Internal recorder will include VAD + Meter stages stages: [vadEnergy({ thresholdDb: -50, attackMs: 80, releaseMs: 200 }), meter()], connection: { ws: { silencePolicy: 'keep' } }, // 'keep' | 'drop' | 'mute' onError: (e) => console.error(e),})
const connectedText = computed(() => (isConnected.value ? 'Connected' : 'Disconnected'))</script>
<template> <section> <p>Status: {{ status }} ({{ connectedText }})</p>
<div v-if="partial">Partial: {{ partial }}</div> <div v-if="transcript">Final: {{ transcript }}</div>
<div class="controls"> <button @click="start?.()">Start mic</button> <button @click="stop?.()">Stop mic</button> <button @click="connect">Connect</button> <button @click="disconnect">Disconnect</button> </div> </section>
<!-- Optional: drop silence to save bandwidth --> <!-- change to connection: { ws: { silencePolicy: 'drop' } } in the hook options --></template>
<style scoped>.controls { display: flex; gap: .5rem; margin-top: .5rem; }</style>- The hook creates an internal recorder when you don’t pass one; it exposes
start/stophelpers. autoConnect: trueconnects on mount and starts the internal recorder.- Use
silencePolicy: 'drop'to send only speech frames;mutekeeps cadence with zeroed frames.
- Concepts → Controller & Transport (policies, retries)
- Providers → Deepgram / Soniox (WS‑specific options)