An instrument (+ → Instrument, #331) makes a note a sound. It is a node graph like a sound FX: Notes nodes bring the note in, a Sample plays a sound for it or a Chip makes one the way old game consoles did (#329), an Acid plays an acid bass line as nodeseq.com does (#421), the effects of a sound FX shape it and the Output is what is heard. It is the first piece of the music tools planned in #330: a pattern (#333) plays one, and a song (#334) lays patterns out. A behavior plays one with Play note, and the tab plays it from its keys.
Instruments are listed under Instruments in the asset panel, patterns under Patterns. A new one starts as Notes → Sample → Output: every note from C4 to B4 plays the Sample, which waits for its sound to be picked.
Notes
Notes are numbered as MIDI numbers them: 0 is the lowest, 60 is C4 (the middle C), 69 is the
A at 440 Hz, 127 the highest. A note is named with its octave after it and no dash, sharps only:
C4, C#4, D4 … The lowest is C-1, the highest G9.
The nodes
| Node | What it does |
|---|---|
| Notes | Where the notes come in. any triggers what it is connected to for every note from From to To. note is the number of the note played, frequency its pitch in Hz (440 * 2^((note - 69) / 12)), velocity how hard it is struck, 0 to 1. With Single notes there is one trigger output for every note in the range as well, and each can have a name of its own (Name of C2 in the inspector): the output shows it, and so do the keys of the piano roll. |
| Sample | Plays a sound asset when its trigger is on. With Follow note (on by default) it plays higher or lower by the distance from Root, the note it sounds as it is (C4 by default), and keeps its length — it is played by the same worklet as a Pitch (#317). Off, every note plays it as it is, as for a drum. Play says how it is played while the note is held and once it is let go (table below). Loop start and Loop end are in seconds, 0 at the end being the end of the sound, counted in the stretch of the clip when it plays one. The volume of the sound asset counts. |
| Chip | Makes a raw wave for every note that triggers it, the way sfxr makes its sounds: square, triangle, saw, sine or noise, with a slide, vibrato, an arpeggio and repeat. It needs no sound file. See Chips. |
| Acid | An acid bass line as nodeseq.com plays it: a saw or a square through a low-pass with a sharp resonance, one ADSR on volume and filter, a glide from the note before. It needs no sound file. See Acid. |
| Parameters | The instrument's own parameters, each a number output with a default — like the parameters of a sound FX's Input. One per instrument. |
| Sound FX | Sends the sound through a sound FX asset, as if it were the sound at the FX's Input. Every parameter of that FX is an input here; one left open is the FX's default. The node is titled after the FX, and a double click opens it. |
| Envelope | Shapes a note as it is played: Attack, Decay and Release in seconds, Sustain from 0 to 1. On the way of the sound it is its volume; its output value moves a number with the same curve, from From to To. See Envelopes. |
| Output | What is heard. It takes one sound, like every other sound input; a second edge replaces the first. To play several together, put a Mix in front of it (several Mixes in a row for more than two). One per instrument. |
| Gain, Pitch, Parametric EQ, Filter, Mix, Delay, Bitcrusher, Phaser, Flanger, Pan, Compressor, Limiter, Saturation, Tremolo, Vibrato, Reverb | The effects of a sound FX, the same nodes, with Bypass and Wet. |
Play of a Sample:
| Play | While the note is held | When it is let go |
|---|---|---|
| Gate (default) | Plays the sound once | Fades out in 10 ms |
| One-shot | Plays the sound once | Plays on to the end of the sound |
| Loop | Repeats from Loop start to Loop end | Fades out in 10 ms |
| Loop, then release | Repeats from Loop start to Loop end | Finishes the round it is in, then plays what comes after Loop end, to the end of the sound |
Loop, then release is the sampler's sustain loop: a string, an organ or a pad held as long as the
key, and then its natural decay. It needs something after Loop end. With Loop end at 0 the loop
runs to the end of the sound, so the note ends with the round it is in. Nothing plays until it is
stopped any more: a drone in a game is a Loop played by Play note with duration 0, which holds
it until a Stop sound. A Sample saved with the former Mode and Loop is loaded as one of
these: Gate or One-shot without Loop stays what it was, either with Loop becomes Loop.
The Sample is a device (#400), a compact 480x280 graph units, a plate in polished gold.
trigger is on the left, out on the right, and everything is set right in the graph:
- the sound is picked from the button beside the name, sounds and their clips alike, as in the inspector;
- a screen, Waveform, draws the sound or clip across its whole length, mirrored about the
middle, as loud as its volume plays it. Seconds run along the bottom, the length (and the volume,
where it is not 1) stands on the bezel. What becomes of each part has a colour of its own: gold
is played once, the loop is pale gold on a lit band between two lines, what Loop, then
release plays once the note is let go is amber (After the note), and what a Loop never gets
to is dark. Under the glass it says how it plays (
LOOPS 500 ms TO 900 ms WHILE HELD) and at what pitch (AS IT IS AT C4, orAS IT IS ON EVERY NOTEwithout Follow note). The loop is drawn as both ways of playing hold it: an end of 0 or past the sound is the end of the sound, a start not before the end loops all of it. Without a sound the screen says why: No sound picked, That sound is gone, The sound has no file, Loading or Cannot load; - a button for each Play, Gate, One-shot, Loop and Loop+rel, two by two;
- Root as a chrome knob that reads as a note (C4), and Follow note as a lever beside it (Follow). A drag is one undo step, a double-click goes back to C4;
- Start above End of the loop as fields in seconds, typed in or scrubbed sideways.
Settings that do nothing as things stand are grey, as in the rows they replace: the root without Follow note, the loop's ends without a loop. Nothing on it moves by itself. A new instrument places its Output past the device.
Triggers. The outputs of a Notes node that say "this note" are of their own kind, a
trigger, drawn in lime. A trigger goes only into the trigger input of a source — nothing of a
behavior can get in, and it fits nothing else. A trigger input takes any number of edges, and
the source plays when one of them is on: C1 and D1 can both play the same Sample.
Zones and layers. Several Notes nodes split the keyboard into zones: a bass sample for C1-B2,
another one from C3. Where two ranges overlap, both are heard. A drum kit is one Notes node over
C1-B1 with Single notes on, a Sample on each output (C1 Kick, D1 Snare, F#1 Hi-Hat …) and Mixes
gathering them into the Output: one instrument plays the whole kit. Only one drum sounds for a
note, so each Mix is its other side and the drum keeps its own volume. Two layers that sound at
once (a piano with a string pad under it) are mixed by the Mix's mix: with the curve
Crossfade 0.5 is both at half, with Both at center both in full (see
the curve of a Mix).
An instrument saved while the Output still took any number of sounds and played them together keeps its edges, but does not compile until there is one: the Output says Takes one sound: put a Mix in front of it to play several.
How a note is made
The graph is compiled once per note and kept until the instrument, or a sound or sound FX it
names, changes. For a note, any of a Notes node is on when the note is in its range, a single
output when it is that note; every other trigger is off. A Sample whose trigger is off is silence,
and so is everything only it feeds; a Mix with one silent side is its other side. A note for
which nothing sounds is silence, not a problem: the keys draw it grey and Play note gives out
voice 0.
noteandfrequencyare numbers once the note is known.velocityand the instrument's parameters stay open, filled in for every voice, soSet sound FX parameterscan still move them on a playing note.- Velocity is always loudness: the voice is played at
volume * velocity. The outputvelocityis there for anything else as well, a filter that opens with a harder stroke. - A note is one voice, whatever the graph mixes into it, each way reading its own file, so
Play notegives out onevoiceandStop soundends all of it. It ends when all its ways have ended, or rings on first where it has an echo (see Sounds). - The end of a note (the key let go, the duration of
Play noteover): every Gate and Loop Sample fades out in 10 ms, One-shot ones play on, and Loop, then release ones leave their loop. A buffer source stops looping and plays on by itself. The pitch worklet is told to leave: it lets the round finish that its newest frame has read into, so nothing already read changes under the frames still sounding, and plays on from there. A Sample under an Envelope does not fade: it plays on through the release. - Pitch. A Sample that follows the note is shifted by the phase vocoder of #317, so a note far from the root sounds rough, as there. A note at its root is the sound itself, played without the worklet. On a Chip a Pitch moves its frequency itself, exactly and at no cost.
- At most 12 ways on the pitch worklet play at once, in the whole editor or game. One more cuts the oldest in 10 ms, a note already let go before one still held. Each runs a phase vocoder: measured in Chrome on the Windows runner, 12 take half of the audio thread and 24 all of it. Past that every sound dropped out until enough of them had ended, as a quick run up the keys of sampled one-shots did.
- Sound FX node and a Mix. The arriving sound is set in wherever the FX's Input was. A Mix in the FX therefore plays the sound twice, one source per way, as in the sound FX itself.
A node that keeps the instrument from compiling says so, and the tab's header says it too (otherwise it counts the notes that sound): a Sample with no sound picked or one that is gone, a source nothing triggers, an effect nothing comes into. A Sound FX node naming no FX, or one that is gone, warns and lets the sound through as it is.
Envelopes
An Envelope (#335) shapes a note over its time: from silence up to full in Attack, down to the level Sustain in Decay, held there as long as the note is, and back to silence in Release once the note ends. A new one is 0.005 / 0.1 / 1 / 0.1: a soft start, otherwise the sound as it is.
-
The shape is fixed. The attack is a straight line; the decay and the release ease out, as a struck string does, and are 60 dB of the way there when their time is up. A release takes 10 ms at least, so a note never clicks. There is no Punch: a short decay to a sustain under 1 is the same, and a Gain sets how loud it is overall.
-
A note ending early (shorter than attack and decay) releases from the level it has reached by then, not from the sustain: mid-attack from halfway up, for one.
-
On the way of the sound (Sample → Envelope → Output) it is the volume of everything before it, behind a Mix too. Two in a row multiply. The end of the note goes to it instead of the 10 ms fade: a Gate or Loop Sample plays on through the release (a loop loops on), a Loop, then release one leaves its loop as well, and the note ends when the release has. A One-shot under an Envelope of its own plays through as it would, the Envelope held at its sustain. Echoes after it ring on as ever.
-
Sustain 0 ends the note once its decay is over, however long the note is held (#329): every source under that Envelope ends then, a One-shot and a Chip too. A laser or a jump played by
Play notewithduration0 therefore ends by itself rather than running on silently until aStop sound. -
valuemoves a number with the same curve: at From while the note rests, at To at the top of the attack, and where the sustain puts it between them while held. Its pin is of a kind of its own, an envelope, drawn in coral. It fits only a number the sound itself holds while it plays:frequency,Qandgainof a Filter,panof a Pan,semitonesof a Pitch,gainof a Gain, thewetof these four but the Pitch,mixof a Mix,bits,rateandwetof a Bitcrusher,frequencyandwetof a Phaser,offsetandwetof a Flanger, andfrequencyanddutyof a Chip. Not the time of a Delay, not the times of an Envelope, not a parameter of a Sound FX node, not the slide or the other numbers of a Chip, not the LFO of a Phaser or Flanger. A cutoff, a Gain, the rate of a Bitcrusher and the frequency of a Phaser or a Chip move in equal ratios (octaves, decibels), everything else evenly. Both ends are held to the range of the number it moves. -
A filter sweep: Sample → Filter → Envelope → Output, and a second Envelope with its
inleft open, itsvalueinto the Filter'sfrequency, From 200, To 5000. Each Envelope is a curve of its own; one Envelope can feed several numbers. -
A number an Envelope moves is the Envelope's. It is no longer reached by a parameter or by
Set sound FX parameters. A Pitch it moves comes on top of the shift the note already has. -
The numbers of an Envelope count from the moment the note starts. They can come from
velocityor a parameter (a harder stroke, a longer decay), but changing them later does not bend a note already playing. -
Stop sound with Let ring lets a note with an Envelope fall away in its release; without it the note is cut in 10 ms, as is every note the Stop of the tab ends.
-
The same note again while one is still in its release is a voice of its own; the first plays its release out.
-
The curve runs on the audio thread, one
ConstantSourceNodeper Envelope and note; a number it moves gets it through aWaveShaper. Firefox has nocancelAndHoldAtTime: there the level a release starts from is worked out from the shape. -
Measured (2026-10-06, e2e 34, Chrome offline): attack 0.1, decay 0.2, sustain 0.5, release 0.3 rendered 0.5 at 0.05 s, 1 at 0.1 s and 0.5005 at 0.3 s; released at 0.5 s it was 0.0158 at 0.65 s and 0.0005 at 0.8 s, released mid-attack at 0.05 s it fell from 0.5 to 0.05 at 0.15 s. A Gain moved from 0.25 to 1 by that curve was 0.5 halfway up. Live, a looped note of 0.2 s through an Envelope with a release of 1 s was gone 1.32 s after it started, read every 150 ms.
-
The node is a device (#388), 520x280, a plate in pale sea green. The inputs are on the left,
outandvalueon the right, and everything is set right in the graph:- a dark screen, Level over a note, draws the curve in amber: a letter above each stage
(A, D, S, R), its time below it, and the sustain in percent above its line. The time is
squeezed so a short attack still shows beside a long release: each stage is as wide as the
root of its seconds, the hold a fixed fifth. With Sustain 0 there is no hold and no release,
and the screen says Note ends. While a link takes
value, the bezel says what it moves between (VALUE 400 TO 3500); - a chrome knob for Attack, Decay, Sustain and Release. A drag is one undo step, a double-click goes back to the default, the wheel zooms the graph. The times give their first second most of the turn (5 ms an eighth of the way, 1 s not quite half);
- From and To as fields, typed in or scrubbed sideways, since they have no range a knob could turn through.
The decay at Sustain 1 and From/To while nothing takes
valueare faded; a connected number is greyed and can not be changed. The screen is drawn from the settings, not listened to. - a dark screen, Level over a note, draws the curve in amber: a letter above each stage
(A, D, S, R), its time below it, and the sustain in percent above its line. The time is
squeezed so a short attack still shows beside a long release: each stage is as wide as the
root of its seconds, the hold a fixed fifth. With Sustain 0 there is no hold and no release,
and the screen says Note ends. While a link takes
Chips
A Chip (#329) makes the sound of a note itself, the way old game consoles did and sfxr does:
a raw wave, and what its frequency does over the note. Notes any into its trigger, its out
on through an Envelope, a Filter or a Bitcrusher like any Sample. Where sfxr counts every slider
from 0 to 1, the Chip counts in Hz, semitones and seconds.
| Input | Default | What it does |
|---|---|---|
| Wave (setting) | Square | Square, Triangle, Saw, Sine, Noise (sfxr's: 32 new values every period, so it gets lower with the frequency) or Noise (periodic) (the short noise of the NES, a 15-bit register repeating every 93 steps: metallic, with a pitch). A square and the periodic noise are half as loud as the others, as in sfxr. |
| Follow note (setting) | on | Plays at the pitch of the note. Off, it plays at Frequency. |
| Frequency (Hz) | 440 | The pitch without Follow note. A number connected here goes before both: frequency of a Notes node, a parameter, or an Envelope's value for a sweep in Hz. |
| Duty | 0.5 | How much of a square is up: 0.5 is even, 0.25 and 0.125 thinner and more nasal. Held to 0.01 to 0.99. |
| Duty sweep (/s) | 0 | Moves the duty by that much per second. |
| Slide (st/s) | 0 | Glides the pitch in semitones per second: -100 is a laser falling about eight octaves a second. |
| Delta slide | 0 | Changes the slide by that many semitones per second, every second. |
| Min frequency (Hz) | 0 | Where a slide falls below it, the note ends. 0 never. |
| Vibrato depth (st) | 0 | How far it wobbles up and down, in semitones. |
| Vibrato speed (Hz) | 6 | How often it wobbles a second. |
| Arpeggio (st) | 0 | Jumps the pitch by that many semitones once, Arpeggio time seconds into the note: the "pling" of a pickup. |
| Arpeggio time (s) | 0.1 | When it jumps. |
| Repeat (s) | 0 | Starts slide, duty sweep and arpeggio over every so many seconds: the trill of a powerup. 0 never. |
-
An input that does nothing as things stand is grey in the inspector: Frequency while it follows the note, Duty and Duty sweep for a wave that is no square, Vibrato speed at depth 0, Arpeggio time without an arpeggio. One that is connected counts as moving, whatever was typed into it. Follow note is left off the node and the inspector while Frequency is connected.
-
Frequency and duty run on the audio thread (AudioParams): an Envelope moves them while the note plays, and
Set sound FX parametersreaches one fed by a parameter, as with every other number. A Pitch behind a Chip moves its frequency itself. The rest (slide, vibrato, arpeggio, repeat, the wave) is fixed when the note starts; a parameter can still set it for every note. -
It ends with its note, faded out in 10 ms, or under an Envelope through its release, and with Sustain 0 once the decay is over. Below Min frequency it ends by itself. Envelope, Filter and Bitcrusher are nodes of their own; sfxr's punch is a short decay, its phaser is the Flanger (#341).
-
How it is made: a worklet of its own per note, next to the Pitch and the Bitcrusher in the same module. Every sample is worked out 8 times and averaged, as sfxr does, which takes the worst of the aliasing off a raw wave. Without the worklet a Chip is silent and its note ends at once. The frequency follows sfxr (Tomas Pettersson, MIT licence) as jsfxr (public domain) ports it.
-
The node is a device (#386), 840x280 like the Compressor's, in the warm grey of a handheld console. The inputs are on the left,
outon the right, and everything is set right in the graph:- a button for each wave at the top, the one playing lit, and the Follow note lever beside them;
- two green screens. Wave draws two periods of the wave as loud as it plays, a square at its duty. Pitch over a note draws what slide, delta slide, vibrato, arpeggio and repeat make of the pitch, in semitones from where it starts: three rounds of a repeat, else at least a second, and a quarter past the end when the note falls below Min frequency, with that line dashed and the time it ends written at the top. It starts from Frequency, or from A4 while it follows the note, which only moves where it ends;
- a chrome knob for every number, grouped Oscillator, Slide, Vibrato, Arpeggio and
Repeat. A drag is one undo step, a double-click goes back to the default, the wheel zooms the
graph. Frequencies and times give their low end most of the turn; Duty sweep, the slide and
the arpeggio have 0 straight up and the small numbers either way near it. Min frequency and
Repeat at 0 read
off.
What does nothing as things stand is faded, a connected number is greyed and can not be turned. The screens are drawn from the settings, not listened to, so they also show a Chip nothing plays. The presets give a Chip five columns of room.
-
Measured (2026-10-06, e2e 34, Chrome offline on the Windows runner): a square at 220 Hz with a Pitch of 12 and a duty of 0.25, started at 0.25 s, was silent up to sample 12000 and then made 329 periods in 0.75 s, up 0.249 of each, peak 0.5. Sliding from 440 Hz down two octaves a second with a lowest frequency of 110 Hz it fell silent at 1 s and said so. 64 voices of noise with a slide and vibrato rendered 2 s in 2.0 s: about 63 at once, where a Pitch manages about 24. Live, a note of 0.2 s through an Envelope with a decay of 0.1 s and sustain 0 was gone 0.26 s after it started, read every 150 ms.
Acid
An Acid (#421) plays an acid bass line the way nodeseq.com
plays its Acid bass: one oscillator, a low-pass with a sharp resonance, and one ADSR that shapes
the volume and sweeps the filter alike. Its defaults are nodeseq's acid. Notes any into its
trigger, its out to the Output. It always plays the pitch of the note.
| Input | Default | What it does |
|---|---|---|
| Wave (setting) | Saw | Saw or Square, band-limited (PolyBLEP), so a high note does not alias. |
| Cutoff (Hz) | 250 | Where the filter rests, 40 Hz to 18 kHz. nodeseq's Cutoff 0 to 100 is 40 Hz * 2^(8.8 * value/100), so its 30 is 250 Hz. |
| Resonance | 0.78 | How sharply it rings at the cutoff, 0 to 1: a Q of 0.5 dB to 17.5 dB, as nodeseq's Reso 0 to 100. |
| Env mod | 0.62 | How far the envelope opens the filter: up to five octaves above the cutoff at 1, never above 18 kHz. At 0.62 that is 3.1 octaves, from 250 Hz up to 2.14 kHz. |
| Attack (s) | 0.003 | How long volume and filter take to climb to the top, in a straight line. |
| Decay (s) | 0.16 | How long they take to fall back to where the sustain holds them. Grey at Sustain 1 while Env mod is 0. |
| Sustain | 0.15 | Where the volume stays while the note is held, 0 to 1. The filter stays at 60 % of that up its sweep: at the defaults 420 Hz. |
| Release (s) | 0.08 | How long volume and filter take to close after the note ends. |
| Glide (s) | 0.05 | How long the pitch takes to glide from the note before. 0 does not glide. |
-
Why it squelches: the filter falls back in Hz, not in octaves, as a Web Audio
setTargetAtTimeon the cutoff does. So it drops fast from the top and lingers low. With the sharp two-pole resonance on top, that makes the "yow". Attack, decay and release work as nodeseq automates them: the attack is a straight ramp, decay and release aresetTargetAtTimewith a time constant of a third and a quarter of their time. -
Every note strikes anew and glides from the pitch of the note before on the same Acid, also after a gap of up to 2 s. A note that starts while the one before still sounds cuts it short, in a fall with a time constant of 6 ms, gone after 60 ms. Notes that start at the same moment do not glide and do not cut, they all play.
-
Its own volume: it plays out its Release when the note ends and stops after 1.6 releases and 50 ms, as nodeseq stops its oscillator. So it needs no Envelope behind it. An Envelope there would shape it a second time.
-
Velocity makes it louder or softer. A note at velocity 1 is as loud as nodeseq's bass at its volume 0.8.
-
On the audio thread (AudioParams): Cutoff, Resonance and Env mod. An Envelope can move Cutoff,
Set sound FX parametersreaches any of them fed by a parameter. A Pitch behind it moves its frequency. Wave, the ADSR and Glide are fixed when the note starts. The note's end and the cut are set on the audio clock, to the sample. -
How it is made: a worklet per note, in the same module as the Chip. The filter is a state-variable filter solved without a delay in the loop (Simper's). It has the same curve as the cookbook biquad that Chrome's
BiquadFilterNoderuns, and it stays steady however fast the cutoff moves. Like that node it takes its Q in dB. -
Grit comes from behind it: nodeseq runs its whole mix through a
tanhdrive, a compressor and a limiter. Here that is a Saturation, a Compressor and a Limiter after the Acid or on the master. -
The node is a device (#421), 580x280, in the brushed silver of the 303. The inputs are on the left,
outon the right:- a button for each wave at the top, the one playing lit in orange;
- a dark screen, Filter over a note: the filter where it rests in dull green, at the top of its sweep in acid green, the room between them lit faintly, 20 Hz to 20 kHz, +24 to -48 dB. It says where it rests and how far a note sweeps it; the bezel says where the sustain holds it;
- a chrome knob for every number, grouped Filter (cutoff, resonance, env), Envelope (attack, decay, sustain, release) and Note (glide). A drag is one undo step, a double-click goes back to the default.
The screen is drawn from the settings, not listened to. The presets give an Acid three columns of room; Randomize and Mutate leave it as it is.
-
Measured against nodeseq (2026-10-13, Chrome offline): five sixteenths at 132 BPM, played by the worklet and by nodeseq's voice built from Web Audio nodes as its studio builds it. In every 10 ms window the two were no more than 0.14 dB apart in loudness, and 95 to 97 % of the windows were within a tenth of an octave in brightness (the others are dark windows where only the place of the saw's jump differs). That held at the defaults, at full resonance and sweep, without resonance, and with a slow ADSR. e2e 34 checks it.
-
A Chrome quirk found on the way: an
OscillatorNodestarted inside a block of 128 samples, with its frequency automated, runs its first samples at the frequency it had before it started (440 Hz unless told otherwise). So in nodeseq every note but those on a block's start begins at a phase at random. Nobody hears that, and the Acid starts every note at the same phase.
The Instrument tab
The graph, and below it the keys: all 128 notes, the ones that sound in white and black, the others grey. A key plays while the pointer holds it; dragging across the keys plays each one in turn. A click also gives the keys the computer keyboard, played as in a tracker by where a key is, not what it says, so QWERTZ and QWERTY play alike:
| Keys | Notes |
|---|---|
Z S X D C V G B H N J M |
C to B of the bottom octave (C3 at first) |
, L . ; / |
C to E of the octave above |
Q 2 W 3 E R 5 T 6 Y 7 U |
C to B of the octave above the bottom one |
I 9 O 0 P |
C to E of the octave after that |
← → |
The bottom octave down and up, from C-1 to C8 |
The header says which notes the keys play (Keys C3 to E5), and a line under the keys marks them
while the keys have the focus. A held key does not repeat; letting it go, or the keys losing the
focus, ends the note. A key let go stays half lit while its note is still heard: a one-shot
playing out, a loop left, an echo. Striking it again plays a new note beside it, as a sampler does,
so a drum can be hit quickly. Stop, or Esc on the keys, ends everything the tab started, and
so does closing the tab or pressing Play for the game. Every parameter of the
instrument has a number field in the header, starting at its default: what the keys play with,
heard at once on what is playing. Those values are the tab's own and never written into the asset.
Presets, Randomize and Mutate
Three buttons in the header make 8-bit sounds the way sfxr does (#329). Each is one undo step. Randomize and Mutate play the note last played at once (C4 before any), for up to 2 s. A preset plays nothing: try it on the keys. With Audition on it is heard like any other change.
- Preset opens a menu in two parts. Either kind puts a new graph in place of the
instrument's; groups and notes on the canvas stay.
- Instruments to play tunes on, at the top (see the table below). Their Chips follow the note, so every key plays at its own pitch, and most hold the note while the key is held. They come out the same every time they are picked: Randomize and Mutate make a variant.
- Effects below the line: Pickup, Laser, Explosion, Powerup, Hit, Jump, Blip. Each is Notes for all 128 notes → Chip → Envelope → Filter → Bitcrusher → Output. One Explosion in two has sfxr's phaser as well, a Flanger after the Filter, with sfxr's offset and sweep and no LFO (#341). Every click draws the sound anew within its kind, with sfxr's generator for it. The Chip plays at its own frequency, Follow note off, so every key plays the same sound, as an effect does; turn it on to play the sound as a melody. The Envelope has Sustain 0, so the sound ends by itself.
- Randomize draws every number of the Chips, Envelopes, Filters, Bitcrushers, Phasers and Flangers in the graph anew, as sfxr's randomizer does: most of them wild. A Flanger's offset and sweep come from sfxr's phaser, its LFO and feedback and all of a Phaser, which sfxr has not, within bounds of their own (a rate of 0.05 to 5 Hz, a Phaser at 200 Hz to 4 kHz). It leaves the wave, Follow note, the type and Slope of a Filter, the Stages of a Phaser, every Mix, an Envelope's From and To and every other node as they are. An Envelope gets Sustain 0. An input with an edge into it keeps what it is fed.
- Mutate moves the numbers of those nodes a little, each one in two: a frequency, cutoff or rate by up to a quarter of an octave, the rest by a step and a tenth of its value. A 0 stays 0, so nothing that was off comes on, and Sustain stays as it is.
- Randomize and Mutate are grey while the graph has none of those nodes.
- Audition (off when the tab opens, as in the Pattern tab): on, every change to the graph plays the note last played again, 150 ms after it rests, so dragging a number is heard once you let it be. Moving a node is no change.
The instruments, all on every note unless the table says otherwise:
| Preset | Made of | Note |
|---|---|---|
| Square Lead | Square with a slight vibrato → Envelope → Low-pass 6 kHz | held |
| Pulse Lead | Square at duty 0.13 (about an eighth), thin and nasal, with vibrato → Envelope | held |
| Echo Lead | Square at duty 0.25 → Envelope → Delay, a dotted eighth, ping-pong | held |
| Octave Arp | Square that jumps an octave up and back 12.5 times a second (Arpeggio 12, Repeat 0.08 s) | held |
| Synth Brass | Saw → Envelope with a slow attack → Low-pass that an Envelope opens from 400 Hz to 3.5 kHz | held |
| Detuned Saws | Two saws a tenth of a semitone apart (Pitch 0.1) → Mix → Envelope → Low-pass 7 kHz | held |
| String Pad | As Detuned Saws with vibrato, attack 0.5 s, release 1 s, Low-pass 2.2 kHz | held |
| Phased Pad | Saw with vibrato → Envelope, attack 0.3 s, release 0.8 s → Low-pass 3 kHz → Phaser of 6 stages at 600 Hz, 2.5 octaves wide, 0.3 times a second | held |
| Organ | Square and the square an octave up → Mix → Envelope at full sustain → Low-pass 4.5 kHz | held |
| Flute | Triangle with vibrato, a breath of noise through a Band-pass at 2.5 kHz → Mix → Envelope | held |
| Bell | Two sines 17.6 semitones apart (a ratio of 2.76), each with its own Envelope → Mix → Delay | dies away in 2 s |
| Pluck | Square at duty 0.3 → Envelope → Low-pass that snaps shut from 6 kHz to 600 Hz | dies away |
| Triangle Bass | The triangle of the NES → Envelope at full sustain | held |
| Pluck Bass | Saw → Envelope → resonant Low-pass that snaps open to 2.4 kHz and falls back towards 180 Hz | held |
| Acid Bass | Acid with nodeseq's acid: a saw at cutoff 250 Hz, resonance 0.78, env mod 0.62, attack 3 ms, decay 0.16 s, sustain 0.15, release 80 ms, glide 50 ms → Delay, a dotted eighth, a little of it | squelches, glides |
| Drum Kit | Single notes 36 to 46: Kick on C2, Snare on D2, Closed hat on F#2, Open hat on A#2, as in General MIDI | each ends by itself |
The kick is a sine that drops from 160 Hz until it is below 30 Hz, the snare a falling triangle and high-passed noise, the hi-hats noise above 6 or 7 kHz, short and long. A filter that sweeps has its own Envelope under it, wired to Cutoff: its From and To are the cutoff at rest and at the top.
sfxr counts its sliders from 0 to 1; the presets turn them into Hz, semitones and seconds with sfxr's own formulas. Its envelope holds a sound and lets it fall in a straight line, an Envelope falls away quick at first, so the decay is three times sfxr's hold and fall. sfxr's punch and its filter sweeps are left out, its phaser is #341. A Preset with both of sfxr's filters keeps the low-pass.
Playing one in a game
Play note plays one note of an instrument:
| Input | |
|---|---|
instrument |
The instrument asset, picked on the node or arriving as an id. |
note |
Its number, 60 is C4 (the default). |
velocity |
0 to 1, how hard it is struck: quieter, and handed to what the instrument does with it. Default 1. |
volume |
A factor like the volume of Play sound. Default 1. |
duration |
Seconds until the note is released, on the clock of the sound: its fade is set there to the sample when the note starts. 0 holds it until a Stop sound ends it. |
detached |
As at Play sound: the note outlives the object that played it. |
params |
Values for the instrument's parameters, from an Instrument parameters node. |
tempo |
BPM, what a Delay synced to beats in the instrument counts by. Default 120. |
fx |
A sound FX the note goes through on top of what the instrument does (#346), as through a Sound FX node at its Output. From the asset (the default) is the row Sound FX of the instrument, None none (#352). Its Pitch moves the note's sound itself, with no latency. |
fx params |
Values for that FX's parameters, from a Sound FX parameters node. They stay apart from params: an FX parameter and an instrument parameter of the same name are two numbers. |
bus |
The bus the note is heard on. From the asset (the default) goes along the links of the instrument in the Master routing (#351). Its tab plays straight into the master (#368). |
Its output voice is the number of the note, like Play sound's: Set sound FX parameters moves
its parameters while it plays (the bundle of an Instrument parameters node goes into its
fx params; with an fx a name both have changes both), Stop sound with voice ends it — with Let ring only the sound, so its echoes
ring on, and an Envelope plays its release first. A note belongs to the object that played it and
stops when that object is destroyed.
Instrument parameters works like Sound FX parameters: it names an instrument and picks the
parameters to set, each a number input starting at the default its Parameters node gives it.
Renaming a parameter there renames it on every such node; where Play note and the node name two
different instruments, Play note warns.
Playing in time
The scheduler (#332) plays notes of instruments in time, on the clock of the sound rather than
the game's, so a slow frame or a busy page never moves a note. The Pattern tab plays a
pattern with it, the Song tab a song, and the check scripts drive it directly
(__gm.music).
- Ticks. Musical time is counted in ticks, 480 to the quarter note as in MIDI files: a 1/32 triplet is 40, a 1/16 quintuplet 96, all whole numbers. The tempo always counts quarter notes a minute (20 to 400 BPM), in 6/8 as in 4/4.
- Meter. A song and a pattern pick theirs from one list of signs, 1/2 to 9/8 (#363, #364). A beat is the note value under the line, so 6/8 has six beats of 240 ticks and 7/8 a bar of 3.5 quarters. A position reads as bar and beat from 1 and the tick inside the beat from 0.
- Lookahead. Every 25 ms it plans the notes of the next 100 ms with their exact start. The beat comes from a timer in a Web Worker, which keeps its pace in a hidden tab, where the page's own timers are held to once a second. The first note sounds 50 ms after the start, the time the page needs to get it to the audio thread.
- To the sample. A note planned ahead starts on its sample: a buffer source by Web Audio's
start(when), the pitch worklet by counting down inside the render block it falls in. Its end is set on the clock to the sample as well: the fade of a Gate or Loop Sample. Leaving the loop of a Loop, then release Sample waits for the page, a few milliseconds. - Loops. At the end of a loop the window looks on into the next round, so the seam has no gap. A note reaching past the end of the loop ends there, where the playhead jumps back. Started behind the loop, it plays on.
- Changes. The sequence is read again for every window, so a note drawn while it plays is heard from the next window on; what is planned stays. A new tempo or loop counts from what is planned. Stop fades every note it started, those still to come too, and the next start goes on from there; a jump does the same and starts at the new place.
- Where it stands is the audio clock less the output latency (
AudioContext.outputLatency), so a playhead drawn from it is not ahead of the sound. - Measured (2026-10-06, e2e 34, headless Chrome on the CI's Windows machine): a looped bar at 150 BPM, nine notes exactly 0.4 s apart across the seam, each planned 89 to 91 ms before its second; the position matched the clock less 32 ms latency to the tick. Offline, the worklet started on samples 590, 12000 and 15200 as planned.
Not yet
No swing (#372). No import of sfxr or jsfxr codes, no WAV
export (#340). An Envelope shapes notes of an instrument only, not a sound played through a sound
FX. Notes can not be played from an .mid file or a MIDI keyboard.
Using the editor
- Concepts
- Accounts
- Agents (MCP)
- Scenes
- Scene editing
- Behaviors
- Structs
- Colliders
- Physics
- Textures
- Vector art
- Pixel art
- Fonts
- Videos
- Sounds
- Instruments
- Patterns
- Songs
- Animations
- Curves
- Tiles
- Tilemaps
- State machines
- Shaders
- Particles
- Containers
- Compositions
- Keyboard
Working on the code