A proof-of-concept browser-based voice recording & transcription application leveraging native browser APIs for speed, portability, and privacy-first audio handling.
π Launch the live demo
ReVoice is designed to demonstrate the capabilities of modern browser APIs for real-time voice recording and transcription. It avoids heavy local AI models in favor of the Web Speech API for fast transcription and MediaRecorder API for transparent audio persistence, all within the browser's security context.
| Component | Technology | Purpose |
|---|---|---|
| Framework | SvelteKit | Fast, reactive UI components |
| Build Tool | Vite | Modern, zero-config bundler |
| Styling | Tailwind CSS | Utility-first CSS framework |
| Database | Dexie.js | IndexedDB abstraction layer |
| APIs | Web Speech, MediaRecorder, Web Audio | Native browser transcription & audio handling |
src/
βββ lib/
β βββ components/ # Svelte UI components
β β βββ CompatibilityShield.svelte
β β βββ EqVisualizer.svelte
β β βββ TranscriptionProvider.svelte
β βββ engines/ # Transcription engine implementations
β β βββ base.ts # Abstract base class
β β βββ native.ts # Web Speech API implementation
β βββ audio.ts # Audio utilities (MIME detection, cloning)
β βββ compat.ts # Browser compatibility checks
β βββ db.ts # Dexie.js database layer
β βββ types.ts # TypeScript interfaces & types
βββ routes/
β βββ +layout.svelte # Root layout with sidebar & dock
β βββ +layout.js # SPA configuration
β βββ +page.svelte # Main recording dashboard
βββ app.css # Global styles
build/ # Static build output
specification.md # Original product specification
cd c:\Projects\ReVoice\revoice
# Install dependencies
pnpm install
# Start development server
pnpm run dev
# Open http://localhost:5173 in your browser
# Create static build
pnpm run build
# Output: ./build/ (ready for Cloudflare Pages)
# Preview production build locally
pnpm run preview
The core innovation of ReVoice is its pluggable engine architecture. All transcription engines implement the ITranscriptionEngine interface:
interface ITranscriptionEngine {
start(stream: MediaStream, config?: EngineConfig): Promise<void>;
stop(): Promise<void>;
getState(): 'idle' | 'listening' | 'processing';
onResult(callback: (result: TranscriptionResult) => void): () => void;
onError(callback: (error: Error) => void): () => void;
getMetadata(): EngineMetadata;
}
NativeEngine (src/lib/engines/native.ts)
webkitSpeechRecognition (available in Chrome, Edge, Safari)New engines can be added by:
TranscriptionEngineTranscriptionProviderExample: DeepgramEngine, AssemblyAIEngine, LocalMLEngine
Dexie.js Database Schema
db.version(1).stores({
sessions: '++id, timestamp, duration, title, mimeType, engineType',
audioData: '++id, sessionId',
transcripts: '++id, sessionId, text, time',
});
Stores:
Recorded audio and transcripts are stored locally in the browser's IndexedDB. However, the transcription process uses the Web Speech API, which sends audio to cloud services (Google servers for Chrome/Edge, Apple servers for Safari).
ReVoice handles audio capture through a dual-track system:
MediaRecorder Track: Captures audio for persistence
Web Audio Track: Powers the frequency visualizer
Stream Cloning: Uses stream.clone() to feed both tracks from a single microphone input.
The CompatibilityShield component runs on page load to detect:
If any API is missing, users see a warning but can continue (graceful degradation).
audio/mp4 instead of WebMThese are handled transparently in the NativeEngine and audio.ts utilities.
children (slot content)src/lib/components/CompatibilityShield.svelteaudioContext, analyser, barCount, height, barColorsrc/lib/components/EqVisualizer.svelteengine (ITranscriptionEngine), childrensrc/lib/components/TranscriptionProvider.svelte// Create a recording session
const sessionId = await createSession(title, engineType, mimeType);
// Retrieve all sessions (ordered by most recent)
const sessions = await getAllSessions();
// Get specific session
const session = await getSession(id);
// Update session duration
await updateSessionDuration(id, durationMs);
// Delete session and all associated data
await deleteSession(id);
// Store audio blob
const audioId = await storeAudioData(sessionId, blob);
// Retrieve audio for a session
const blob = await getSessionAudio(sessionId);
// Store transcript segment
await storeTranscript(sessionId, text, timeMs, isFinal);
// Get all transcripts for a session
const transcripts = await getSessionTranscripts(sessionId);
// Get full transcript as string
const fullText = await getSessionFullTranscript(sessionId);
// Get database statistics
const stats = await getDBStats();
// { sessionCount: 5, audioCount: 5, transcriptCount: 142 }
// Clear all data
await clearAllData();
// Get supported audio format for current browser
const format = getSupportedAudioFormat();
// { mimeType: 'audio/webm;codecs=opus', codecs: ['opus'] }
// Check if specific type is supported
MediaRecorder.isTypeSupported('audio/webm;codecs=opus');
// Clone a stream for multiple consumers
const cloned = cloneMediaStream(originalStream);
// Create MediaRecorder with auto-detected MIME
const recorder = createMediaRecorder(stream);
// Get file extension from MIME type
const ext = getAudioFileExtension('audio/webm'); // '.webm'
ReVoice builds to a static site compatible with Cloudflare Pages:
# Build production version
pnpm run build
# Deploy ./build directory to Cloudflare Pages
# (via CLI or GitHub Actions)
Configuration:
pnpm run buildbuild/The build/ directory can be deployed to:
src/lib/engines/myengine.ts:import { TranscriptionEngine } from './base';
export class MyEngine extends TranscriptionEngine {
async start(stream: MediaStream, config?: EngineConfig): Promise<void> {
// Initialize your service
}
async stop(): Promise<void> {
// Cleanup
}
getMetadata() {
return {
name: 'My Service',
version: '1.0.0',
type: 'api',
};
}
}
+page.svelte to use the new engine:import { MyEngine } from '$lib/engines/myengine';
let engine = new MyEngine();
Check MIME type support:
console.log(getSupportedAudioFormat());
Monitor transcription events:
engine.onResult((result) => console.log('Transcription:', result));
engine.onError((error) => console.error('Error:', error));
Inspect IndexedDB:
Edit tailwind.config.js to customize colors, fonts, spacing:
export default {
theme: {
extend: {
colors: {
'revoice-blue': '#3b82f6',
},
},
},
};
Development server supports hot module replacementβchanges to .svelte files automatically refresh.
# Clear cache and reinstall
rm -r node_modules .svelte-kit
pnpm install
pnpm run build
MIT (See LICENSE file)
For issues, questions, or feature requests:
Last Updated: January 9, 2026
Status: PoC (Production-Ready for Testing)
npm run build
You can preview the production build with npm run preview.
To deploy your app, you may need to install an adapter for your target environment.