Beep.js
Beep is a JavaScript toolkit for building browser-based synthesizers.
Install / Use
/learn @stewdio/Beep.jsREADME
Beep.js

TL;DR
Create a synthesizer with one line of code: synth = new Beep.Instrument()
Or plink away on the demo synth: http://beepjs.com.
Tap the pulsing Play button for a
jaunty music lesson.
MIDI controller support
Beep accepts input from MIDI controller keyboards via the brand new Web MIDI API. (Just so we’re clear, this is very awesome ;) You’ll need either Chrome 42 or Chrome 43+. For Chrome 42 you must enable the Web MIDI API manually by visiting chrome://flags/#enable-web-midi and clicking Enable. For Chrome 43 and later this is enabled by default. Simply plug in your modern MIDI controller keyboard via USB, then load up Beep. Your keys and pitch-bending wheel will work just fine. And further support is coming soon!
Hackable
Beep is a JavaScript toolkit for building browser-based synthesizers using
the WebAudio API. It takes a “batteries included” approach, meaning it boots
up ready to give you the audio equivalent of “Hello, World!” without too much
fuss. One line like synth = new Beep.Instrument() will build a bundle of
Trigger interfaces, each with its own Voices for Notes—that is, a piano
keyboard that you can begin banging on immediately. But what’s a software
piano that can’t play itself? Use synth.scorePlay() to play the default
score provided for you. (And yes, you can always write your own scores!)
The blurb above and descriptions below include some sample code. If you’re
new to hacking around in the browser you may be wondering where that code’s
supposed to go. Are you viewing this in a modern desktop browser? Then you
can open up your browser’s JavaScript Console and start hacking away right
now. Here’s how:
Chrome: View → Developer → JavaScript Console, or
⌥⌘J.
Safari: First,
enable the Developer menu.
Then, Develop → Show Error Console, or ⌥⌘C.
Firefox: Tools → Web Developer → Web Console, or
⌥⌘K.
Opera: View → Developer Tools → Opera Dragonfly,
or ⌥⌘I, then click on the Console tab.
Notes
Creating a new note is easy: n = new Beep.Note(). But unless you’re content
with nothing but concert A’s blaring at 440Hz all day, you’re going to want to
create other notes like so: new Beep.Note('E♭') or new Beep.Note('5E♭')
for an E♭ that’s in the 5th octave rather than the default 4th octave. So what
does that 5E♭ give you anyway? An object like this:
{
A: 440, // What Concert A are we tuned to?
hertz: 622.253…, // Frequency of the note.
isFlat: true, // Set if ♭. Similar: isSharp and isNatural.
letter: "E", // Explains itself, no?
letterIndex: 4, // ['ABCDEFG'].indexOf(letter).
midiNumber: 75, // Corresponding MIDI controller keyboard code.
modifier: "♭", // Set to ♭, ♮, or ♯.
name: "E♭", // Note name. Will include ♮.
nameIndex: 7, // ['A♭','A♮','B♭','B♮','C♮'…].indexOf(name)
nameSimple: "E♭", // Note name. Will NOT include ♮.
octaveIndex: 5, // On a standard piano, 0–8.
pianoKeyIndex: 55,// On a standard piano, 0–87.
tuning: "EDO12" // Default: Equal Division of Octave into 12 steps.
}
Flexible parameters
Sure, you can call new Beep.Note('E♭') and accept the above default
parameters that come with it. But you can also send an Object to Note
instead of a String and set each of those parameters manually! No specific
param is required so just send what you need:
new Beep.Note({ A: 442, name: 'E♭', octaveIndex: 5 })
By the numbers
Can we just throw all this named-note garbage out the window? Yes. Want the
Devil’s note? Try new Beep.Note(666). What does that give you?
{ hertz: 666 } I happen to like named notes though. They provide a pretty
nice grid to work with, eh?
Easy ASCII
It might quell your anxieties to know that Note will intelligently convert
the common # (number) into a proper ♯ (sharp) and will also accept a
lowercase b as a substitue for ♭ (flat). There’s no need to use ♮
(natural) but it is in the code there should you desire to invoke it.
Smart conversion
If you commit a serious blunder like new Beep.Note('B♯') don’t stress,
Note will kindly assume you intended Note('C♮') instead.
(There is no B♯.)
If you happen to be old school German then, yes, you can use H instead of
B. (Similarly, there is no H♯. Weirdo.)
Western tunings
Right now only western tunings are supported—I’m afraid that’s all I know how
to work with. All note params pass through Note.validateWestern() which does
the above fancy logic. From there I’ve included support for two separate
tunings: Just intonation
and Equal temperament.
More needs to be written on this topic for sure…
Bach up a second
So all that’s great, but a Note is just a mathematical model. (You’ll notice
it has no play() method for example.) It doesn’t make any sound. For that we
will need a Voice.
Voices
How do you make a Note sing? Give it a Voice. Or rather—create a Voice
initialized with a Note and maybe pass it an AudioContext to pipe the
sound out to. Just as with Note the arguments for Voice are all optional.
Providing none will yield a Voice with a default Note of 440Hz:
voice = new Beep.Voice()// We’re running with defaults.
voice.play()// Listen to that pure 440Hz Concert A.
voice.pause()// Ok, we’ve had enough.
Note arguments
Voice will pass note-like arguments to Note. It doesn’t take an in-state
Liberal Arts degree to imagine what new Beep.Voice('2E♭') or
new Beep.Voice({ A: 442, name: 'E♭', octaveIndex: 2 }) might produce then.
You could even try new Beep.Voice(new Beep.Note('2E♭')) if you’re not into
that whole brevity thing, man.
Audio arguments
If you do not pass an AudioContext or GainNode to Voice it will create
an AudioContext for itself. This is convenient because it means Voice just
works (batteries-included, eh?) but there are hardware limits on the number of
AudioContexts you can create. We’ll see how to solve this later by creating
an Instrument and passing its AudioContext to each Voice.
Only fix what’s Baroque
I guess all the above is pretty cool, but having to type voice.play() and
voice.pause() everytime I want to voice a Note is kind of a drag. And
that’s where Trigger comes in.
Triggers
We can dream up a Note, give it a Voice, but wouldn’t it be great if we
had some visible DOM Elements and Event Listeners working on our behalf?
Behold, your default Concert A: t = new Beep.Trigger(). Simply creating
a new Trigger will also construct the DOM bits and listeners for you.
No further fuss necessary.
Notes & Voices
As you may have guessed, Trigger will create a Voice for you and assign it
a Note. Setting this at initialization time is trivial:
new Beep.Trigger('E♭'). See the Voice description above to get an idea of
the variation possible here. And it’s likewise trivial to alter the Note or
Voice after creation.
Many Voices
Rather than one single voice, Trigger is setup to handle a whole Array of
them. In fact, the default Trigger uses two voices: one employs a sine-wave
oscillator at the intended Note while a second employs a square-wave
osciallator running one octave lower for a nice chunky Nintendo sound.
Customizing your instance’s createVoices() method is the name of the game!
Audio arguments
Just like Voice, Trigger is happy to ingest an AudioContext or
GainNode argument but will make do without one if it has to. See the above
Voice blurb for more details. Additionally you can pass it a Function…
Customizing Trigger’s createVoices() method
Upon initialization each instance of Trigger calls its createVoices()
method. If you’re the type of gal that likes to annihilate mosquitos using
atom bombs then you can just overwrite Beep.Trigger.prototype.createVoices.
Otherwise, why not pass a custom function during initialization like so:
var trigger = new Beep.Trigger( '2Eb', function(){ this.voices.push(
// Let’s call this our “Foundation Voice”
// because it will sing the intended Note.
new Beep.Voice( this.note, this.audioContext )
.setOscillatorType( 'sine' )
.setAttackGain( 0.4 ),
// This Voice will sing a Perfect 5th above the Foundation Voice.
new Beep.Voice( this.note.hertz * 3 / 2, this.audioContext )
.setOscillatorType( 'triangle' )
.setAttackGain( 0.1 ),
// This Voice will sing 2 octaves above the Foundation Voice.
new Beep.Voice( this.note.hertz * 4, this.audioContext )
.setOscillatorType( 'sawtooth' )
.setAttackGain( 0.01 ),
// This Voice will sing 1 o
Related Skills
docs-writer
99.2k`docs-writer` skill instructions As an expert technical writer and editor for the Gemini CLI project, you produce accurate, clear, and consistent documentation. When asked to write, edit, or revie
model-usage
337.1kUse CodexBar CLI local cost usage to summarize per-model usage for Codex or Claude, including the current (most recent) model or a full model breakdown. Trigger when asked for model-level usage/cost data from codexbar, or when you need a scriptable per-model summary from codexbar cost JSON.
arscontexta
2.9kClaude Code plugin that generates individualized knowledge systems from conversation. You describe how you think and work, have a conversation and get a complete second brain as markdown files you own.
zola-ai
An autonomous Solana wallet agent that executes payments via Twitter mentions and an in-app dashboard, powered by Claude.
