A multimodal real-time streaming application built on Google's Agent Development Kit (ADK) with advanced message queuing, audio processing, and comprehensive debugging capabilities.
- < Real-time Audio Streaming - Bidirectional audio communication with the AI agent
- < Video Streaming - Webcam and screen sharing capabilities
- = Text Chat - Real-time text-based conversation
- =οΏ½ Message Queuing - Advanced priority-based message handling with overflow protection
- = Performance Monitoring - Real-time queue health and transmission statistics
- =οΏ½ Comprehensive Debugging - Built-in tools for troubleshooting audio and connection issues
-
Install Dependencies
pip install -r requirements.txt
-
Start the Server
python main.py
-
Open the Application
- Navigate to
http://localhost:8881/live - Click the microphone button to start audio streaming
- Use camera/screen share buttons for video streaming
- Navigate to
The application includes comprehensive debugging tools accessible via the browser console (F12 Console).
Quick Diagnostics
// Quick status check
checkTransmission()
// Returns: "οΏ½ Transmitting: 150 audio messages sent" or error details
// Full system status
debugTransmission()
// Shows detailed connection, queue, and transmission statistics
// Monitor transmission for 30 seconds
monitorTransmission(30)
// Reports message rates and transmission activity// Adjust playback volume (0.0 to 1.0)
setPlaybackVolume(0.5) // 50% volume
setPlaybackVolume(0.8) // 80% volume
setPlaybackVolume(0.3) // 30% volumeThe console automatically logs audio-related issues:
- Sample Rate Mismatch:
< Audio sample rates - Context: 48000Hz, Server: 24000Hz - Network Gaps:
< Audio gap detected: 348ms (packet #17) - Adaptive Buffering:
< Adaptive buffering: increased threshold to 275ms due to network gaps - Buffer Management:
< Starting playback with 320ms buffered (threshold: 250ms) - Overflow Protection:
< Audio buffer too full (1600ms), dropping packet to prevent delay - Transmission Count:
= AUDIO DIRECT: Sent 50 audio messages
| Problem | Console Message | Solution |
|---|---|---|
| Slow/Fast Audio | οΏ½ Sample rate mismatch! |
Sample rates don't match - this is normal and handled automatically |
| Audio Gaps | < Audio gap detected: XYZms |
Network delays - system auto-adapts buffer size |
| Audio Delays | < Audio buffer too full |
Poor network - system drops packets to maintain real-time |
| Overlapping Audio | Multiple scheduling messages | Fixed - single audio processing path |
| Too Loud/Quiet | No specific message | Use setPlaybackVolume(0.5) to adjust |
| No Audio | L No audio messages transmitted |
Check microphone permissions and connection |
Use debugTransmission() to get detailed statistics:
{
messagesSent: {
audio: 245, // Outbound audio messages
video: 12, // Video frames sent
text: 3, // Text messages
control: 1 // Control messages
},
isConnected: true,
isRecording: true,
wsReadyState: 1, // WebSocket state (1 = OPEN)
audioPacketsReceived: 89, // Incoming audio packets
lastAudioGap: 45, // Time since last audio (ms)
audioContextSampleRate: 48000 // Device sample rate
}0- CONNECTING1- OPEN (οΏ½ Good)2- CLOSING3- CLOSED (L Problem)
The system includes automatic queue health monitoring:
// Queue status shows health of message processing
queueStatus: {
enabled: true,
connected: true,
overallHealth: "healthy", // healthy | degraded | critical
outbound: {
audio: { totalSize: 2, maxSize: 5, health: "healthy" },
video: { totalSize: 0, maxSize: 2, health: "healthy" }
}
}checkTransmission()- Look for:
= Not connected to serveror< Not recording audio - Solution: Check browser permissions, refresh page
// Check for sample rate issues
debugTransmission()
// Look at audioContextSampleRate vs server rate (24000Hz)
// Adjust volume if too loud/quiet
setPlaybackVolume(0.4)// Monitor WebSocket connection
debugTransmission()
// Check wsReadyState (should be 1)
// Check isConnected (should be true)// Monitor message rates
monitorTransmission(15)
// Should show steady audio transmission (~22 messages/sec)
// Check queue health
debugTransmission()
// overallHealth should be "healthy"The application follows Google Live API specifications:
- Input Sample Rate: 16kHz (Live API native rate)
- Output Sample Rate: 24kHz (Live API output rate)
- Buffer Size: 2048 samples
- Channels: Mono (1 channel)
- Format: 16-bit PCM, little-endian
- MIME Type:
audio/pcm;rate=16000(includes sample rate) - Playback Buffering: Adaptive continuous buffering (200-300ms)
- Chunk Processing: 20ms AudioContext scheduling
- Gap Tolerance: Automatic network delay compensation
Message queues are optimized for real-time performance:
- Audio Queue: 5 messages max, 100ms rate limit
- Video Queue: 2 frames max, 1000ms rate limit
- Text Queue: 50 messages max, no rate limit
- Control Queue: 20 messages max, urgent priority
The application includes several audio enhancements for clear playback:
- Gain Control: All incoming audio is adjusted to 70% of its original volume by default to ensure a comfortable listening level. This can be changed with the UI volume slider.
- Soft Clipping: Prevents harsh digital distortion by gently compressing audio signals that exceed 95% of the maximum volume.
- Click Prevention: A micro-fade is applied to the beginning and end of each audio chunk to prevent audible "clicks" during seamless playback.
Debug functions are defined in /static/live/debug-monitor.js and automatically loaded.
The application employs a sophisticated, real-time audio pipeline designed for low latency and resilience to network instability.
- Capture: Audio is captured from the microphone at the browser's native sample rate.
- AudioWorklet Processing (
audio-processor.js): The raw audio stream is immediately passed to a dedicatedAudioWorkletthread. This prevents audio processing from blocking the main UI thread. - Resampling & Formatting: Inside the worklet, the audio is down-sampled to the 16kHz mono format required by the Live API and converted to 16-bit PCM samples.
- Transmission (
audio-client.js): The 16-bit PCM audio chunks are Base64 encoded and sent over the WebSocket connection. For optimal latency, outbound audio currently bypasses the message queue system and is sent directly.
This part of the pipeline is engineered to handle network jitter and provide smooth playback.
- Reception (
audio-client.js): The client receives 24kHz audio chunks from the server, encoded in Base64. - Gap Detection: Upon receiving a packet, the client measures the time since the last packet arrived. If a significant delay ("gap") is detected, it signals that the network is unstable.
- Adaptive Buffering: Based on the detected network gaps, the client dynamically adjusts its buffer size (
adaptiveThreshold). A more unstable network results in a larger buffer to prevent stuttering, at the cost of slightly higher latency. - Decoding & Buffering: The Base64 data is decoded, and the audio samples are placed into a continuous playback buffer.
- Gain Adjustment: Before being buffered, the audio signal's volume is adjusted. By default, it's set to 70% of the original volume to provide comfortable listening levels and prevent clipping. This can be further adjusted with the UI volume slider.
- Scheduled Playback: A high-precision scheduling loop (
scheduleNextChunk) pulls audio from the buffer. It uses the browser'sAudioContextclock to schedule playback of small audio chunks back-to-back, ensuring no audible gaps or clicks between them. - Buffer Safety: If the network is too slow and the buffer runs empty (an "underrun"), the scheduler will pause playback until the buffer has been refilled to a safe level. This is the primary cause of audible "choppiness" and is a direct result of network instability. The console will log a "Buffer running low" message when this occurs.
Message Priority Queue Rate Limiting WebSocket Send
WebSocket Receive Processing Queue Application Handler
-
Check Basic Connection
checkTransmission()
-
Monitor Transmission Activity
monitorTransmission(10)
-
Examine Detailed Status
debugTransmission()
-
Test Audio Controls
setPlaybackVolume(0.5)
| Icon | Message Type | Example | Meaning |
|---|---|---|---|
| =οΏ½ | Connection | WebSocket connection established |
Successful server connection |
| = | Transmission | AUDIO DIRECT: Sent 50 audio messages |
Outbound message counts |
| < | Audio | Audio gap detected: 120ms |
Audio timing issues |
| οΏ½ | Warning | Sample rate mismatch! |
Configuration warnings |
| L | Error | Failed to initialize audio |
Critical errors |
| οΏ½ | Success | Transmitting: 89 audio messages sent |
Successful operations |
The /static/live/ web application follows a modular, event-driven architecture with clear separation of concerns:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β App Controller β
β (Application Orchestrator) β
βββββββ¬ββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββ
β β
βΌ βΌ
βββββββββββββββ βββββββββββββββ
β UI Manager βββββββββββββββ€State Managerβ
β (View) β β (Observer) β
βββββββββββββββ βββββββββββββββ
β β²
βΌ β
βββββββββββββββ βββββββββββββββ
βSession Mgr β β Config β
β (Lifecycle) β β (Settings) β
βββββββββββββββ βββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Multimodal Client β
β (Communication Layer) β
βββββββ¬ββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββ
β β
βΌ βΌ
βββββββββββββββ βββββββββββββββ
βAudio Client β βQueue System β
β (Audio I/O) β β (Buffering) β
βββββββββββββββ βββββββββββββββ
β β
βΌ βΌ
βββββββββββββββ βββββββββββββββ
βAudioWorklet β β Metrics β
β (Processing)β β (Monitoring)β
βββββββββββββββ βββββββββββββββ
| Module | Responsibility | Key Features |
|---|---|---|
app-controller.js |
Application orchestration and coordination | Event routing, initialization, lifecycle management |
state-manager.js |
Centralized state management with observer pattern | Reactive state updates, change notifications |
ui-manager.js |
DOM manipulation and user interface management | Event binding, visual updates, component states |
session-manager.js |
Session lifecycle and user management | Session IDs, connection state, user authentication |
config.js |
Application configuration and validation | Settings management, environment configuration |
| Module | Responsibility | Key Features |
|---|---|---|
multimodal-client.js |
Video/audio streaming coordination | Webcam, screen sharing, WebSocket management |
audio-client.js |
Audio processing and real-time communication | Microphone input, audio playback, WebSocket audio |
audio-processor.js |
Audio worklet for real-time processing | Low-latency audio capture, format conversion |
| Module | Responsibility | Key Features |
|---|---|---|
message-queue-manager.js |
Central queue coordination | Connection management, queue health monitoring |
priority-queue.js |
Outbound message prioritization | Rate limiting, overflow strategies, batching |
processing-queue.js |
Inbound message processing | Buffering, chunking, ordering, real-time processing |
queue-metrics.js |
Performance monitoring and health tracking | Throughput metrics, latency tracking, health assessment |
| Module | Responsibility | Key Features |
|---|---|---|
debug-monitor.js |
Development and troubleshooting tools | Console utilities, transmission monitoring, status reporting |
queue-debug.js |
Queue system debugging interface | Real-time queue visualization, performance analysis |
User Action β UI Manager β App Controller β Client Layer β WebSocket β Server
State Change β State Manager β Observers β UI Components β Visual Update
Microphone (16kHz) β AudioWorklet β Base64 Encoding β WebSocket β Server
Server β WebSocket β Continuous Buffer (24kHz) β AudioContext Scheduling β Speakers
Message β Priority Assignment β Rate Limiting β Overflow Handling β WebSocket Send
WebSocket Receive β Processing Queue β Buffering/Chunking β Application Handler
- StateManager maintains application state
- Components subscribe to state changes
- Automatic UI updates on state modifications
// Subscribe to state changes
this.stateManager.subscribe('recording', (state) => {
this.updateMicButton(state);
});- AppController orchestrates all user actions
- Clear separation between UI events and business logic
- Centralized event routing and handling
// Event delegation pattern
this.uiManager.setupEventListeners({
onMicClick: () => this.handleMicClick(),
onVolumeChange: (volume) => this.handleVolumeChange(volume)
});- Different overflow strategies for different message types
- Configurable processing modes (immediate, buffered, chunked)
- Adaptive connection quality handling
// Different strategies for different queue types
overflowStrategy: {
audio: 'DROP_OLDEST', // Real-time audio
video: 'REPLACE_NEWEST', // Latest frame only
text: 'FAIL_SEND' // Don't drop user input
}- Clear encapsulation and dependency management
- Explicit imports/exports for better maintainability
- Tree-shaking and bundling optimization
- Direct Audio Path: Incoming audio bypasses queue system for minimum latency
- AudioWorklet: Low-latency audio processing in dedicated thread
- Continuous Buffering: AudioContext scheduling for seamless playback
- Adaptive Buffering: Dynamic buffer sizing based on network conditions (200-300ms)
- Gap Detection: Automatic detection and compensation for network delays
- Chunk-based Processing: 20ms audio chunks for smooth real-time streaming
- Overflow Protection: Smart packet dropping to prevent audio delay buildup
- Priority Queues: Critical messages (audio) get higher priority
- Rate Limiting: Prevents overwhelming the connection
- Audio Context Pooling: LRU cache for audio contexts
- Queue Size Limits: Configurable memory bounds per queue type
- Garbage Collection: Automatic cleanup of old messages and contexts
- Automatic Reconnection: Exponential backoff retry logic
- Offline Buffering: Messages queued when connection lost
- Quality Adaptation: Automatic rate adjustment based on connection quality
CONFIG = {
queue: {
audio: { maxSize: 5, rateLimitMs: 100 },
video: { maxSize: 2, rateLimitMs: 1000 }
},
audio: { sampleRate: 22000, bufferSize: 2048 },
debug: { enableQueueLogging: false }
}- Environment Detection: Automatic WebSocket URL generation
- Validation: Configuration validation on startup
- Hot Updates: Some settings can be changed at runtime
- Component Level: Try-catch in individual methods
- Module Level: Error boundaries between modules
- Queue Level: Fallback to direct communication
- Connection Level: Automatic reconnection and recovery
- Queue Failure: Falls back to direct WebSocket communication
- Audio Failure: Continues with text-only communication
- Connection Loss: Buffers messages for later delivery
- Message Throughput: Messages per second by type
- Queue Health: Depth, drop rate, processing latency
- Connection Quality: Latency, packet loss, connection state
- Audio Quality: Sample rate matching, adaptive gap detection, overflow protection
- Console Commands:
debugTransmission(),monitorTransmission() - Visual Indicators: Queue health, connection status, audio activity
- Performance Logs: Automatic logging of performance issues
- Mobile-first: Tailwind CSS responsive utilities
- Grayscale Theme: Consistent color scheme with CSS custom properties
- Component States: Visual feedback for all interactive elements
- Keyboard Navigation: Full keyboard support
- Screen Reader: Semantic HTML and ARIA labels
- High Contrast: Clear visual hierarchy and focus indicators
- Message Sanitization: All WebSocket messages validated
- Configuration Bounds: Limits on queue sizes and rates
- URL Validation: WebSocket URL construction with validation
- Memory Limits: Configurable bounds on queue and buffer sizes
- Rate Limiting: Protection against message flooding
- Timeout Management: Automatic cleanup of stale resources
This architecture provides a scalable, maintainable, and performant foundation for real-time multimodal communication with built-in debugging, monitoring, and error recovery capabilities.
This project is part of the Google Agent Development Kit ecosystem.