@webaudio-kit/core is React-free. It owns Web Audio graph construction,
frequency clamping, dB/gain math, pitch helpers, playback handles, and cleanup.
@webaudio-kit/react owns React ergonomics. It provides context state, lazy
AudioContext setup, master volume, analyser access, and hook-level playback
state.
@webaudio-kit/react declares @webaudio-kit/core as a peer dependency. The
React package imports and re-exports core helpers, but app projects should own
the core version explicitly so direct core imports and React hook internals use
the same package copy.
The supported local Node floor is >=20.19. That floor follows the current
tooling stack used by the workspace checks while keeping the browser runtime
packages usable for teams still on Node 20 LTS. Publish automation can run on a
newer Node version without changing the supported floor.
apps/demo is a manual QA and public demo target. It should prove the package
APIs are usable, but it should not become the source of library behavior.
No package should create an AudioContext during module import. Browser audio
must be initialized after user interaction.
Good:
const tone = useTone({ frequency: 440 });
<button onClick={() => void tone.play()}>Play</button>;Avoid:
const context = new AudioContext();at module top level.
The React provider creates:
masterGain -> analyser -> destinationTone and sweep calls create short-lived oscillator graphs:
oscillator -> gain -> panner -> masterGainNoise calls create short-lived buffer-source graphs:
bufferSource -> gain -> panner -> masterGainIf StereoPannerNode is unavailable, core falls back to:
source -> gain -> masterGainEach playTone(), playFrequencySweep(), or playNoise() call creates new
nodes. This avoids reusing stopped sources, which the Web Audio API does not
allow.
The playback handle owns:
- oscillator stop
- cleanup after
onended - safe disconnection
- idempotent
stop()
React hooks own:
- current playback handle
isPlaying- timeout cleanup for finite durations
- stopping previous playback before starting a new one
Frequencies default to the 20..20000 Hz range.
Gain defaults to 0.2. Non-finite gain falls back to 0.2; negative gain is
normalized to 0.
Pan defaults to center and clamps to -1..1.
Expected error surfaces:
useAudioContextoutside provider throws a clear React usage error.- unsupported browsers throw
Web Audio API is not available in this browser. - invalid sweep duration throws
durationMs must be a positive number. - invalid noise duration throws
durationMs must be a positive number.
Apps should catch playback errors near UI actions and show a small user-facing message.
Future packages should keep the same ownership boundaries:
- visualizer components should consume analyser data but not own playback
- microphone helpers should isolate permission handling
- AudioWorklet helpers should remain optional and browser-gated