Skip to content

Feature request: public accessor for underlying AudioParam on Param/Signal #1418

@naomiaro

Description

@naomiaro

Summary

Param stores the native Web Audio AudioParam as protected _param, with no public accessor. Applications that need direct access to the underlying AudioParam — for native Web Audio automation scheduling or for working around suspended AudioContext behavior — must cast and access the private field.

Use Cases

1. Native Web Audio fade automation

When building a multi-track audio editor, fade envelopes (fade in/out on clips) are scheduled using native AudioParam methods (setValueAtTime, linearRampToValueAtTime, setValueCurveAtTime, cancelScheduledValues). These envelopes are managed entirely outside Tone.js's event tracking because:

  • The fades are re-scheduled frequently (on every play, pause, seek, and loop iteration)
  • They need to be cancelled atomically without affecting other Tone.js-managed parameters
  • Using Param's wrapper methods would add entries to Param._events, causing stale automation state that conflicts with our own envelope management

The cleanest approach is direct AudioParam access, keeping Tone.js's internal timeline and our automation completely separate.

2. Immediate value changes on suspended AudioContext

Setting .value on a Tone.js Param calls cancelScheduledValues(this.now()) + setValueAtTime(value, this.now()). When the AudioContext is suspended (e.g., before user gesture, or mobile Safari backgrounding), this.now() is frozen. While this does technically schedule the value, in practice we've observed that calling setValueAtTime(value, 0) directly on the native AudioParam gives more reliable immediate-effect behavior for mute toggling during suspended state — preventing brief audio glitches where all tracks are audible before solo muting takes effect on context resume.

Current Workaround

We access the private _param field via type casting:

let hasWarned = false;

export function getUnderlyingAudioParam(signal: unknown): AudioParam | undefined {
  const param = (signal as { _param?: AudioParam })._param;
  if (!param && !hasWarned) {
    hasWarned = true;
    console.warn(
      'Unable to access Tone.js internal _param. ' +
      'This likely means the Tone.js version is incompatible.'
    );
  }
  return param;
}

This works on Tone.js 15.x but is fragile across version upgrades and relies on internal implementation details.

Suggested API

A read-only public property on Param:

// In Param class
get nativeParam(): AudioParam {
    return this._param;
}

Or alternatively on Signal:

// In Signal class  
get nativeParam(): AudioParam {
    return this._param._param;
}

This would allow legitimate use cases that need native Web Audio API access without breaking encapsulation of Tone.js's internal state management.

Environment

  • Tone.js version: 15.1.22

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