npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

webaudiofontplayer

v1.0.0

Published

WebAudioFont Player

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 webaudiofontplayer

Preset 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 notes

player.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 point

Audio 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.destination

TypeScript

Type declarations are included. No @types package needed.

import WebAudioFontPlayer, { Sf2Preset, NoteEnvelope, Slide } from 'webaudiofontplayer';

License

MIT © Maxime Larrivée-Roy