Step-by-step guide for building Web Audio processing pipelines — AudioWorklet processors, custom nodes, parameter automation, and WebMIDI integration. Use when the user asks how to build or wire up Web Audio code from scratch. Trigger on phrases like "build an AudioWorklet", "create a custom Web Audio effect", "how do I use WebMIDI", "how do I write an AudioWorkletProcessor", or "how do I connect
The exact command may vary by repository. Check the README on GitHub.
For the skill author
Drop this on your repo README
Shows your skill is listed on Skillteca, generates a backlink and trackable traffic.
[](https://www.skillteca.com.br/skills/webaudio-guide?utm_source=badge&utm_medium=readme&utm_campaign=badge)
One short email with only the new Automação skills. 4 minutes of reading, no spam, unsubscribe with one click.
You confirm your email on the first send. No spam. Unsubscribe with one click.
Web Audio Processing Guide
Step 1 — Set up AudioContext and load worklet module
Create AudioContext inside a user gesture handler (click, keydown) to satisfy autoplay policy.
Call await audioContext.audioWorklet.addModule('path/to/processor.js') before creating nodes.
The module path is resolved relative to the page; bundlers may require special config (e.g. Vite's ?worker&url import).
Check audioContext.state and call audioContext.resume() if it is 'suspended'.
Step 2 — Write the AudioWorkletProcessor
Subclass AudioWorkletProcessor in a separate file; call registerProcessor('name', Class) at module scope.
Implement process(inputs, outputs, parameters) — inputs[n][channel] and outputs[n][channel] are Float32Array views for the current render quantum. Use .length in loops instead of hardcoding 128.
Pre-allocate all working buffers in constructor(); never create objects or arrays inside process().
Declare custom AudioParams via static get parameterDescriptors() returning an array of { name, defaultValue, minValue, maxValue, automationRate } descriptors.
Return true from process() to keep the processor alive; false or no return shuts it down.
Step 3 — Wire up the node graph
Instantiate the node on the main thread: new AudioWorkletNode(ctx, 'name', options).
Use node.connect(destination) and source.connect(node) to build the signal path; AudioContext.destination is the hardware output.
Access AudioParams via node.parameters.get('paramName') — set .value for immediate changes.
Schedule smooth automation with .linearRampToValueAtTime(), .setTargetAtTime(), or .setValueCurveAtTime() on the AudioParam.
Tear down cleanly: node.disconnect(), then source.stop(), then audioContext.close().
Step 4 — Worklet ↔ main thread communication
Use this.port.postMessage() / node.port.onmessage for low-frequency control (meters, state changes, error reporting).
Batch postMessage calls; avoid sending one per render quantum (currently ~344/sec at 44.1 kHz with 128-frame quanta).
For high-frequency or low-latency control (e.g. live gain, pitch), share a SharedArrayBuffer and read/write integer views with Atomics.load() / Atomics.store(). Scale floats to integers or bit-cast through a private buffer; Atomics does not operate on Float32Array.
Cross-origin isolation (COOP: same-origin + COEP: require-corp response headers) is required for SharedArrayBuffer.
Step 5 — WebMIDI integration
Request access with await navigator.requestMIDIAccess({ sysex: false }); handle the MIDIAccess object on success.
Enumerate inputs via midiAccess.inputs.forEach(input => ...) and attach input.onmidimessage = handler.
Parse the MIDIMessageEvent.data byte array: data[0] & 0xF0 = status (0x90 = note on, 0xB0 = CC, 0xE0 = pitch bend), data[1] = note/CC number, data[2] = velocity/value.
Map MIDI values to AudioParam targets: normalize 7-bit CC (0–127) to your parameter range and write via .setTargetAtTime() or a SharedArrayBuffer cell for real-time feel.