Skip to main content

Audio Stutter Prevention

Learn how SAMMY Three’s multi-layered buffering system protects against audio stuttering and how to avoid main thread blocking in your application
SAMMY Three uses a sophisticated audio buffering system to maintain smooth playback even when your application’s main thread gets temporarily busy. However, extended main thread blocking can still cause audio stuttering. This guide explains how the system works and how to prevent audio issues.

How the buffering system works

SAMMY Three implements a three-layer buffering system to protect against audio stuttering:

Layer 1: JavaScript Audio Queue

The initial buffer that holds raw audio data waiting to be scheduled.
  • Buffer Size: 7,680 samples per buffer
  • Duration: 320ms per buffer at 24kHz sample rate
  • Queue Depth: Typically holds 640ms to 1.6 seconds of audio

Layer 2: Web Audio API Pre-scheduled Buffers

Audio buffers that are pre-scheduled in the Web Audio API’s internal queue.
  • Schedule Window: Maintains 400ms of audio scheduled ahead of current playback
  • Key Benefit: These buffers play automatically, even if JavaScript is blocked

Layer 3: Initial Buffering Delay

A startup delay that ensures the system begins with adequate buffering.
  • Delay: 300ms before first audio plays
  • Purpose: Builds up initial buffer cushion
The Web Audio API continues playing pre-scheduled buffers independently of JavaScript execution, which is why brief main thread blocks don’t cause stuttering.

The 400ms critical threshold

The most important number to remember is 400ms - this is how far ahead the audio streamer schedules buffers.
const SCHEDULE_AHEAD_TIME = 0.4; // 400ms look-ahead window

What happens during main thread blocking:

  • Safe (< 400ms)
  • Risky (≈ 400ms)
  • Stuttering (> 400ms)
Scenario: 200ms main thread block
  • ✅ Pre-scheduled buffers continue playing
  • ✅ No audible gaps or stuttering
  • ✅ System recovers seamlessly when thread unblocks
Time 0ms:     Main thread blocks
Time 0-200ms: Audio plays normally from buffer
Time 200ms:   Main thread unblocks, scheduler resumes
Result:       No user impact

Common causes of main thread blocking

Understanding what can block your main thread helps prevent audio issues:

Safe operations (typically < 100ms)

  • DOM updates and style recalculations
  • Moderate API calls with async/await
  • Standard React re-renders
  • Small array operations

Risky operations (100-400ms)

These operations are on the edge of causing stuttering. Consider optimizing or moving to web workers.
  • Large DOM manipulations
  • Complex calculations without chunking
  • Processing moderate-sized images
  • Multiple synchronous API calls
  • Large state updates in React

Dangerous operations (> 400ms)

These will definitely cause audio stuttering. Always use web workers or async patterns for these operations.
  • Synchronous file I/O operations
  • Processing large datasets synchronously
  • Complex image/video processing
  • Blocking network requests
  • Heavy cryptographic operations

Best practices to prevent stuttering

1. Use Web Workers for heavy computation

Move CPU-intensive tasks off the main thread:
// ❌ Bad: Blocks main thread
function processLargeDataset(data) {
  const results = [];
  for (let i = 0; i < 1000000; i++) {
    results.push(complexCalculation(data[i]));
  }
  return results;
}

// ✅ Good: Runs in Web Worker
const worker = new Worker('processor.worker.js');
worker.postMessage({ data });
worker.onmessage = (e) => {
  const results = e.data;
  // Handle results without blocking
};

2. Chunk large operations

Break up large synchronous operations into smaller chunks:
// ❌ Bad: Processes everything at once
function processAllItems(items) {
  items.forEach(item => {
    expensiveOperation(item);
  });
}

// ✅ Good: Processes in chunks with breaks
async function processItemsChunked(items, chunkSize = 100) {
  for (let i = 0; i < items.length; i += chunkSize) {
    const chunk = items.slice(i, i + chunkSize);
    chunk.forEach(item => expensiveOperation(item));
    
    // Give the main thread a break
    await new Promise(resolve => setTimeout(resolve, 0));
  }
}

3. Use requestIdleCallback for non-critical work

Schedule non-critical work for when the browser is idle:
// ✅ Good: Runs when browser is idle
requestIdleCallback(() => {
  performNonCriticalWork();
}, { timeout: 200 }); // Fallback timeout

4. Optimize React renders

Prevent unnecessary re-renders that can block the thread:
// ✅ Use React.memo for expensive components
const ExpensiveComponent = React.memo(({ data }) => {
  // Component only re-renders when data changes
  return <ComplexVisualization data={data} />;
});

// ✅ Use useMemo for expensive calculations
const processedData = useMemo(() => {
  return expensiveDataProcessing(rawData);
}, [rawData]);

5. Monitor performance

Use the Performance API to identify blocking operations:
// Measure operation duration
performance.mark('operation-start');
performOperation();
performance.mark('operation-end');

performance.measure('operation', 'operation-start', 'operation-end');
const measure = performance.getEntriesByName('operation')[0];

if (measure.duration > 400) {
  console.warn('Operation took', measure.duration, 'ms - risk of audio stuttering!');
}
Use Chrome DevTools Performance tab to profile your application and identify long tasks that might cause audio stuttering.

Audio recording considerations

The audio recording system processes buffers approximately every 43ms (2048 samples at 48kHz). While this is much shorter than the playback buffer, blocking the main thread can still affect recording quality by preventing timely processing of audio worklet messages.
Recording is generally more sensitive to main thread blocking than playback, but the 400ms playback buffer is the more critical threshold for user-noticeable stuttering.

Summary

To prevent audio stuttering in SAMMY Three:
  1. Remember the 400ms rule: Keep main thread operations under 400ms
  2. Use Web Workers: Move heavy computation off the main thread
  3. Chunk large operations: Break up synchronous work
  4. Monitor performance: Profile your app to identify blocking operations
  5. Optimize rendering: Minimize unnecessary React re-renders
By following these guidelines, you can ensure smooth, uninterrupted audio playback in your SAMMY Three application, even when performing complex operations.
I