webaudiofontplayer
v1.0.0
Published
WebAudioFont Player
Maintainers
Readme
webaudiofontplayer
A sample-based instrument player for the Web Audio API, designed to work with presets generated by sf2-json.
Supports full MIDI controller semantics: volume, expression, sustain pedal, and pitch bend — with envelope-based note scheduling and up to 64 concurrent voices per instance.
Installation
npm install webaudiofontplayerPreset format — sf2-json
webaudiofontplayer expects presets in the sf2-json format: SoundFont 2 (.sf2) files converted to JSON objects where each zone contains a base64-encoded audio sample.
Use the sf2-json package to convert your .sf2 files:
sf2tojson ./assets/piano.sf2 ./data/json/This produces one .json file per instrument, each exporting an object shaped like:
{
zones: [
{
keyRangeLow: 0,
keyRangeHigh: 127,
originalPitch: 6000,
coarseTune: 0,
fineTune: 0,
loopStart: 3000,
loopEnd: 8000,
sampleRate: 44100,
file: "T2dnUwAC...", // base64-encoded audio
ahdsr: [
{ duration: 0, volume: 1 },
{ duration: 0.5, volume: 1 },
{ duration: 0.1, volume: 0 }
]
}
]
}Quick start
import WebAudioFontPlayer from 'webaudiofontplayer';
import preset from './presets/acoustic_grand_piano.json'; // sf2-json format
const audioCtx = new AudioContext();
const player = new WebAudioFontPlayer(preset, audioCtx);
// Play middle C (MIDI 60) for 2 seconds at 80% volume
player.queueWaveTable(audioCtx.currentTime, 60, 2.0, 0.8);API
new WebAudioFontPlayer(preset, audioCtx, compressor?)
Creates a new player instance and initializes the audio graph.
| Parameter | Type | Description |
|-------------|------------------------|-------------|
| preset | Sf2Preset | sf2-json preset object |
| audioCtx | AudioContext | Web Audio API context |
| compressor | AudioCompressorNode \| null | Optional — routes output through your compressor or effects chain. If omitted, connects directly to audioCtx.destination. |
The compressor parameter expects any object with an input: AudioNode property:
// With a custom compressor
const player = new WebAudioFontPlayer(preset, audioCtx, myCompressor);
// Without compressor (connects to destination)
const player = new WebAudioFontPlayer(preset, audioCtx);player.preset
Gets or sets the active sf2-json preset. Swapping the preset takes effect on the next queueWaveTable call; notes already playing continue with the previous preset.
import flute from './presets/flute.json';
player.preset = flute;player.queueWaveTable(when, pitch, duration, volume?, slides?)
Schedules a note for playback.
| Parameter | Type | Description |
|-----------|------------|-------------|
| when | number | AudioContext time in seconds to start. Use audioCtx.currentTime for immediate playback. |
| pitch | number | MIDI note number (0–127). Fractional values are supported for microtonal use. |
| duration | number | Note duration in seconds. |
| volume | number | Velocity/volume from 0.0 to 1.0. Defaults to 0.5. Internally capped at 0.8. |
| slides | Slide[] | Optional pitch glide instructions (see below). |
Returns a NoteEnvelope handle (a GainNode subtype), or null if no zone matched the pitch or the sample has not yet been decoded.
// Immediate note
const env = player.queueWaveTable(audioCtx.currentTime, 60, 1.5, 0.9);
// Scheduled note
const env = player.queueWaveTable(audioCtx.currentTime + 0.5, 64, 1.0, 0.7);
// Cancel a specific note early
env?.cancel(); // soft fade (~20ms)
env?.cancel(true); // hard cut (~5ms)Slides
A Slide defines a linear pitch glide applied to the note's playback rate:
// Bend up 2 semitones over 500ms, then back down 1 semitone over 300ms
player.queueWaveTable(audioCtx.currentTime, 60, 2.0, 0.8, [
{ when: 0.5, delta: 2 },
{ when: 0.8, delta: -1 }
]);| Property | Type | Description |
|---------|----------|-------------|
| when | number | Time offset from note start (in seconds) when the slide reaches its target |
| delta | number | Pitch delta in semitones relative to the original note pitch |
player.cancelQueue()
Immediately silences all playing and scheduled notes. Useful for stopping all sound at once (e.g. on song stop or scene change).
await player.cancelQueue();player.setController(number, value)
Processes a MIDI Control Change (CC) message.
| CC | Name | Effect | |----|-----------------|--------| | 7 | Channel Volume | Sets the main gain (0–127 → 0.0–1.0) | | 11 | Expression | Sets a secondary expression gain (0–127 → 0.0–1.0) | | 64 | Sustain Pedal | ≥64 activates sustain; <64 releases held notes |
player.setController(7, 100); // Volume ~78%
player.setController(11, 127); // Full expression
player.setController(64, 127); // Sustain on
player.setController(64, 0); // Sustain off → releases held notesplayer.setPitchBend(value)
Applies a MIDI pitch bend to all active and future notes. The bend range is ±2 semitones.
| Parameter | Type | Description |
|----------|----------|-------------|
| value | number | Raw 14-bit MIDI pitch bend value (0–16383). Center = 8192. |
player.setPitchBend(8192); // No bend (center)
player.setPitchBend(16383); // Full bend up (+2 semitones)
player.setPitchBend(0); // Full bend down (−2 semitones)player.isSustainActive()
Returns true if the sustain pedal (CC64) is currently held.
if (player.isSustainActive()) {
// sustain is active
}player.registerSustainNote(cancelFn)
Registers a callback to be called when the sustain pedal is released. Use this to implement hold behaviour: instead of letting a note stop naturally, extend it until sustain is released.
const env = player.queueWaveTable(when, pitch, 9999, volume);
if (player.isSustainActive()) {
player.registerSustainNote(() => env?.cancel());
} else {
// Schedule normal note end
setTimeout(() => env?.cancel(), duration * 1000);
}player.close()
Releases all Web Audio nodes and clears internal state. Must be called when the player is no longer needed to avoid memory leaks.
player.close();
// player must not be used after this pointAudio graph
The internal signal chain per player instance:
BufferSource(s)
│
Envelope (GainNode × N, up to 64 voices)
│
mainGain (CC7 volume)
│
expressionGain (CC11 expression)
│
compressor.input ──or── audioCtx.destinationTypeScript
Type declarations are included. No @types package needed.
import WebAudioFontPlayer, { Sf2Preset, NoteEnvelope, Slide } from 'webaudiofontplayer';License
MIT © Maxime Larrivée-Roy
