Skip to content

DX: clear error when initializeWamHost/createInstance receive a non-native BaseAudioContext (standardized-audio-context / Tone.js default contexts) #12

Description

@naomiaro

Hosting WAMs inside an app built on Tone.js (or anything using standardized-audio-context) fails with an opaque error:

TypeError: Failed to construct 'AudioWorkletNode': parameter 1 is not of type 'BaseAudioContext'

The incompatibility is fundamental and by construction — standardized-audio-context never exposes its native nodes, and WAM nodes subclass the native AudioWorkletNode — so there is nothing the SDK can do to make it work. But the error gives integrators no clue what happened or that a one-line fix exists on their side (Tone.setContext(new AudioContext())).

Suggested improvement: an explicit guard in initializeWamHost (and/or WebAudioModule.createInstance):

if (typeof BaseAudioContext !== 'undefined' && !(audioContext instanceof BaseAudioContext)) {
  throw new TypeError(
    'WAM requires a native BaseAudioContext. Wrapper contexts (standardized-audio-context, ' +
    "Tone.js default context) cannot host WAM nodes. Tone.js users: Tone.setContext(new AudioContext())."
  );
}

plus a short docs/FAQ note. This exact investigation cost us a day; a clear message would save every Tone.js integrator the same trip.

Related Firefox caveat worth a docs mention: even with a native context, Tone.js currently breaks on Firefox at context initialization (Listener wraps AudioListener AudioParams Firefox doesn't implement) — see Tonejs/Tone.js#1457 (Tonejs/Tone.js#1457).


Longer-term alternative: context-agnostic node construction

The guard above is the quick DX win, but on reflection the incompatibility isn't fundamental to what WAM does — it's an artifact of WamNode extends AudioWorkletNode binding to the native class at module-definition time. Wrapper libraries like standardized-audio-context ship their own API-compatible AudioWorkletNode implementation (port, parameters, processorOptions, connect/disconnect all present), so worklet-based code can run on either context type if the node class is resolved per context instead of taken from the global scope.

That's how we run our recording/metering worklets on both native and Tone.js (standardized-audio-context) graphs today — construction goes through a context-appropriate factory rather than a fixed subclass. Tone.js itself uses the same branch internally:

// Tone.js core/context/AudioContext.js
return new (context instanceof BaseAudioContext
  ? AudioWorkletNode          // native context → native class
  : stdAudioWorkletNode       // wrapper context → standardized-audio-context's class
)(context, name, options);

For the SDK this would mean resolving the AudioWorkletNode base per context — e.g. a class-factory (const WamNode = makeWamNode(resolveAudioWorkletNodeCtor(context))) or moving from inheritance to composition. We recognize that's a real API change rather than a patch: plugins are compiled against the current base class, so the ecosystem would need an SDK release plugins rebuild against. But it would let WAM hosts live directly inside Tone.js / standardized-audio-context apps — which matters more than it might seem, because the native-context escape hatch is currently broken on Firefox anyway (Tonejs/Tone.js#1457): wrapper contexts polyfill the AudioListener params Firefox lacks, native contexts can't. Context-agnostic construction would make WAM hosting work on Firefox through the wrapper path.

Happy to prototype the class-factory approach in a PR if there's interest — the clear-error guard remains worthwhile either way as the immediate fix.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions