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:

  1. pull existing state
  2. apply restored values
  3. 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.

Was this article helpful?
Dislike