Skip to main content

no-floating-audio-contexts

Require AudioContext instances to be retained so they can be closed.

Rule catalog ID: R018

Targeted pattern scopeโ€‹

This rule targets browser audio context construction:

  • new AudioContext(...)
  • new webkitAudioContext(...)
  • new window.AudioContext(...)
  • new globalThis.AudioContext(...)

The rule reports contexts that are immediately discarded, explicitly voided, or used through an immediate non-cleanup method call such as new AudioContext().resume(). It ignores locally shadowed direct constructor bindings so project-local classes with the same name are not treated as browser audio contexts.

What this rule reportsโ€‹

The rule reports:

  • standalone new AudioContext() expressions
  • void new AudioContext()
  • immediate non-cleanup use of an unowned context

Immediate close() calls are allowed because they do not leave an owned context behind.

Why this rule existsโ€‹

AudioContext can hold audio hardware, decoding, graph, and scheduling resources. AudioContext.close() releases system audio resources used by the context. If the context is discarded, cleanup code cannot close it.

Incorrectโ€‹

new AudioContext();
void new window.AudioContext();
new AudioContext().resume();

Correctโ€‹

const context = new AudioContext();

try {
await context.resume();
} finally {
await context.close();
}
function createAudioContext() {
return new AudioContext();
}
audioContextRegistry.add(new AudioContext());

Behavior and migration notesโ€‹

Store the context in the audio session, component, game engine, visualizer, or test fixture that owns the audio graph. That owner should call close() during cleanup.

This rule does not autofix because adding a variable without a matching close() path would not fix the resource lifecycle.

ESLint flat config exampleโ€‹

import runtimeCleanup from "eslint-plugin-runtime-cleanup";

export default [
runtimeCleanup.configs.recommended,
];

When not to use itโ€‹

Do not enable this rule for one-page demos where the browser page lifetime is intentionally the cleanup boundary. Use narrow inline disables for those examples.

Further readingโ€‹