Dev Helper Doc
Overview
Using standard HTML, CSS, JavaScript, Web Audio, and Web MIDI APIs, developers can build instruments, effects, sequencers, visualizers, educational tools, and interactive audio experiences directly inside professional production environments.
At the center of this workflow is WaxWeb.js — a lightweight framework that simplifies transport synchronization, MIDI handling, scheduling, playhead access, automation, and state management.
No special SDK is required.
No plugin framework experience is required.
If you can build a website, you can build a WAX app.
Getting Started
Include WaxWeb.js at the top of the script.
<script src="wax-web.js"></script>
Create a WAX instance:
const wax = WaxWeb.create({
appName: "my-first-app"
});
This gives you access to:
wax.audio
wax.midi
wax.playhead
wax.transport
wax.scheduler
wax.data
wax.bridge
Audio
Use the Web Audio API exactly as you normally would.
Inside WAX, audio is already activated by the host.
You do not need “Click to Start Audio” buttons.
Output (Instruments & Effects)
const wax = WaxWeb.create();
const ctx = wax.audio.context();
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.connect(gain);
gain.connect(ctx.destination);
gain.gain.value = 0.1;
osc.start();
The AudioContext runs tied to the DAW’s sample rate and processing block size.
Input (Effects)
To receive audio from the DAW:
const wax = WaxWeb.create();
const ctx = wax.audio.context();
wax.audio.input(ctx).then((input) => {
input.connect(ctx.destination);
});
This works similarly to microphone capture in a browser, except the source comes from the DAW.
MIDI
WAX uses the standard Web MIDI API.
MIDI input comes from the DAW track.
MIDI output is sent back into the DAW.
Initialize MIDI
const wax = WaxWeb.create();
wax.midi.ready().then(() => {
console.log("MIDI Ready");
});
Receive MIDI
wax.midi.onMessage((msg) => {
console.log(msg.type);
console.log(msg.note);
console.log(msg.velocity);
});
Example message types:
- noteon
- noteoff
- cc
- pitchbend
- programchange
Send MIDI
wax.midi.noteOn(60, 127);
wax.midi.noteOff(60);
wax.midi.cc(1, 64);
Transport
Respond to DAW playback and BPM changes.
Playback Events
const wax = WaxWeb.create();
wax.transport.onPlay(() => {
console.log("DAW Started");
});
wax.transport.onStop(() => {
console.log("DAW Stopped");
});
BPM Updates
wax.transport.onBpm((bpm) => {
console.log("Host BPM:", bpm);
});
Playhead
The playhead provides access to:
- transport position
- PPQ timing
- tempo
- looping
- time signature
- playback state
This is the timing authority for DAW synchronization.
Start Playhead Updates
const wax = WaxWeb.create();
wax.playhead.start(8);
8 = update interval in milliseconds.
Read Timing
const timing = wax.playhead.getTiming();
console.log(timing.ppq);
console.log(timing.bpm);
console.log(timing.isPlaying);
Useful Timing Values
timing.ppq
timing.ppqBarRelative
timing.ppqExtrapolated
timing.timeInSeconds
timing.timeInSamples
timing.bpm
timing.timeSigNumerator
timing.timeSigDenominator
Scheduling
JavaScript timers are not reliable for audio playback.
Functions like:
setInterval()
requestAnimationFrame()
can stall when:
- the editor closes
- the tab is hidden
- the UI thread slows down
Professional timing must happen on the audio thread.
Correct Scheduling
Instead of:
setInterval(() => {
playSound();
}, 125);
Schedule directly on the Web Audio timeline:
const when = audioContext.currentTime + 0.05;
source.start(when);
The audio engine guarantees accurate playback.
Tempo-Based Scheduling
WAX provides a scheduler system for DAW-synced sequencing.
Step Sequencer Example
const wax = WaxWeb.create();
const ctx = wax.audio.context();
const scheduler =
wax.scheduler.createStepScheduler({
audioContext: ctx,
steps: 16,
onStep(step, when) {
playStep(step, when);
}
});
scheduler.start();
This automatically:
- follows DAW transport
- synchronizes to BPM
- uses PPQ timing
- schedules on the audio thread
- falls back to local clocking when transport stops
Understanding PPQ
PPQ = Pulses Per Quarter Note.
This is the most important timing system in DAWs.
Examples:
ppq = 0
Beginning of song.
ppq = 1
One quarter note later.
ppq = 2.5
Halfway through beat 3.
Converting PPQ to Steps
16th-note sequencer:
step = Math.floor(ppq * 4) % 16;
DataTree
DataTree handles persistent state.
Use it for:
- presets
- sequencer patterns
- UI state
- project recall
- saved parameters
Save State
const wax = WaxWeb.create({
appName: "my-synth"
});
wax.data.push({
cutoff: 1200,
resonance: 0.4,
waveform: "saw"
});
Load State
wax.data.pull().then((data) => {
console.log(data);
});
Hydration Events
wax.data.onHydrated((data) => {
console.log("Restored", data);
});
Best Practice
Always attempt a pull before pushing data.
Incorrect:
wax.data.push(defaultState);
on startup can overwrite the user’s saved preset.
Correct flow:
- pull existing state
- apply restored values
- only push after user changes
Automation
WAX automation is MIDI-based.
The recommended workflow:
- send CC when user moves controls
- receive CC from host automation
- update UI + audio engine together
Avoiding Feedback Loops
Only send MIDI when the change originated from the user.
Do not retransmit incoming automation back to the host.
Example
let fromMIDI = false;
slider.addEventListener("input", () => {
if (!fromMIDI) {
wax.midi.cc(1, slider.value);
}
});
wax.midi.onMessage((msg) => {
if (msg.type === "cc") {
fromMIDI = true;
slider.value = msg.value;
fromMIDI = false;
}
});
Background Execution
Audio scheduling should never depend entirely on UI timing.
The UI thread may:
- slow down
- sleep
- pause
- stall
The audio thread continues independently.
Use:
- AudioContext.currentTime
- scheduled playback
- lookahead scheduling
- transport synchronization
instead of relying on visual timers alone.
Quick Reference
Create App
const wax = WaxWeb.create({
appName: "my-app"
});
Audio
wax.audio.context()
wax.audio.input()
wax.audio.output()
MIDI
wax.midi.ready()
wax.midi.onMessage()
wax.midi.noteOn()
wax.midi.noteOff()
wax.midi.cc()
Transport
wax.transport.onPlay()
wax.transport.onStop()
wax.transport.onBpm()
Playhead
wax.playhead.start()
wax.playhead.stop()
wax.playhead.getTiming()
wax.playhead.ppq()
wax.playhead.bpm()
wax.playhead.isPlaying()
Scheduler
wax.scheduler.createStepScheduler()
DataTree
wax.data.push()
wax.data.pull()
wax.data.onHydrated()
wax.data.cached()
Final Notes
WAX allows web applications to become deeply integrated audio tools inside professional production environments.
By combining modern browser technologies with DAW synchronization, transport awareness, MIDI routing, scheduling, and persistent state management, WAX dramatically lowers the barrier to creating powerful music software.
The web is no longer separate from audio production.
With WAX, the browser becomes part of the studio.