Severity: medium · Type: performance · Confidence: medium
Location:src/lotus/lotusAudioInject.ts:144, 221
Problem
playInjectedClip does new AudioContext() per clip and closes it in cleanup(). An in-call page already holds close to Chrome's per-document AudioContext limit (~6): three from useAudioContext (InCallView.tsx:258, CallEventAudioRenderer.tsx:63, ReactionAudioRenderer.tsx:36), one from MatrixAudioRenderer.tsx:117, one per LiveKit Room (acquireAudioContext), plus one owned by LotusDenoiseProcessor. close() is asynchronous, so rapid clip replacement (replace-mode aborts the previous clip and closes its context in the background) can transiently push past the cap, at which point new AudioContext() throws and the clip silently fails with only a logger.warn.
How to trigger
Spam soundboard buttons with lotusDenoiseSource=1 active, on Chrome.
Suggested fix
Keep one lazily-created module-level AudioContext (plus one MediaStreamAudioDestinationNode) for all injected clips and reuse it, disposing only the BufferSource/GainNode per clip.
Filed from the September 2026 audit (branch lotus).
**Severity:** medium · **Type:** performance · **Confidence:** medium
**Location:** `src/lotus/lotusAudioInject.ts:144`, `221`
### Problem
`playInjectedClip` does `new AudioContext()` per clip and closes it in `cleanup()`. An in-call page already holds close to Chrome's per-document `AudioContext` limit (~6): three from `useAudioContext` (`InCallView.tsx:258`, `CallEventAudioRenderer.tsx:63`, `ReactionAudioRenderer.tsx:36`), one from `MatrixAudioRenderer.tsx:117`, one per LiveKit `Room` (`acquireAudioContext`), plus one owned by `LotusDenoiseProcessor`. `close()` is asynchronous, so rapid clip replacement (replace-mode aborts the previous clip and closes its context in the background) can transiently push past the cap, at which point `new AudioContext()` throws and the clip silently fails with only a `logger.warn`.
### How to trigger
Spam soundboard buttons with `lotusDenoiseSource=1` active, on Chrome.
### Suggested fix
Keep one lazily-created module-level `AudioContext` (plus one `MediaStreamAudioDestinationNode`) for all injected clips and reuse it, disposing only the `BufferSource`/`GainNode` per clip.
---
_Filed from the September 2026 audit (branch `lotus`)._
jared
added this to the EC fork audit 2026-09 · Medium & Low milestone 2026-09-12 02:13:04 -04:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Severity: medium · Type: performance · Confidence: medium
Location:
src/lotus/lotusAudioInject.ts:144,221Problem
playInjectedClipdoesnew AudioContext()per clip and closes it incleanup(). An in-call page already holds close to Chrome's per-documentAudioContextlimit (~6): three fromuseAudioContext(InCallView.tsx:258,CallEventAudioRenderer.tsx:63,ReactionAudioRenderer.tsx:36), one fromMatrixAudioRenderer.tsx:117, one per LiveKitRoom(acquireAudioContext), plus one owned byLotusDenoiseProcessor.close()is asynchronous, so rapid clip replacement (replace-mode aborts the previous clip and closes its context in the background) can transiently push past the cap, at which pointnew AudioContext()throws and the clip silently fails with only alogger.warn.How to trigger
Spam soundboard buttons with
lotusDenoiseSource=1active, on Chrome.Suggested fix
Keep one lazily-created module-level
AudioContext(plus oneMediaStreamAudioDestinationNode) for all injected clips and reuse it, disposing only theBufferSource/GainNodeper clip.Filed from the September 2026 audit (branch
lotus).