Audio capture pipeline
How raw microphone audio becomes a 16 kHz PCM16 WAV file ready for transcription — inside an AudioWorklet, streamed over IPC, and buffered in the main process.
Target rate
16 kHz mono
Chunk size
1600 samples · 100 ms
Sample format
Int16 (PCM16)
Capture in an AudioWorklet
The overlay opens the microphone with getUserMedia (mono, with echo cancellation, noise suppression, and auto-gain), then routes it through a Web Audio graph into a custom AudioWorkletProcessor that runs on the audio thread.
Why the worklet code is inlined
Resampling & quantization
The hardware captures at roughly 48 kHz, but Whisper wants 16 kHz. The worklet linearly interpolates samples down to the target rate, clamps each to [-1, 1], and quantizes to signed 16-bit. Samples accumulate into a 1600-sample buffer — 100 ms of audio — which is posted to the main thread whenever it fills.
| Parameter | Value | Notes |
|---|---|---|
| TARGET_SAMPLE_RATE | 16000 | Whisper's expected input rate. |
| CHUNK_SIZE | 1600 | Samples per chunk — exactly 100 ms at 16 kHz. |
| ratio | sampleRate / 16000 | Decimation ratio; the read position advances by this each output sample. |
| encoding | Int16LE | Negative samples scaled by 0x8000, positive by 0x7FFF. |
Streaming & buffering
Each 100 ms chunk is sent to the main process over the realtime:audio-chunkchannel. Despite the “realtime” naming, the main process simply buffers these chunks in an array — nothing is sent to Groq until you stop.
Naming vs behavior
Building the WAV on stop
When recording stops, all buffered chunks are concatenated and a 44-byte WAV header is prepended to produce a standard mono PCM16 WAV in memory. Recording duration is derived directly from the byte length:
const durationSec = mergedPcm.length / 2 / 16000;
// 2 bytes per Int16 sample, 16000 samples per secondThat WAV is what gets uploaded to Groq. Continue to Transcription for what happens next.