Skip to content

Web/Firefox: pcm16bits stream samples become progressively distorted past ~14s (byte count and cadence are correct) #601

Description

@wrunk

Package version

record: 6.2.0

Environment

  • OS: macOS 26.3.1 (a) (25D771280a) on Apple Silicon
  • Browser: Firefox 151.0 (aarch64). Chrome and Safari are fine on the same machine.

Describe the bug

When recording via startStream on Firefox, the PCM samples themselves become
progressively distorted starting around the 13–14 second mark, and the distortion
gets worse the longer the recording runs (very obvious at ≥30 s). Chrome, Safari,
and all native targets are fine on the same hardware.

await recorder.startStream(const RecordConfig(
  encoder: AudioEncoder.pcm16bits,
  sampleRate: 16000,
  numChannels: 1,
));

Also reproduces at sampleRate: 48000 (so it's not the resampler).

To Reproduce

  1. Flutter web app with record: 6.2.0 on Firefox 151 desktop.
  2. Call startStream with the config above and collect every chunk into a single
    Uint8List in memory.
  3. After ≥30 s of continuous speech, build a WAV blob from those bytes in the same
    tab (Uint8ListBlob → object URL).
  4. Assign that URL to an <audio> element and play it.
  5. Distortion is audible starting ~14 s and worsens through the end.

Expected behavior

PCM samples remain clean for the full duration of the recording, as they do on
Chrome / Safari / native.

Additional context

Before filing, I built a minimal diagnostic page to isolate where the corruption
is introduced. Findings:

  • Byte count is exact. Total captured bytes match duration × sampleRate × 2
    to within rounding (e.g. 867,824 bytes / 32,000 B/s = 27.12 s — matches the
    actual recording length).
  • Chunk arrival cadence is steady ~46 ms. No drops, no stalls.
  • Persists at native 48 kHz (sampleRate: 48000). Rules out the resampler.
  • Persists when the in-memory WAV is played back directly in the same Firefox
    tab.
    Rules out network, base64 encoding, server, and downstream decoders.

So the distortion is in the PCM samples themselves, produced by the worklet, on
Firefox specifically.

Hypothesis: since byte rate and cadence are stable, this looks like progressive
sample miscoding (phase / scaling drift, or a Float32→Int16 conversion buffer that
gets misaligned over time) rather than dropped or duplicated frames. The ~13 s
threshold suggests state that's accumulated across many worklet process() calls.

Workaround: replacing record_web with a hand-rolled AudioWorklet (~50 lines
of JS) that does the same Float32→Int16 conversion per render quantum with zero
converted state carried across process() calls produces clean output for
arbitrarily long recordings on the same Firefox build / hardware / getUserMedia
stream. Happy to share the worklet code or the diagnostic page if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions