Build1 distinct publisher3 min readPublished
One developer's catalogue of 16 traps: browser echo cancellation only references audio-element and WebRTC playback, so TTS routed through Web Audio guarantees the bot hears itself.
The Engineer · Build desk
Compiled by The EngineerSomething wrong?How this is made
Echo cancellation is a subtraction job, and it needs a copy of the thing being subtracted. In a browser that copy arrives only from paths the browser itself owns: an `<audio>` element, or a WebRTC receiver track. A Web Audio graph does not reliably supply one [2]. So when TTS leaves through a buffer source node because someone wanted sample-accurate amplitude for a mouth shape, the canceller has nothing to work against. The mic hears the synthetic voice cleanly and the recognizer files it as a user turn [3]. No threshold downstream separates that from a person, because without a reference signal a speaker's output and a human voice look the same at the microphone [5].
The usual patch is text matching: hold what TTS just said and drop transcripts that resemble it. The author's fix skips that layer, sending TTS from the server as a WebRTC remote track played through an `<audio>` element, reported as 99 seconds of continuous speech over open speakers with zero false user turns [4]. Read what that sentence requires: a server-side media path, a remote track, and an element to consume it. Client-side synthesis and direct buffer access are what you hand back.
Convergence is the same mechanism one order down. The canceller learns only while audio is actually playing, so a session that opens in silence begins with an untrained filter and the user's first 10 to 20 seconds get suppressed along with the echo, with autoGainControl ramping up from quiet making that same window worse [7]. The remedy is to make noise on purpose: a greeting or sound effect during the loading screen, AGC set to false, echoCancellation left alone [8]. The spinner is now part of the audio stack.
The media element turns out to be mandatory in the other direction too. Chromium will not hand a remote MediaStream to Web Audio until some media element starts consuming it, so even an analysis-only graph needs a muted `<audio>` with play() called on it, plus a held reference, since Chrome will collect unreferenced nodes and stop the graph without raising an error [11][12].
What remains after the routing is corrected is a genuine trade. AEC suppresses and distorts near-end audio during simultaneous speech, which is why proper nouns come apart at word beginnings when a user talks over the avatar [9]. The mitigations run three deep and the last of them is instructing the LLM to treat its input as error-prone transcription and reach for phonetically similar terms [10]. Audio from another process, including a video the agent opened itself, has no reference at all and so has no fix; headphones, macOS voice isolation that unsupported systems quietly ignore, and downstream filtering are the entire toolkit [6].
Four of the eight traps reproduced in this excerpt come back to where the reference signal comes from or whether it has converged [16]. The instrumentation advice follows from that: server-side energy logging at 10 Hz, one sample every 100 ms, tells distorted audio from true silence, and a meter on the transmitted stream earns its place because a muted track and an empty room are identical on the wire [13][15][14].
Ranked by verification strength, evidence, and original report placement.
Solution: return TTS audio from the server as a WebRTC remote track and play it via an <audio> element. This eliminates echoes without text-matching workarounds; tested with 99 seconds of continuous speech with speakers on and zero false user turn detections.
Recommended observability: server-side logging of input audio energy or probability at 10 Hz to distinguish complete silence from distorted audio, plus a UI level meter on the actual transmitted stream read via createMediaStreamSource, which does not affect transmission.
A developer writeup published on dev.to, originally in Japanese at forge.workstyle.tech, compiles 16 traps encountered while building browser voice AI (avatars, voice bots, streaming AI), organised as symptom, cause, solution.
AEC (acoustic echo cancellation) requires a reference signal, the sound to cancel. Only the browser's official playback paths (<audio> elements and WebRTC receiver tracks) serve as references; custom playback via the Web Audio API does not reliably function as a reference.
Symptom: TTS audio is picked up by the mic and STT recognises it as user speech, creating a self-response loop.
Browser AEC can only reference audio played by the same tab or app; audio from other processes is indistinguishable from human speech to the mic, so audio and lyrics from a YouTube video the agent opened keep getting transcribed by STT.
Follow any of these and your For You feed starts watching them — no settings page required.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Mechanistically specific, single unverified source
The claims are unusually concrete for a single-source cluster: named constraints (AEC reference limited to <audio> and WebRTC receiver tracks), named constraints toggles (autoGainControl, echoCancellation, voiceIsolation), working code for the muted-element srcObject workaround and the ConstantSource keep-alive, and one quantified test. But everything rests on one dev.to post by one developer, with no browser or OS version matrix, no spec citation, and no independent reproduction, so the general assertions about Chromium behaviour remain untested outside the author's own stack.
One practitioner's product, no third-party uptake
Adoption evidence is limited to the author's own disclosure of building browser voice avatars, voice bots and headless streaming renderers, plus one self-run 99-second test. No other team, product, library, vendor or platform is shown using these patterns, and no user, traffic or deployment scale is given, so the observed footprint is a single developer.
Slightly overstated generality, restrained tone
The writing is deliberately unpromotional - it states plainly that cross-process audio has no technical silver bullet and that dual-speech distortion is an inherent trade-off, which pulls the gap toward zero. The mild positive comes from scope: browser-wide and Chromium-wide behaviours, plus a 'guarantees the bot hears itself' framing, are asserted from one developer's single-stack testing without versions or reproduction, so generality outruns the evidence a little.
Self-syndication, no vendor or commercial stake shown
The post is cross-posted to dev.to from the author's own domain, forge.workstyle.tech, which is a visibility and credibility incentive, and it references a companion article of the author's for STT vocabulary hints. Against that, no product, SDK, model vendor or paid service is being sold, the recommendations are all first-party browser APIs, and the piece volunteers unflattering limitations. Supplied material shows no funding, employer or affiliate relationship.
Plausible and internally consistent, thinly corroborated
Internal consistency is high and the prescriptions are falsifiable at the API level, which supports moderate confidence in the direction of the findings. Confidence is capped by structural limits: one publisher, one author, one product's experience, a truncated excerpt, no version or platform bounds, and a single self-reported measurement with no baseline comparison.
build
The stroke width that never rendered: SVG attributes lose every cascade fight1 distinct publisher
build
The name dictionary and the conversation cannot both fit in Whisper's prompt window1 distinct publisher
build
height:auto is animatable now, so your max-height ceiling is a bug you can delete1 distinct publisher
build
In Manifest V3 every message handler is a cold start, and the fix for lost state hides new failures1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 25, 2026