A comprehensive Next.js application for comparing different speech-to-text (STT) transcription services and text-to-speech (TTS) models. Test accuracy, speed, and cost across multiple AI providers with a modern, responsive interface.
- Node.js 20+
- pnpm or npm
- AWS S3 bucket and credentials (if using S3 storage)
- MongoDB database (local or Atlas)
pnpm install # or npm installMONGODB_URI: MongoDB connection stringMONGODB_DB_NAME: Database nameAWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY: S3 programmatic accessAWS_S3_BUCKET: S3 bucket for audio and resultsAWS_S3_REGION: S3 region- Provider API keys as applicable (only the ones you use):
ASSEMBLYAI_API_KEY,GOOGLE_API_KEY, etc.
- App config:
BASE_URL: Public URL (Vercel) or local URLENABLE_STREAMING:true|falsefor streaming features
Note: Never commit secrets. Do not edit .env files here; create them locally/in Vercel.
pnpm dev
# open http://localhost:3000- Create a new Vercel project and import this repo
- Set all environment variables in Vercel Project Settings
- Build defaults are fine (Next.js 15)
- Deploy, then set
BASE_URLto your Vercel URL and redeploy
- Overview, quick links to core flows
- Generate speech (TTS) from text using multiple providers
- Compare audio quality, speed, and cost
- Transcribe uploaded/recorded audio (STT) across providers
- View WER/CER and detailed metrics
- Real-time STT streaming demos (if
ENABLE_STREAMING=true)
- Run controlled experiments, save and analyze results
- View top-performing models by WER/speed/cost
- Review and manage user votes and comparisons
Examples use pnpm; substitute npm run if needed.
- Run all tests:
pnpm test- Verify MongoDB connection:
pnpm test:mongodb- Verify S3 access:
pnpm test:s3- Bulk transcription sample:
pnpm test:bulk-transcription-
POST /api/tts- Generate speech from text. Body includes provider, voice, text.
- Example:
curl -X POST $BASE_URL/api/tts \ -H 'Content-Type: application/json' \ -d '{"provider":"assemblyai","voice":"alloy","text":"Hello world"}'
-
POST /api/transcribe- Transcribe an audio file. Supports multipart form-data.
- Example:
curl -X POST $BASE_URL/api/transcribe \ -H 'Accept: application/json' \ -F 'file=@test-audio/audio/audio_0.wav' \ -F 'provider=assemblyai'
-
GET /api/models- List available TTS/STT models and providers.
-
GET /api/leaderboard- Aggregated model performance scores.
- MongoDB stores experiment metadata, results, votes, and sessions (
lib/mongodb.ts). - S3 stores audio inputs/outputs and large artifacts (
lib/s3-storage.ts,lib/unified-storage.ts). - Client logs and streaming logs available under
app/api/streaming/*andlib/streaming-logger.ts.
- 500 errors on API routes: check provider API keys and network egress on Vercel.
- S3 upload errors: verify
AWS_*and bucket CORS; region must match. - Mongo timeouts: ensure IP allowlist (Atlas) and correct
MONGODB_URI. - Mismatched BASE_URL: set accurate URL in env for callback/webhook flows.
- How do I add a new provider? Add config under
app/api/*and extendlib/tts/*orlib/transcription/*. Then expose inapp/api/modelsand UI components underapp/components. - Can I run without S3? Yes, switch storage to local in
lib/storage.ts(where supported), but S3 is recommended for multi-instance deploys. - Does it work without MongoDB? Some pages may render, but experiments, votes, and results persistence require MongoDB.
- Open issues and PRs are welcome. Please include clear reproduction steps and follow the existing code style.
- Run
pnpm lintandpnpm testbefore submitting PRs.
- Do not share or commit secrets. Report vulnerabilities privately to maintainers.
- Rotate API keys periodically and scope IAM credentials to least privilege.
MIT © 2025 MADAILAB, Mayank Bambal, Rohit Aggarwal. See LICENSE.
Visit Voice LM Arena to test the platform live.
- Key Features
- Tech Stack
- Pages & Features
- Supported Providers
- Quick Start
- Project Structure
- Architecture Overview
- Usage Examples
- API Documentation
- Database Schema
- Available Scripts
- Deployment
- API Key Management
- Advanced Features
- Testing & Validation
- Adding New Models
- Performance Optimization
- Troubleshooting
- FAQ
- Contributing
- License
- Audio Recording: Record audio directly in the browser using Web Audio API
- Audio Upload: Support for MP3, WAV, M4A, WebM, OGG, and Opus formats
- Multiple Providers: Compare 7+ transcription services side-by-side
- Real-time Processing: Live status updates and progress tracking
- Accuracy Metrics: Word Error Rate (WER), Character Error Rate (CER), and Semantic WER
- Cost Analysis: Per-minute pricing and total cost calculations
- Voice Generation: Convert text to speech using multiple TTS models
- Voice Selection: Choose from various voice options (OpenAI) and voice models (Deepgram)
- Audio Playback: Built-in audio player with download functionality
- Quality Options: Different output formats and quality settings
- Round-trip Testing: Generate speech then transcribe back to test accuracy
- Seamless Integration: Generate speech on Speaker page, automatically transfer to Listener page
- Ground Truth Comparison: Original text used as reference for accuracy measurement
- Complete Testing Cycle: Text → Speech → Transcription → Accuracy Analysis
- Session Storage: Automatic data transfer between pages using browser session storage
- Leaderboard: Rank models by accuracy, speed, and cost
- Quadrant Analysis: Visual comparison charts for different metrics
- Historical Data: Track performance over time with MongoDB storage
- Cost Optimization: Find the best balance of accuracy vs. cost
- Experiment Access: Coupon-based access to premium features
- Provider Keys: Automatic API key provisioning for testing
- Feature Control: Enable/disable specific functionality based on coupon
- Usage Tracking: Monitor coupon usage and limits
- Framework: Next.js 15.4.4 with App Router
- Language: TypeScript 5.x
- Styling: Tailwind CSS 4.1.11
- State Management: React Context + useReducer
- Audio Processing: Web Audio API, react-audio-voice-recorder
- UI Components: Heroicons, Lucide React, Framer Motion
- Runtime: Node.js 18+ with Edge Runtime support
- Database: MongoDB with Mongoose ODM
- Storage: AWS S3 integration + in-memory fallback
- API: RESTful endpoints with CORS support
- Authentication: API key-based provider authentication
- Platform: Vercel (optimized)
- Functions: Serverless with configurable timeouts
- Environment: Support for multiple deployment environments
- Monitoring: Built-in error tracking and logging
- Hero Section: Clear value proposition and call-to-action
- Main Options: Two primary testing paths (Listener & Speaker)
- Coupon Entry: Access to experimental features
- Feature Overview: Why choose Voice LM Arena
- Responsive Design: Mobile-first approach with Tailwind CSS
- Audio Input Methods:
- Real-time recording with microphone
- File upload with drag-and-drop support
- Session storage integration for round-trip testing
- Model Selection: Multi-select interface with provider grouping
- Real-time Results: Live transcription with progress indicators
- Accuracy Metrics: WER calculation with detailed breakdown
- Cost Analysis: Per-minute pricing and total cost
- Export Options: Download results and comparisons
- Text Input: Large text area with character count
- TTS Model Selection: Provider-based model grouping
- Voice Options:
- OpenAI: Alloy, Echo, Fable, Onyx, Nova, Shimmer
- Deepgram: 8 different voice models (Thalia, Asteria, etc.)
- Audio Generation: Real-time speech synthesis
- Transcribe Integration: One-click transcription testing
- Audio Management: Play, pause, download, and share
- Performance Rankings: Sort by accuracy, speed, cost, or rating
- Quadrant Analysis: Visual comparison charts
- Cost vs. WER
- WER vs. Speed
- WER vs. CER
- Model Details: Comprehensive model information and ratings
- Filtering Options: By provider, features, and status
- Interactive Charts: Click for detailed model information
| Provider | Models | Features | Pricing |
|---|---|---|---|
| OpenAI | Whisper-1, GPT-4o, GPT-4o Mini | High accuracy, language detection | $0.006/min |
| Deepgram | Nova-3 | Real-time, word timestamps | $0.0043/min |
| DeepInfra | Whisper Large-v3 | Cost-effective, high quality | $0.002/min |
| Mistral | Voxtral Mini | Fast processing, multilingual | $0.003/min |
| ElevenLabs | Scribe | Audio understanding | $0.005/min |
| Google Gemini | Gemini Pro | Advanced audio comprehension | $0.0025/min |
| AssemblyAI | Whisper | Word-level timestamps | $0.004/min |
| Provider | Models | Voices | Features |
|---|---|---|---|
| OpenAI | TTS-1, TTS-1-HD | 6 voices | High quality, natural speech |
| Deepgram | Aura-2 | 8 voice models | Real-time, customizable |
| ElevenLabs | Eleven Multilingual | 30+ voices | Multilingual, emotional control |
- Node.js 18.0.0 or later
- npm or yarn package manager
- API keys for desired providers
- MongoDB database (optional, for advanced features)
git clone <your-repository-url>
cd compare_voice
npm installCreate a .env.local file in the root directory:
# Required API Keys
OPENAI_API_KEY=your_openai_api_key
DEEPGRAM_API_KEY=your_deepgram_api_key
DEEPINFRA_API_KEY=your_deepinfra_api_key
MISTRAL_API_KEY=your_mistral_api_key
ELEVENLABS_API_KEY=your_elevenlabs_api_key
GEMINI_API_KEY=your_gemini_api_key
ASSEMBLYAI_API_KEY=your_assemblyai_api_key
# Optional: Database Configuration
MONGODB_URI=your_mongodb_connection_string
# Optional: S3 Storage
USE_S3_STORAGE=false
AWS_S3_BUCKET_NAME=your_bucket_name
AWS_ACCESS_KEY_ID=your_access_key
AWS_SECRET_ACCESS_KEY=your_secret_key
AWS_S3_REGION=us-east-1
# Optional: Debug Mode
DEBUG=falsenpm run devOpen http://localhost:3000 in your browser.
compare_voice/
├── app/ # Next.js App Router
│ ├── api/ # API Routes (Serverless Functions)
│ │ ├── coupons/ # Coupon validation and management
│ │ │ └── validate/ # Validate coupon codes
│ │ ├── models/ # Model configuration endpoints
│ │ │ └── route.ts # GET model configurations
│ │ ├── save-audio/ # Audio file storage
│ │ │ └── route.ts # POST audio file uploads
│ │ ├── speech/ # Text-to-Speech generation
│ │ │ └── route.ts # POST TTS requests
│ │ ├── transcribe/ # Speech-to-Text transcription
│ │ │ └── route.ts # POST STT requests (parallel processing)
│ │ ├── tts-transcribe/ # Combined TTS + STT workflow
│ │ │ └── route.ts # POST round-trip testing
│ │ └── voices/ # Voice list management
│ │ └── route.ts # GET available voices per provider
│ ├── components/ # React Components
│ │ ├── audio/ # Audio recording/upload components
│ │ │ ├── AudioRecorder.tsx # Browser-based audio recording
│ │ │ └── AudioUpload.tsx # File upload with drag-and-drop
│ │ ├── coupon/ # Coupon system components
│ │ │ └── CouponModal.tsx # Coupon code entry interface
│ │ ├── layout/ # Layout and navigation
│ │ │ ├── Header.tsx # Top navigation bar
│ │ │ └── Footer.tsx # Footer with links
│ │ ├── results/ # Results display components
│ │ │ ├── ResultsTable.tsx # Transcription results comparison
│ │ │ ├── MetricsCard.tsx # Individual metric display
│ │ │ └── WERBreakdown.tsx # Detailed WER analysis
│ │ └── settings/ # Settings management
│ │ └── APIKeyManager.tsx # API key input and validation
│ ├── hooks/ # Custom React Hooks
│ │ ├── useApiKeyManager.ts # API key state management
│ │ ├── useCouponManager.ts # Coupon validation logic
│ │ ├── useWERCalculator.ts # Real-time WER calculation
│ │ └── useStreamingThrottle.ts # Throttle streaming updates
│ ├── lib/ # Core Libraries and Business Logic
│ │ ├── transcription/ # STT provider implementations
│ │ │ ├── openai.ts # OpenAI Whisper integration
│ │ │ ├── deepgram.ts # Deepgram Nova integration
│ │ │ ├── deepinfra.ts # DeepInfra integration
│ │ │ ├── mistral.ts # Mistral Voxtral integration
│ │ │ ├── elevenlabs.ts # ElevenLabs Scribe integration
│ │ │ ├── gemini.ts # Google Gemini integration
│ │ │ └── assemblyai.ts # AssemblyAI integration
│ │ ├── tts/ # TTS provider implementations
│ │ │ ├── openai-tts.ts # OpenAI TTS implementation
│ │ │ ├── deepgram-tts.ts # Deepgram Aura implementation
│ │ │ └── elevenlabs-tts.ts # ElevenLabs TTS implementation
│ │ ├── storage/ # Data persistence layer
│ │ │ ├── memory-storage.ts # In-memory storage (default)
│ │ │ ├── mongodb.ts # MongoDB connection
│ │ │ ├── s3-storage.ts # AWS S3 file storage
│ │ │ ├── unified-storage.ts # Unified storage interface
│ │ │ └── storage-init.ts # Storage initialization
│ │ ├── models.ts # Model configuration and metadata
│ │ ├── coupons.ts # Coupon validation logic
│ │ ├── wer.ts # Word Error Rate algorithms
│ │ ├── utils.ts # Utility functions
│ │ ├── api.ts # API client helpers
│ │ └── client-logger.ts # Client-side logging
│ ├── listener/ # Speech-to-Text testing page
│ │ └── page.tsx # Main STT interface
│ ├── speaker/ # Text-to-Speech testing page
│ │ └── page.tsx # Main TTS interface
│ ├── leaderboard/ # Performance analytics dashboard
│ │ └── page.tsx # Model comparison charts
│ ├── experiments/ # Experimental features page
│ │ └── page.tsx # Beta features access
│ ├── streaming/ # Real-time streaming page
│ │ └── page.tsx # Live transcription demo
│ ├── layout.tsx # Root layout with providers
│ ├── page.tsx # Landing page
│ ├── globals.css # Global Tailwind styles
│ └── types.ts # TypeScript type definitions
├── scripts/ # Utility Scripts and Tools
│ ├── test-mongodb.ts # MongoDB connection test
│ ├── test-s3-storage.ts # S3 storage validation
│ ├── test-storage-system.ts # Full storage system test
│ ├── test-api-endpoints.ts # API endpoint testing
│ ├── test-coupon-system.ts # Coupon validation test
│ ├── test-transcription-formats.ts # Audio format testing
│ ├── create-indexes.ts # Create database indexes
│ ├── upload-models-config.ts # Upload model data to DB
│ ├── check-db-contents.ts # Inspect database contents
│ └── run-all-tests.ts # Execute all test scripts
├── test-audio/ # Test audio files
│ ├── sample.mp3 # Sample audio for testing
│ └── test-formats/ # Various format samples
├── public/ # Static assets
│ ├── logo.svg # Application logo
│ ├── favicon.ico # Favicon
│ └── images/ # Static images
├── .env.local # Local environment variables (gitignored)
├── .env # Environment template
├── next.config.ts # Next.js configuration
├── tailwind.config.ts # Tailwind CSS configuration
├── tsconfig.json # TypeScript configuration
├── package.json # Dependencies and scripts
├── vercel.json # Vercel deployment config
└── README.md # This file
The application follows a modern serverless architecture with clear separation of concerns:
┌─────────────────────────────────────────────────────────────────┐
│ CLIENT (Browser) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Listener │ │ Speaker │ │ Leaderboard │ │
│ │ Page │ │ Page │ │ Page │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ └─────────────────┴──────────────────┘ │
│ │ │
│ ┌────────────▼────────────┐ │
│ │ React Context API │ │
│ │ (State Management) │ │
│ │ + SessionManager │ │
│ └────────────┬────────────┘ │
└───────────────────────────┼──────────────────────────────────────┘
│
┌───────────▼───────────┐
│ API Routes (Edge) │
│ Next.js Server │
│ + ExperimentIdMgr │
└───────────┬───────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
┌───────▼────────┐ ┌───────▼────────┐ ┌──────▼──────┐
│ STT Providers │ │ TTS Providers │ │ Storage │
│ • OpenAI │ │ • OpenAI │ │ • MongoDB │
│ • Deepgram │ │ • Deepgram │ │ • AWS S3 │
│ • DeepInfra │ │ • ElevenLabs │ │ • Memory │
│ • Mistral │ └────────────────┘ └─────────────┘
│ • ElevenLabs │
│ • Gemini │
│ • AssemblyAI │
└────────────────┘
The system uses a centralized session management approach for consistent experiment tracking:
┌─────────────────────────────────────────────────────────────┐
│ ID Management Hierarchy │
├─────────────────────────────────────────────────────────────┤
│ │
│ 🎯 Experiment ID (Primary Key) │
│ ├─ Format: {type}_{sessionId} │
│ ├─ Example: "stt_session_1703123456_abc123" │
│ └─ Purpose: Unique experiment identifier │
│ │
│ 📝 Session ID (Grouping Key) │
│ ├─ Format: session_{timestamp}_{random} │
│ ├─ Example: "session_1703123456_abc123" │
│ └─ Purpose: Groups models from same run │
│ │
│ 📁 Input ID (Deduplication Key) │
│ ├─ Format: MongoDB ObjectId │
│ ├─ Example: "507f1f77bcf86cd799439011" │
│ └─ Purpose: Deduplicates identical inputs │
│ │
│ 🔗 Model Result ID (Individual Key) │
│ ├─ Format: MongoDB ObjectId │
│ ├─ Example: "507f1f77bcf86cd799439012" │
│ └─ Purpose: Individual model result identifier │
│ │
└─────────────────────────────────────────────────────────────┘
- User records/uploads audio → Browser captures audio
- Audio sent to
/api/transcribe→ Server validates request - Server distributes to selected STT providers (parallel processing)
- Each provider returns transcription + metadata
- Server calculates WER/CER metrics against ground truth (if provided)
- Results stored in MongoDB (if configured) or memory
- Real-time updates sent back to client via streaming response
- Client displays comparison table with metrics
- User enters text + selects TTS model → Client validates input
- Request sent to
/api/speech→ Server validates and routes - TTS provider generates audio file
- Audio uploaded to S3 (if configured) or returned as base64
- Audio URL/data stored with metadata
- Client receives audio URL + metadata
- User can play audio or transfer to Listener page for round-trip testing
- User generates speech on Speaker page
- Clicks "Transcribe" → Audio data stored in sessionStorage
- Redirected to Listener page → Audio auto-loaded
- Ground truth = original text
- Transcription tested against all selected STT models
- Complete accuracy analysis displayed
- Factory Pattern: Provider implementations use factory pattern for extensibility
- Strategy Pattern: Different storage strategies (S3, MongoDB, Memory)
- Observer Pattern: Real-time updates using React Context
- Repository Pattern: Unified storage interface abstracts persistence layer
- Adapter Pattern: Standardized API response format across providers
The application uses React Context API with useReducer for complex state:
- API Key Context: Manages provider API keys and validation
- Coupon Context: Handles coupon validation and feature access
- Results Context: Stores transcription/TTS results across pages
- Session Storage: Temporary storage for round-trip workflow data
# Step 1: Navigate to Listener page
# Step 2: Upload or record audio with known text
# Step 3: Enter ground truth text
# Step 4: Select multiple STT models (e.g., OpenAI Whisper, Deepgram Nova)
# Step 5: Click "Transcribe"
# Step 6: Compare WER, CER, and cost metrics
# Expected output:
# - Transcription from each provider
# - Word Error Rate (lower is better)
# - Character Error Rate
# - Processing time
# - Cost per minute# Step 1: Go to Speaker page
# Step 2: Enter text: "The quick brown fox jumps over the lazy dog"
# Step 3: Select TTS model (e.g., OpenAI TTS-1)
# Step 4: Select voice (e.g., Alloy)
# Step 5: Click "Generate Speech"
# Step 6: Click "Transcribe" button on generated audio
# Step 7: Automatically redirected to Listener page
# Step 8: View accuracy of STT models against original text
# Use case: Test how well TTS + STT work together# Step 1: Click "Enter Coupon" on homepage
# Step 2: Enter coupon code (e.g., "BETA2024")
# Step 3: Coupon validated against database
# Step 4: API keys automatically provisioned
# Step 5: Access to premium features enabled
# Step 6: Experiment page unlocked
# Coupon benefits:
# - No need to provide own API keys
# - Access to all providers
# - Usage tracked per coupon# Step 1: Visit /leaderboard page
# Step 2: View model rankings by:
# - Accuracy (WER)
# - Speed (processing time)
# - Cost ($ per minute)
# - Overall rating
# Step 3: Click quadrant charts to see:
# - Cost vs WER trade-offs
# - WER vs Speed analysis
# - Best value models
# Step 4: Filter by provider or featureTranscribe audio using selected STT models.
Request:
{
audioData: string; // Base64 encoded audio
format: string; // Audio format (mp3, wav, m4a, webm, ogg, opus)
groundTruthText?: string; // Optional: for WER calculation
selectedModels: string[]; // Array of model IDs
apiKeys?: { // Optional: custom API keys
openai?: string;
deepgram?: string;
deepinfra?: string;
mistral?: string;
elevenlabs?: string;
gemini?: string;
assemblyai?: string;
};
couponCode?: string; // Optional: for coupon-based access
}Response:
{
results: Array<{
modelId: string;
provider: string;
transcription: string;
confidence?: number;
processingTime: number; // milliseconds
wer?: number; // If ground truth provided
cer?: number; // If ground truth provided
costPerMinute: number; // USD
totalCost: number; // USD
status: 'success' | 'error';
error?: string;
metadata?: {
language?: string;
words?: Array<{word: string; timestamp: number}>;
};
}>;
audioDuration: number; // seconds
audioUrl?: string; // If stored
}Generate speech from text using TTS models.
Request:
{
text: string; // Text to convert (max 4096 chars)
modelId: string; // TTS model ID
voice?: string; // Voice ID (provider-specific)
format?: string; // Output format (mp3, wav, opus)
speed?: number; // Speech speed (0.5-2.0)
apiKeys?: {
openai?: string;
deepgram?: string;
elevenlabs?: string;
};
couponCode?: string;
}Response:
{
success: boolean;
audioUrl?: string; // S3 URL or data URL
audioData?: string; // Base64 if no S3
format: string;
duration?: number; // seconds
modelId: string;
voice: string;
metadata: {
provider: string;
processingTime: number;
costPerCharacter: number;
totalCost: number;
};
error?: string;
}Combined TTS + STT round-trip testing.
Request:
{
text: string; // Original text (ground truth)
ttsModelId: string; // TTS model to use
sttModelIds: string[]; // STT models to test
voice?: string;
apiKeys?: object;
couponCode?: string;
}Response:
{
ttsResult: {
audioUrl: string;
duration: number;
// ... TTS metadata
};
sttResults: Array<{
modelId: string;
transcription: string;
wer: number;
cer: number;
// ... STT metadata
}>;
groundTruth: string;
overallAccuracy: {
avgWER: number;
avgCER: number;
bestModel: string;
};
}Get available STT and TTS models configuration.
Response:
{
sttModels: Array<{
id: string;
name: string;
provider: string;
description: string;
features: string[];
costPerMinute: number;
status: 'active' | 'beta' | 'deprecated';
requiresApiKey: boolean;
supportedFormats: string[];
maxFileSizeMB: number;
}>;
ttsModels: Array<{
id: string;
name: string;
provider: string;
voices: string[];
costPerCharacter: number;
maxCharacters: number;
outputFormats: string[];
}>;
}Validate coupon code and get associated API keys.
Request:
{
couponCode: string;
}Response:
{
valid: boolean;
coupon?: {
code: string;
expiresAt: Date;
usageLimit: number;
usageCount: number;
features: string[];
apiKeys: {
openai?: string;
deepgram?: string;
// ... other providers
};
};
error?: string;
}Get available voices for a TTS provider.
Response:
{
provider: string;
voices: Array<{
id: string;
name: string;
language: string;
gender?: 'male' | 'female' | 'neutral';
description?: string;
previewUrl?: string;
}>;
}{
_id: ObjectId;
modelId: string; // STT model identifier
provider: string; // Provider name
transcription: string; // Transcribed text
audioUrl?: string; // S3 or storage URL
audioDuration: number; // seconds
audioFormat: string; // mp3, wav, etc.
groundTruthText?: string; // Original text for comparison
wer?: number; // Word Error Rate
cer?: number; // Character Error Rate
confidence?: number; // Provider confidence score
processingTime: number; // milliseconds
costPerMinute: number; // USD
totalCost: number; // USD
metadata: {
language?: string;
words?: Array<{word: string; timestamp: number}>;
ipAddress?: string;
userAgent?: string;
};
createdAt: Date;
updatedAt: Date;
}Indexes:
modelId(ascending)provider(ascending)createdAt(descending)wer(ascending) - for leaderboard queries- Compound:
{modelId: 1, createdAt: -1}
{
_id: ObjectId;
modelId: string; // TTS model identifier
provider: string;
inputText: string; // Original text
audioUrl: string; // S3 or storage URL
audioFormat: string;
audioDuration?: number;
voice: string; // Voice ID used
speed?: number; // Speech rate
processingTime: number;
costPerCharacter: number;
totalCost: number;
metadata: {
characterCount: number;
ipAddress?: string;
userAgent?: string;
};
createdAt: Date;
updatedAt: Date;
}Indexes:
modelId(ascending)provider(ascending)createdAt(descending)
{
_id: ObjectId;
ttsModelId: string;
sttModelId: string;
originalText: string; // Ground truth
ttsAudioUrl: string;
transcribedText: string;
wer: number;
cer: number;
totalProcessingTime: number;
totalCost: number;
ttsMetadata: object;
sttMetadata: object;
createdAt: Date;
}Indexes:
- Compound:
{ttsModelId: 1, sttModelId: 1, createdAt: -1} wer(ascending)
{
_id: ObjectId;
id: string; // Unique model identifier
name: string;
provider: string;
type: 'stt' | 'tts';
description: string;
features: string[];
costPerMinute?: number; // For STT
costPerCharacter?: number; // For TTS
status: 'active' | 'beta' | 'deprecated';
requiresApiKey: boolean;
supportedFormats?: string[];
voices?: string[]; // For TTS
rating?: number; // Average rating
popularity?: number; // Usage count
metadata: object;
createdAt: Date;
updatedAt: Date;
}Indexes:
id(unique)provider(ascending)type(ascending)status(ascending)
{
_id: ObjectId;
code: string; // Unique coupon code
apiKeys: {
openai?: string;
deepgram?: string;
deepinfra?: string;
mistral?: string;
elevenlabs?: string;
gemini?: string;
assemblyai?: string;
};
features: string[]; // Enabled features
usageLimit: number; // Max uses
usageCount: number; // Current uses
expiresAt: Date;
isActive: boolean;
metadata: {
createdBy?: string;
notes?: string;
};
createdAt: Date;
updatedAt: Date;
}Indexes:
code(unique)isActive(ascending)expiresAt(ascending)
s3://your-bucket/
├── audio/
│ ├── stt/ # STT audio files
│ │ ├── {timestamp}_{modelId}.{format}
│ │ └── ...
│ └── tts/ # TTS audio files
│ ├── {timestamp}_{modelId}.{format}
│ └── ...
└── exports/ # Exported results
├── {date}_results.json
└── ...
npm run dev- Start development server with Turbopacknpm run build- Build for productionnpm start- Start production servernpm run lint- Run ESLint
npm run db:test- Test MongoDB connectionnpm run db:indexes- Create database indexesnpm run db:upload-models- Upload model configurationnpm run check-db- Check database contents
npm run test:tts-storage- Test TTS storage functionalitynpm run test:tts-stt-storage- Test round-trip storagenpm run test:storage- Test storage configuration
- Push code to Git repository
- Connect repository to Vercel
- Configure environment variables
- Deploy automatically
# Required for Vercel
MONGODB_URI=your_production_mongodb_uri
USE_S3_STORAGE=true
AWS_S3_BUCKET_NAME=your_production_bucket
AWS_ACCESS_KEY_ID=your_production_key
AWS_SECRET_ACCESS_KEY=your_production_secret- Supports any Node.js hosting platform
- Configure environment variables accordingly
- Ensure proper CORS settings
- Set up MongoDB connection (if using)
- OpenAI: Get API Key
- Deepgram: Get API Key
- DeepInfra: Get API Key
- Mistral: Get API Key
- ElevenLabs: Get API Key
- Google Gemini: Get API Key
- AssemblyAI: Get API Key
- Session-based storage (cleared on page refresh)
- No persistent storage of sensitive data
- Real-time API key validation
- Provider-specific key requirements
- In-Memory: Fast, temporary storage (default)
- MongoDB: Persistent storage for analytics
- AWS S3: Scalable file storage for audio files
- Word Error Rate (WER): Industry-standard accuracy measurement
- Character Error Rate (CER): Character-level accuracy
- Semantic WER: Meaning-based accuracy assessment
- Processing Time: Speed comparison across providers
- Cost Analysis: Per-minute and total cost calculations
- Format Support: MP3, WAV, M4A, WebM, OGG, Opus
- File Size Limits: 4MB for Vercel hobby plan
- Quality Optimization: Automatic format conversion
- Real-time Recording: Browser-based audio capture
- Audio Format Testing: Validate different audio formats
- Provider Integration: Test API connections
- Storage Validation: Verify data persistence
- Performance Benchmarking: Compare model performance
# Test transcription formats
npm run test:transcription-formats
# Test storage configuration
npm run test:storage-config
# Test database connection
npm run db:test
# Test coupon system
npm run test:coupon-flow- Add model configuration to database
- Implement provider integration
- Register in factory pattern
- Update type definitions
- Test integration
See ADDING_NEW_MODEL_GUIDE.md for comprehensive instructions on adding new transcription models and providers.
# Clear dependencies and reinstall
rm -rf node_modules package-lock.json
npm install- Verify API keys are correctly set
- Check provider service status
- Ensure proper key format and permissions
- Check file size limits (4MB for Vercel hobby)
- Verify supported audio formats
- Ensure proper audio encoding
- Verify MongoDB URI format
- Check network connectivity
- Ensure proper authentication
Enable debug logging by setting DEBUG=true in environment variables.
- Check audio file sizes
- Monitor API rate limits
- Verify storage configuration
- Check network latency
// Dynamic imports for heavy components
const Leaderboard = dynamic(() => import('@/app/leaderboard/page'), {
loading: () => <LoadingSpinner />,
ssr: false
});- Use Next.js
<Image>component for automatic optimization - Lazy load images below the fold
- Use WebP format with fallbacks
- Implement responsive images
# Analyze bundle size
npm run build
# Check .next/analyze/client.html
# Strategies:
# 1. Use tree-shaking for unused exports
# 2. Import only needed components from libraries
# 3. Use dynamic imports for route-specific code// Memoize expensive calculations
const werScore = useMemo(() =>
calculateWER(transcript, groundTruth),
[transcript, groundTruth]
);
// Prevent unnecessary re-renders
const MemoizedResultsTable = React.memo(ResultsTable);
// Use useCallback for event handlers
const handleTranscribe = useCallback(() => {
// Transcription logic
}, [dependencies]);// Process multiple models simultaneously
const results = await Promise.allSettled(
selectedModels.map(modelId =>
transcribeWithProvider(modelId, audioData)
)
);// In-memory cache for model configurations
const modelCache = new Map();
// Cache API responses (30 minutes)
export const revalidate = 1800;
// CDN caching for static assets# Create proper indexes
npm run db:indexes
# Use projection to limit fields
db.stt_results.find(
{ modelId: "whisper-1" },
{ transcription: 1, wer: 1, _id: 0 }
);
# Aggregation pipelines for leaderboard
db.stt_results.aggregate([
{ $match: { wer: { $exists: true } } },
{ $group: { _id: "$modelId", avgWER: { $avg: "$wer" } } },
{ $sort: { avgWER: 1 } }
]);// Use presigned URLs for direct upload
const presignedUrl = await getPresignedUrl(bucketName, key);
// Implement lifecycle policies
{
"Rules": [{
"Id": "DeleteOldAudio",
"Status": "Enabled",
"ExpirationInDays": 30
}]
}
// Use CloudFront CDN for distribution// Batch multiple transcription requests
POST /api/transcribe
{
audioData: string,
selectedModels: ["whisper-1", "nova-3", "whisper-large-v3"]
}
// Returns all results in one response// Stream results as they complete
export const runtime = 'edge';
async function* streamResults() {
for (const model of models) {
const result = await transcribe(model);
yield JSON.stringify(result);
}
}// Implement per-IP rate limiting
const rateLimit = {
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // Max 100 requests per window
};// Track API response times
console.time('transcription');
const result = await transcribe(audio);
console.timeEnd('transcription');
// Monitor bundle sizes
// next.config.ts
module.exports = {
webpack: (config) => {
config.plugins.push(
new BundleAnalyzerPlugin({ analyzeMode: 'static' })
);
return config;
}
};- Use Vercel Analytics for performance insights
- Implement custom logging for API errors
- Track WER/CER trends over time
- Monitor provider API response times
-
Audio Processing
- Compress audio before upload (MP3 @ 128kbps)
- Limit file size to 4MB for Vercel hobby tier
- Use appropriate sample rates (16kHz for STT)
-
State Management
- Minimize context re-renders
- Use local state when possible
- Clear sessionStorage after use
-
Network Optimization
- Use HTTP/2 multiplexing
- Implement request compression (gzip/brotli)
- Prefetch critical resources
-
Database Queries
- Index frequently queried fields
- Use lean queries for better performance
- Implement pagination for large datasets
Q: Is Voice LM Arena free to use?
A: Yes, Voice LM Arena is open source. However, you need to provide your own API keys for the transcription/TTS providers, which may have associated costs. Alternatively, you can use a coupon code for pre-configured access.
Q: Which STT model is the most accurate?
A: Accuracy varies by audio quality, language, and use case. Check the /leaderboard page for model comparisons. Generally, OpenAI Whisper and Deepgram Nova-3 offer excellent accuracy.
Q: How is Word Error Rate (WER) calculated?
A: WER = (Substitutions + Insertions + Deletions) / Total Words in Ground Truth. Lower is better. A WER of 0.05 means 5% of words are incorrect.
Q: Can I use this for production applications?
A: Yes, but ensure you have proper API key management, error handling, and rate limiting. Consider implementing user authentication and usage quotas.
Q: Do I need MongoDB to use Voice LM Arena?
A: No, MongoDB is optional. The app uses in-memory storage by default. MongoDB enables persistent storage and analytics features.
Q: Do I need AWS S3?
A: No, S3 is optional. Without S3, audio files are stored as base64 data URLs (with size limitations). S3 is recommended for production use.
Q: How do I get API keys?
A: Visit each provider's website:
Q: How do I create a coupon code?
A: Insert a document into the coupons collection:
db.coupons.insertOne({
code: "BETA2024",
apiKeys: { openai: "sk-...", deepgram: "..." },
features: ["stt", "tts", "experiments"],
usageLimit: 1000,
usageCount: 0,
expiresAt: new Date("2025-12-31"),
isActive: true,
createdAt: new Date()
});Q: What audio formats are supported?
A: MP3, WAV, M4A, WebM, OGG, and Opus. Most providers prefer MP3 or WAV format.
Q: What's the maximum audio file size?
A: 4MB for Vercel hobby tier. Upgrade to Pro for 50MB limits, or use direct provider uploads.
Q: How do I add a new STT provider?
A: See ADDING_NEW_MODEL_GUIDE.md for detailed instructions. Basic steps:
- Create provider implementation in
app/lib/transcription/ - Add model configuration to database
- Update type definitions
- Test integration
Q: Can I self-host Voice LM Arena?
A: Yes! Deploy to any Node.js hosting platform. Configure environment variables accordingly. Ensure MongoDB and S3 (or alternatives) are accessible.
Q: Why do results differ between models?
A: Each provider uses different models, training data, and algorithms. Factors include:
- Model architecture (Whisper, Nova, etc.)
- Training dataset quality and diversity
- Language-specific optimizations
- Audio preprocessing techniques
Q: How accurate is the cost calculation?
A: Costs are based on provider's published rates at the time of implementation. Always verify current pricing on provider websites, as rates may change.
Q: I'm getting "API key invalid" errors
A:
- Verify API key format (no extra spaces)
- Check key permissions on provider dashboard
- Ensure billing is enabled for the provider
- Test key directly with provider's API
Q: Audio upload fails
A:
- Check file size (<4MB for hobby tier)
- Verify supported format (MP3, WAV, etc.)
- Check browser console for errors
- Try converting audio to MP3 format
Q: MongoDB connection timeout
A:
- Verify MONGODB_URI format
- Check network connectivity
- Whitelist IP address in MongoDB Atlas
- Test connection:
npm run db:test
Q: Transcription returns empty text
A:
- Check audio quality and volume
- Verify audio format is supported
- Test with different providers
- Check provider API status
Q: Build fails on deployment
A:
- Clear
.nextfolder:rm -rf .next - Reinstall dependencies:
npm ci - Check Node.js version (18+)
- Verify environment variables are set
Q: Can I compare more than 2 models at once?
A: Yes! Select multiple models on the Listener or Speaker page. The app processes all selected models in parallel.
Q: How does round-trip testing work?
A: Generate speech on Speaker page → Click "Transcribe" → Audio transferred to Listener page → Original text used as ground truth → Accuracy calculated.
Q: Can I export my results?
A: Yes, results can be exported as JSON. A download button is available on the results page. CSV export coming soon.
Q: Is real-time streaming supported?
A: Experimental real-time streaming is available on the /streaming page. Select providers that support WebSocket connections (e.g., Deepgram).
Q: Can I use my own audio files for testing?
A: Yes! Use the file upload feature on the Listener page. Supports drag-and-drop for convenience.
- Fork the repository
- Create feature branch (
git checkout -b feature/amazing-feature) - Install dependencies (
npm install) - Set up environment variables
- Make changes
- Add tests if applicable
- Commit changes (
git commit -m 'Add amazing feature') - Push to branch (
git push origin feature/amazing-feature) - Submit pull request
- TypeScript: Use strict type checking
- ESLint: Follow configured linting rules
- Prettier: Code formatting (run
npm run formatif available) - Conventional Commits: Use semantic commit messages
feat:New featurefix:Bug fixdocs:Documentationstyle:Formattingrefactor:Code restructuringtest:Adding testschore:Maintenance
- Test new features thoroughly
- Verify provider integrations
- Check performance impact
- Validate error handling
- Test edge cases (empty input, large files, etc.)
- Ensure mobile responsiveness
- Update README if adding features
- Include screenshots for UI changes
- List breaking changes (if any)
- Reference related issues
- Ensure all tests pass
- Request review from maintainers
# ✅ GOOD: Store API keys in .env.local (gitignored)
OPENAI_API_KEY=sk-...
DEEPGRAM_API_KEY=...
# ❌ BAD: Never hardcode in source code
const apiKey = "sk-1234567890abcdef"; // DON'T DO THIS!// ✅ GOOD: API keys stay on server
// app/api/transcribe/route.ts
const apiKey = process.env.OPENAI_API_KEY;
// ❌ BAD: Never send API keys to client
// Response should not contain actual keys- Rotate API keys every 90 days
- Use separate keys for development/production
- Immediately rotate if key is compromised
- Monitor API usage for anomalies
- Audio files deleted after 30 days (S3 lifecycle)
- No persistent storage of user API keys (session only)
- IP addresses anonymized in logs
- GDPR compliant data retention
// Clear sensitive data on page unload
window.addEventListener('beforeunload', () => {
sessionStorage.clear();
localStorage.removeItem('apiKeys');
});// Validate file size
if (file.size > MAX_FILE_SIZE) {
throw new Error('File too large');
}
// Validate file type
const validTypes = ['audio/mp3', 'audio/wav', 'audio/m4a'];
if (!validTypes.includes(file.type)) {
throw new Error('Invalid file type');
}
// Sanitize filenames
const safeFilename = filename.replace(/[^a-z0-9.-]/gi, '_');// Prevent XSS attacks
import DOMPurify from 'dompurify';
const sanitizedText = DOMPurify.sanitize(userInput);// Implement per-IP rate limiting
const rateLimits = new Map<string, number[]>();
function checkRateLimit(ip: string): boolean {
const now = Date.now();
const requests = rateLimits.get(ip) || [];
// Remove old requests (older than 15 minutes)
const recentRequests = requests.filter(
time => now - time < 15 * 60 * 1000
);
if (recentRequests.length >= 100) {
return false; // Rate limit exceeded
}
recentRequests.push(now);
rateLimits.set(ip, recentRequests);
return true;
}// next.config.ts
const nextConfig = {
async headers() {
return [
{
source: '/api/:path*',
headers: [
{ key: 'Access-Control-Allow-Origin', value: 'https://yourdomain.com' },
{ key: 'Access-Control-Allow-Methods', value: 'POST, GET, OPTIONS' },
{ key: 'Access-Control-Allow-Headers', value: 'Content-Type, Authorization' },
],
},
];
},
};// Add API key validation middleware
export async function verifyApiKey(request: Request) {
const authHeader = request.headers.get('Authorization');
if (!authHeader?.startsWith('Bearer ')) {
return new Response('Unauthorized', { status: 401 });
}
const token = authHeader.substring(7);
// Validate token against database
const isValid = await validateToken(token);
if (!isValid) {
return new Response('Invalid token', { status: 403 });
}
}// Use parameterized queries to prevent injection
db.users.find({ email: userInput }); // ✅ GOOD
// Never interpolate user input
db.users.find({ email: `${userInput}` }); // ❌ BAD
// Enable authentication
mongod --auth --dbpath /data/db
// Use least privilege principle
db.createUser({
user: 'appUser',
pwd: 'securePassword',
roles: [{ role: 'readWrite', db: 'compare_voice' }]
});
// Enable encryption at rest
mongod --enableEncryption --encryptionKeyFile /path/to/keyfile# ✅ GOOD: Use environment variables
MONGODB_URI=mongodb+srv://user:pass@cluster.mongodb.net/db?retryWrites=true
# Enable TLS/SSL
MONGODB_URI=mongodb://localhost:27017/db?tls=true&tlsCAFile=/path/to/ca.pem{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::ACCOUNT:user/app"},
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::your-bucket/audio/*"
}
]
}// Limit presigned URL expiration
const presignedUrl = await getSignedUrl(s3Client, command, {
expiresIn: 3600 // 1 hour
});
// Include content type restriction
const command = new PutObjectCommand({
Bucket: bucketName,
Key: key,
ContentType: 'audio/mp3'
});- All API keys stored in environment variables
-
.env.localadded to.gitignore - HTTPS enabled (SSL/TLS certificates)
- Rate limiting implemented
- Input validation on all endpoints
- CORS properly configured
- Database authentication enabled
- S3 bucket policies configured
- Error messages don't leak sensitive info
- Logging excludes sensitive data
- Regular security audits scheduled
- Dependency vulnerabilities checked (
npm audit) - CSP (Content Security Policy) headers set
- API timeouts configured (prevent DoS)
- File upload size limits enforced
// Log security events
function logSecurityEvent(event: string, details: object) {
console.log(JSON.stringify({
timestamp: new Date().toISOString(),
event,
details,
severity: 'security'
}));
}
// Monitor for suspicious activity
if (failedAttempts > 5) {
logSecurityEvent('multiple_failed_auth', { ip, attempts: failedAttempts });
// Implement temporary IP ban
}This project is licensed under the MIT License - see the LICENSE file for details.
Prof. Rohit Aggarwal — For envisioning VoiceArena as an accessible, open-source testing platform that democratizes voice evaluation for non-technical users. His strategic vision shaped both the platform architecture and research methodology. MAdAiLab — For sponsoring API costs for the experimentation, without which we wouldn’t be able to do extensive testing.
- Next.js - The React Framework for production with excellent developer experience
- React - UI library for building interactive interfaces
- TypeScript - Type-safe JavaScript for robust code
- Tailwind CSS - Utility-first CSS framework for rapid UI development
- Web Audio API - Browser audio processing
- react-audio-voice-recorder - Audio recording component
- Heroicons - Beautiful hand-crafted SVG icons
- Lucide React - Clean and consistent icon set
- Framer Motion - Production-ready animation library
- MongoDB - Flexible NoSQL database for storing results
- Mongoose - Elegant MongoDB object modeling
- AWS S3 - Scalable object storage for audio files
- OpenAI - Whisper STT and TTS models
- Deepgram - Nova-3 STT and Aura TTS
- DeepInfra - Cost-effective model hosting
- Mistral AI - Voxtral multilingual transcription
- ElevenLabs - High-quality voice synthesis
- Google Gemini - Advanced audio understanding
- AssemblyAI - Speech recognition with timestamps
- Vercel - Seamless deployment and hosting
- ESLint - Code linting and quality
- Turbopack - Fast development builds
Special thanks to the open-source community and all contributors who make projects like this possible. This platform was built to help researchers, developers, and businesses make informed decisions about AI voice technologies.
- 📖 Documentation: Check this README
- 🐛 Bug Reports: Open an issue on GitHub
- 💡 Feature Requests: Submit ideas via GitHub Issues
- 💬 Discussions: Join community discussions on GitHub
- 🤝 Contributions: Submit pull requests for improvements
For urgent security issues or partnership inquiries, please contact: mayankbambal@gmail.com
- ✅ Multi-provider STT comparison (7+ providers)
- ✅ TTS testing with multiple voices
- ✅ Round-trip workflow (TTS → STT)
- ✅ WER/CER accuracy metrics
- ✅ Real-time transcription
- ✅ Coupon system for API key management
- ✅ Leaderboard and analytics
- ✅ MongoDB & S3 integration
- ✅ Mobile-responsive UI
- 🔄 Real-time streaming for all providers
- 🔄 CSV/Excel export for results
- 🔄 User authentication & saved sessions
- 🔄 Custom model fine-tuning
- 🔄 Batch processing for multiple files
- 🔄 Advanced audio analysis (pitch, volume, etc.)
- 🔄 Language-specific optimizations
- 🔄 API usage analytics dashboard
- 🔄 Collaborative testing sessions
- 🔄 Plugin system for custom providers
- 🌟 Multi-language UI support
- 🌟 Voice cloning capabilities
- 🌟 Sentiment analysis integration
- 🌟 Speaker diarization
- 🌟 Automatic punctuation & formatting
- 🌟 Integration with popular tools (Zoom, Discord)
- 🌟 Enterprise SSO support
- 🌟 White-label solution
Built with ❤️ for the AI and voice technology community
Star ⭐ this repository if you find it useful!