Table of Contents
A sound asset is an uploaded audio file. Nothing wears it and nothing places it — it is neither on screen nor in the scene tree. A graph plays it, with Play sound, and that is the only way a project makes a noise.
Getting a sound in. Dropping an .mp3, .ogg, .wav or .m4a file onto the canvas
uploads it and makes the asset in one go; several at once are imported one after another, and a
file that fails does not stop the rest. The asset is named after the file ("jump.wav" becomes
"jump"). A dropped file is recognised by its extension, not by its MIME type: browsers report
an .m4a as audio/mp4, audio/x-m4a or as nothing at all.
+ → Sound asks the other question and opens the picker: every sound the server already holds, one row each. A sound has nothing to show, so the row carries a play triangle at its left that plays it — pressing that is a preview and picks nothing. Picking a row and pressing Use sound makes the asset; Upload… is the way in for a file the server does not have yet. A row captioned with an asset name is one this project already uses, and picking it uses that asset rather than making a second one.
Where it goes. The bytes are uploaded to POST ?r=sounds and stored as
data/sounds/{folder}/{sha256}.{ext} — in the uploader's folder and named after their own
content, exactly like a typeface, and with the same four consequences (one file per sound however
often it is uploaded, cacheable forever, a short string in the project document, an undoable
delete). Delete in the picker removes a file of your own nobody uses, as for
images. A sound is never re-encoded on the
way: what the server stores is the file that was picked. The format is read from the file's own
signature, so nothing a request claims can decide what is written.
What the asset says. Its inspector has these rows and a preview:
| Row | Meaning |
|---|---|
| Volume | How loud this sound is by itself, 0-1. |
| Loop | Whether it is music: it repeats until something stops it. |
| Stream | Whether a game plays it from a copy as Opus, a few seconds at a time, rather than decoding all of it first (#349). Off by default. See Streaming long sounds. |
| Sound FX | The sound FX it goes through wherever it plays, none while none is picked (#352). See The sound FX of an asset. |
The preview plays through the very same library a running game uses — same decode, same device — so what the inspector plays is what the graph will play.
These rows hold for the sound and all its clips. The sound itself is always the whole file.
Clips
A clip (#357) is a stretch of a sound's file with a name of its own, as a frame is a cut-out of a texture. A sound has as many as you make, named Clip 1, Clip 2 … at first.
- Every sound field offers the clips right after their sound: Kick, Kick / Clip 1,
Kick / Clip 2. That is
Play sound,Stop sound, the Sample of an instrument, the impulse of a Reverb, the Sound of a song item and the test sound of the Sound FX tab. A clip that is gone reads as its id with a?, as a frame does. - A clip has its own cut: start, end, the two fades and their curves, and Reverse. Volume, Loop, Sound FX and Bus are the sound's and hold for every clip of it.
- Picked in the Sound tab, its inspector has these rows, a Delete clip button below them:
| Row | Meaning |
|---|---|
| Name | What the fields call it after the sound's name. |
| Start (s), End (s) | The stretch of the file the clip is. End 0 is the end of the file. |
| Reverse | Plays that stretch backwards, from its end to its start (#321). See below. |
| Fade in (ms), Fade out (ms) | How long the start fades in and the end fades out when that edge lies inside the file, 5 each by default (#345). |
| Fade in curve, Fade out curve | The shape of each fade: Linear (the default), Equal power, S-curve, Exponential or Logarithmic. See below. |
- Delete (the key, or the button) takes the picked clip away. Whatever names it then reads as missing and plays nothing, and Ctrl+Z brings it back, as with a frame.
- A copy of a sound gets copies of its clips with ids of their own: Duplicate, Embed a copy and a song duplicated with the takes it keeps. A song item on a clip then plays the same clip of the copy.
- The clips are not listed in the Assets panel, only in the fields.
The Sound tab
Opening a sound (double-click in the Assets panel, or Enter) brings up its tab (#343): the file as
one waveform under a ruler in seconds. Under the ruler runs a strip with the
clips of the sound as bands, each with its name (#357). A clip is the sample for
whatever plays it: Play sound, an item in a song and the Sample of an
instrument play its stretch and nothing else, and everything that counts in seconds
counts in it — length, the rounds of a song item and their waveform, a Sample's loop points, a
Pitch.
- With no clip picked, drag in the strip or in the wave to make a new clip from where you pressed. It is picked right away.
- Click a band, or a clip's stretch in the wave, to pick that clip. Click beside the clips to pick the sound again. The picked clip's band is lit, and its stretch stands between two handles in the wave. What lies outside is darker. With no clip picked the whole file is bright and has no handles.
- The other clips stay faint in the wave, a pale tint with a thin edge over their stretch, so you see where they lie while you work on one.
- With a clip picked, a drag only changes that clip (#403):
- Drag a handle in the wave to move that edge. An edge cannot pass the other.
- Drag its band in the strip to move the whole clip. Its length stays, and it stays inside the file.
- Any other drag does nothing. It makes no new clip and does not span the picked one anew. Pick the sound first to make another clip.
- One drag is one undo step.
- Reverse, the button with the play triangle pointing left, plays the picked clip backwards. It is lit while it is on.
- Reverse, the fades and their curves in the header work on the picked clip and are greyed out while none is picked.
- Fade in and out in the header, in milliseconds (Fade in (ms) and Fade out (ms) in the inspector), are the ramps at the two edges inside the file: the stretch rises from silence over the one and falls back to it over the other, so a cut through a wave does not click. They are counted into the stretch, so the stretch stays as long as it is. An edge on the start or the end of the file is left as it is. A fade longer than half the stretch is shortened to half. The dropdown behind each field is the curve of that fade. It shows the curve as a small picture, rising for the fade in and falling for the fade out; its tooltip names it. The ramps are drawn in the wave, each in its curve.
- Play (or Space) plays the picked clip once, else the whole file, at the sound's volume, with a playhead. It ends whatever pattern or song the music tabs were playing, as they end each other. Closing the tab and starting the game end it.
- The wheel zooms about the pointer, the middle button pans, as in the music tabs. Zoomed in far enough, the samples themselves are drawn as a line.
- Left of the wave stands a level scale in dB, a mark every 6 dB (#357). The wheel over the scale zooms the wave's height, from a quarter to 16 times: zoomed out, a full-scale wave leaves room above and below it; zoomed in, quiet stretches get tall enough to see, and what is louder stops at the edge. The fade ramps grow with the wave. Zoomed out, the bright file, the picked clip and its handles shrink with it to the height of a full-scale wave. Fit brings back the whole file at its full height.
- A stereo file shows one wave, the loudest of its channels; it plays in stereo.
- The header's readout says how long the picked clip is, and of how long a file.
The file is never changed. The cut lives on the clip, so a stretch can be made longer again at any time, and two cuts of one file are two clips of one sound.
How it plays. A clip is played by a name of its own,
<file>#<start>~<end>~<fadeIn>~<fadeOut>~<curveIn>~<curveOut>, with ~reverse after it when it
plays backwards. The library fetches and decodes the file once
and makes the stretch from it the first time that name plays: a copy of those samples with the ramps worked in. Length and waveform of a cut are read
from the file itself, without a copy. Copies are made again whenever they are needed, so the
library lets the oldest go once they hold more than 64 Mi samples together — a cut dragged about
in its tab leaves no pile of copies behind. The sound itself, and a clip over the whole file, is no
cut at all and plays the file as it is.
Each clip has a file of its own (#349). A moment after a clip was made or last changed, the editor cuts it from the file, fades and Reverse worked in, encodes that as Opus in Ogg (as a streamed sound is) and uploads it as [stream] Sound - Clip. The sound picker leaves it out. This happens for every clip of every sound, with Stream on or off.
- A game plays the clip from that file. A sound that is only played through its clips is never decoded whole: a long recording with a few short clips in it costs the clips, not the recording.
- The whole file is still decoded where something may play the sound itself: a sound field, a song item or the Sample of an instrument that names the sound and not one of its clips, or a clip whose own file is not there yet. Then the clips are cut from the whole file as before.
- Something that plays the sound itself only while the game runs, by an id the project does not hold, starts late once: the whole file is fetched and decoded then.
- The copy is lossy and at 48 kHz, as Opus always is: 160 kbit/s in stereo, 96 kbit/s in mono. The Sound tab plays and draws the original.
- A changed clip plays from the whole file until its new copy is there; the old one is deleted once the project is saved without it.
Song items keep their place and their length when their clip is cut anew: they play from the new start, and one longer than the new stretch plays it round and round.
Before #357 the cut was set on the sound itself. That is not read anymore: such a sound plays the whole file. The projects were changed over by hand when clips came: every cut sound got a Clip 1 with its cut, and everything that named the sound names the clip now.
The curve of a fade
A fade runs from silence to full. Its curve says how loud it is on the way, at t from 0 (the
silent edge) to 1 (full). A fade out runs its curve backwards, from full to silence.
| Curve | Loudness at t |
Halfway | Sounds like |
|---|---|---|---|
| Linear | t |
0.5 | A straight ramp, the default. |
| Equal power | sin(t · 90°) |
0.71 | Keeps the power even. A fade out of one sound and a fade in of the next laid over each other stay as loud as one. |
| S-curve | (1 - cos(t · 180°)) / 2 |
0.5 | Soft at both ends of the ramp, steep in the middle. |
| Exponential | t² |
0.25 | Stays quiet long near the silent edge. A fade out falls quickly and then lingers, like a sound dying away. |
| Logarithmic | 1 - (1 - t)² |
0.75 | Gets loud quickly. A fade out holds long and drops at the very end. |
Each fade has a curve of its own, so a sound can, say, rise along Linear and die away along Exponential. Sounds saved before #345 had a single Edge fade. It is not read anymore, so they start with 5 ms each and Linear on both.
Playing it backwards
Reverse (#321) turns the stretch of a clip around. The handles keep marking the same part of the file and the wave is drawn as it is. Only the order changes: the end handle is heard first, the start handle last, and the playhead of the tab runs from right to left.
- The fades go with what is heard. Fade in is the ramp at the end handle, because that is where the sound starts now. Fade out is the ramp at the start handle. The wave draws them there. As before, an edge on the start or the end of the file gets no ramp.
- Everything that plays the clip plays it backwards:
Play sound, a song item, the Sample of an instrument, a Pitch in a sound FX. The reversed stretch is made once, like any cut, and the FX work on it as on any other sound. - The length stays.
lengthand the rounds of a song item are as long as forwards. The waveform of a song item is drawn backwards, as it is heard. - A clip over the whole file with Reverse on is a cut of its own, the whole file backwards.
There is no Reverse node in a sound FX. Playing backwards needs the whole sound in advance. A bus only has what is sounding right now, and a Chip has no end until its note decides. The switch on the clip covers the sound files, and two clips of one stretch, one of them reversed, give both directions.
Playing it
| Node | What it does |
|---|---|
| Play sound | Starts the sound named by its sound input. volume multiplies the asset's own volume (1 leaves it alone), loop is a choice of three (From the asset, On, Off), detached says the voice belongs to nobody, fx is the sound FX it plays through (From the asset, the default, takes the one the sound names, None plays it without, see below), and fx params takes values for that FX's parameters. Its output voice is the number of this one play, for changing it while it plays, length how many seconds one run of the sound lasts, and tail that length with the echoes of its FX after it, at the values it starts with: how long until the voice is quiet. All three are 0 where nothing was played. tempo (BPM, 120 where nothing is connected) is what a Delay synced to beats in its FX counts by. |
| Stop sound | Silences every voice of the sound it names, however many plays started it — a loop included. Naming no sound silences everything the run is playing. With voice connected it silences only that one play (the number Play sound gave out), and nothing where that is 0. A voice is faded out in 10 ms rather than cut, so it does not click, its echoes with it; Let ring stops only the sound and lets the echoes fade as they would, and a note of an instrument with an Envelope falls away in its release first. |
| Set sound FX parameters | Hands new values to the parameters of one voice that is already playing, by the voice number its Play sound or Play note gave out, at once or over time seconds. It also takes the playback of a Play pattern or Play song and moves the sound FX on all of it (#346). On a note with a sound FX of its own, a name both its instrument and its FX have changes both. |
| Set bus volume | Sets how loud a bus is, 0 to 1, at once or over time seconds along an easing (#325). An empty bus is the master. |
| Bus volume | How loud a bus is now: what Set bus volume set, else the volume of its asset. 0 for a bus that does not exist. |
| Sound | A constant sound, for either of the two to work on through an edge. |
The division is deliberate: the asset says what a sound is, the node only colours it. A
Play sound with nothing typed into it therefore plays the sound the way the project describes
it, and turning every jump sound down is one edit on one asset rather than a hunt through the
graphs.
Why loop has three answers and not a checkbox. A tick box can only say yes or no, and
one of the two has to double as "nothing said" — so whichever way it is read, one case is lost.
It used to add to the asset's flag, which made music impossible to play one-shot. The choice is
therefore From the asset (the default: the node says nothing and the asset decides), On and
Off, and the last two overrule the asset in their direction. A drum loop can be used as a single
hit, and an effect can be looped, without a second asset for either.
An edge may drive the input instead; a boolean arriving there is taken literally, so a comparison
or a variable wired into loop reads as On / Off.
Every play is a voice of its own. Firing Play sound twice in a row is heard twice,
overlapping — two coins picked up half a second apart sound like two coins. This holds for looping
sounds as well: playing a loop twice gives two loops, and it is Stop sound that ends all of
them at once.
It is loaded ahead. Every sound of the project is fetched and decoded as soon as the project is
open, the way images and fonts are, so the first play starts in the frame its node ran. A sound
that streams has only the start of its copy fetched, and one only played
through its clips has the clips' own files decoded instead of its own. Decoding
ahead opens no audio device: that only happens on the first play. A sound that is still loading
when it is played — a run started the moment the project opened — starts a moment late instead.
Play sound never waits on the network, and a Stop sound that happens while the bytes are still
in flight wins: the sound does not start afterwards on its own. Values a graph sends to such a
voice in the meantime are what it starts with.
A sound belongs to the object making it. A voice is owned by the behavior that started it, and
ends when that behavior is destroyed, echoes and all — a sound is something a thing in the world is doing, not a
thing of its own. Destroy an enemy and the engine it was running stops with it; no Stop sound in
an On destroy needed, and none forgotten. The same rule is what ends the sounds of a scene that
Load scene swaps away: every behavior in it is destroyed. A behavior on an object with
Keep on scene load is not, so its sound plays on through the swap — the plain way to have music
that does not restart with every level.
detached is the way out, and it exists for one shape. An object that plays its own death
sound and destroys itself in the same flow would cut that sound off in the instant it started.
With detached on, the voice is nobody's: it outlives the object and plays to its end (or forever,
if it loops — then only Stop sound or the end of the run silences it). It is off by default,
because the owned case is the one that is almost always meant.
The run owns its sounds. Pausing (Ctrl+Enter) holds every voice where it is and resuming
carries it on mid-note. Stopping the run (Esc) ends every voice, looping music included —
nothing may outlive the world that started it. An editor run does the same, and is restarted by
any edit, so a loop cannot pile up one voice per keystroke.
Time scale does not change the pitch. A game played in slow motion keeps its sounds at normal speed rather than sounding like a tape being slowed — the global clock scales what the world does, not how the device plays back.
Streaming long sounds
A sound is decoded whole before it plays: 48000 samples a second for each side, four bytes each. That is nothing for a jump, but five minutes of music in stereo are about 115 MB, held for as long as the page is open. Stream (#349) plays such a sound from a copy, a few seconds at a time.
- The copy. Switched on, the editor decodes the file once, encodes it as Opus in Ogg (160 kbit/s in stereo, 96 kbit/s in mono, as a bake is) and uploads it as [stream] name. The sound picker leaves it out. The original stays as it is: the Sound tab shows and cuts that one. Switched off, the copy is taken off the sound and deleted once the project is saved without it.
- Each clip streams from the copy of its own cut that every clip has (with Stream or without), so a clip never needs the whole file. A clip that is the whole file plays the sound's copy.
- Until a copy is there, the sound or clip plays decoded, as without Stream. A sound with Stream on is never decoded ahead when the project opens, though: only once it is played without a copy.
- What a game holds is the compressed copy as it is fetched, and its first two seconds decoded, so it starts at once. The rest is decoded about four seconds ahead of what is heard and let go once it has been played.
- Everything after it stays the same: volume, Loop, its sound FX, its bus and the master. A
Play sound, a song item and Stop sound treat it as any other sound. - It starts on its sample, also in a song, and runs round its loop without a gap: the copy is decoded straight on, and where it jumps — a loop, a song item that starts mid-way — the decoder is started 640 ms earlier, after which it gives back what decoding from the start would, to the bit.
- Pitch moves what is streamed with a shifter, as on a bus: the length stays, and it is heard a frame later, 42.7 ms at 48 kHz.
- The Sound tab says long, try Stream for a sound of a minute or more that does not stream, and streamed, copy waiting or copy 40% for one that does.
- What always stays decoded: the Sample of an instrument and the impulse of a Reverb, which read the samples themselves, and the original in the Sound tab while it is edited.
- Browsers. Streaming needs Opus in WebCodecs, which Chrome has. Elsewhere the sound is decoded as before. The copy is made in the browser too, so making one needs Chrome as well.
- Bakes of patterns and songs always stream.
Measured (2026-10-09, e2e 34 on the CI's Windows machine): five minutes of a sine at 8 kHz in mono,
4.8 MB as WAV, 3.6 MB as its copy. Streamed while a game plays it, the renderer grows by 5.8 MB;
with Stream off the same sound decoded grows it by 69.6 MB. Streamed offline at 48 kHz, every
sample is the one the copy decoded whole has there, by the feed's decoder and by the browser's
decodeAudioData alike: from a start at 0.25 s two seconds in, round the seam of its loop, and for
a reversed clip of it round its loop (largest difference 0, no sample late). With Stream off, a
game that plays the sound only through that clip decodes the clip's own copy and not the file: its
ten seconds are the cut made from the original to within 0.0091 a sample, and to 0.013 dB in any
20 ms.
Buses and the master
Every sound ends in the master (#325). Between a sound and the master there can be buses: Music, SFX, UI, whatever a project wants. A bus mixes everything heard on it, so its volume turns all of it up or down together, and its sound FX works on the sum: one compressor, one echo for everything on it.
A bus is an asset (+ → Audio → Bus). Its inspector has two rows:
| Row | Meaning |
|---|---|
| Volume | How loud everything on it is, 0-1. |
| Sound FX | A sound FX all of it goes through as one chain, before the volume. Its parameters start at their defaults. |
Where a bus goes on, and where a sound, an instrument, a pattern or a song is heard, is said in one place: the Master routing.
The master is no asset. Its Volume and Sound FX are in Project settings under Master, so every project has exactly one. A new project has only the master; buses are made as needed.
After the master comes a fixed limiter before the speakers (#359), in the editor and in the game. It lets no sample past -1 dBFS, so a mix that is too loud is squeezed instead of clipping hard. It cannot be set or turned off, and it is no excuse for a mix that is too loud: the level meters of the Song tab show where it is at work. It looks 5 ms ahead, so everything is heard 5 ms later. Playing a song or pattern, the playhead and a recorded take count that in. It needs the FX worklet. Until that is loaded, a moment after the first sound, the master goes straight out.
Master routing
Menu → Master routing opens a tab with one graph per project (#351). It works like the routing of a song, one level up:
- A node per asset. Every sound, instrument, pattern, song, video with sound, composition and bus of the project has one, named after it, and there is one Master. The nodes come and go with the assets: a new asset gets its node in a column for its type, and a deleted one takes its node and its links along. They cannot be added or deleted by hand. What a song keeps to itself (an embedded sound or pattern) has no node: its song routes it.
- A new or duplicated asset goes where the others go (#365). A sound, instrument, pattern, song or video gets the links most others of its type have, at their volume: with two sounds in SFX and Reverb and one on the master, the next one goes into SFX and Reverb. On a tie the latest of them decides, so if there is only one song, the next one follows it. Where nothing of its type is linked yet, it goes as most of all that plays, and where nothing is, to the master. Videos, compositions and the music go by each other apart: a video where no other video is linked goes to the master, not where the sounds go, and a song does not follow the videos. The same holds for a composition. A new bus is always linked to the master.
- Links are sends. A drag from the out of a node into the in of a bus or of the master links them. A node may have several links, the same sound then goes everywhere it is linked at once: a step sound into SFX and, quieter, into a Reverb bus. Only a bus and the master take links in.
- Volume and Mode of a link are in the inspector when it is picked: its level from -60 to +12 dB, 0 by default. A link out of a bus or a song also has a Mode, as on a track: after the volume (the default), after the sound FX but before the volume, or before the sound FX. A link out of a sound, an instrument, a pattern, a video or a composition always takes it after the volume and the sound FX.
- No loops. A link that would lead round to where it started is refused.
- A node without a link is never heard. It says Goes nowhere, and the header counts how many do. A deleted bus leaves what went only into it on the master, so deleting a bus silences nothing.
- A click on a node shows its asset in the inspector, as the asset panel would; on the master,
its volume and sound FX, the same as in Project settings. A double click opens the asset's
tab.
Fitand the magnet in the header work as in every graph tab.
A project from before there was a routing has every asset linked to the master, which is how it always sounded.
Which bus a sound is heard on.
- Where its node in the routing is linked. So a step sound is SFX everywhere it is played, and no node has to say so.
Play sound,Play note,Play patternandPlay songhave an inputbus. Left at From the asset it goes along the links of the asset. Master or a bus picked there overrules it: then it goes into that bus alone.- What lies inside a song or a pattern is heard the way the song or pattern is: the links of a sound placed on a track, or of the instrument of a pattern, do not count there. The order is the song's own (see Songs), then the links of the song, then the buses they go into, then the master.
What it costs. An asset with one link at 0 dB goes straight into its bus, as with no routing at all. Only one with more than one link, or another level, gets a bus of its own that all its voices go through, with the sends on it. Each send is held back as far as the latest way of the asset, so a bus with a Pitch on it does not make one way come later than the other.
In the game. Set bus volume changes a bus while the game runs, Bus volume reads it. The
volume set holds for the rest of the run, the next scene included, over the volume of the asset.
The next Play starts with the volumes of the assets again. An options menu keeps its volume in the
savegame and sets it again at the start. Set sound FX parameters without a voice changes the
sound FX of the bus picked in its bus input instead, the master among them.
Pausing with Pause game does nothing to the buses: sounds play on. A game that wants quiet
in its pause menu sets a bus volume itself, for instance SFX to 0 while Music plays on.
In the editor the tabs leave the Master routing out (#368). The Sound, Sound FX, Instrument, Pattern and Song tab play through the sound FX of their asset and then straight into the master, whatever its links say. The Video tab, the Composition tab and the preview of the Animations tab play a video's sound straight into the master too. So a bus with a reverb on it is not heard in a tab, but a compressor on the master is. Inside a song its own routing still counts. The Sound FX tab plays the sound it tries out through the FX in the tab instead of the sound's own.
A Pitch on a bus or on the master is allowed. It is late by one frame like every Pitch on a bus, and notes of a song are started that much earlier.
Sound FX
A sound FX asset (+ → Sound FX) is what a sound is played through. It opens in a tab of
its own with a node graph, like a shader: an Input (the sound as Play sound starts it), an
Output (what is heard) and effects between them, wired out → in. The palette offers the
Input and the Output once each; their pins carry sound and fit nothing but each other.
The sound FX of an asset
Sounds, instruments, patterns and songs have a row Sound FX (#352). Empty is none. What it does depends on the asset:
| Asset | Goes through its Sound FX |
|---|---|
| Sound | every voice of it: Play sound, an item of a song, the Sound tab. |
| Instrument | every note, after what the instrument graph does, as through a Sound FX node at its Output: Play note, every note of a pattern or song, the keys of its tab and of the Pattern tab. |
| Pattern | all its notes together, as one chain: Play pattern, the Pattern tab, a pattern item of a song. |
| Song | the whole song, after its tracks and the routing, before its links in the Master routing: Play song, the Song tab. |
Play sound, Play note, Play pattern and Play song have an input fx, and an item of a song
a row Sound FX, with the same choice: From the asset (empty, the default) takes the row of the
asset, None plays without one, and a sound FX picked there takes its place. It does not come on
top. The Sound FX of an instrument stays on every note of a pattern played with another FX, since
that FX is one step later.
A sound that is only a source plays without its own: as the Sample of an instrument, as the impulse response of a Reverb, and as what the Sound FX tab tries out. Its row means nothing there.
| Node | What it does |
|---|---|
| Gain | Louder or quieter, in dB (Gain (dB)): 0 leaves it alone, -6 is about half as loud, +6 twice. Above 0 the sound can clip. In the graph the node is drawn as a small device (#384): the Compressor's brushed plate in orange, 160x160 graph units without an engraved name, with one chrome knob between its inputs and its output. The knob works as the Compressor's do: drag up or down, a double-click resets it to 0 dB, each turn one undo step, the wheel zooms the graph. 0 dB stands straight up, and the quiet end below -24 dB takes only a fifth of the turn. A linked Gain (dB) greys the knob. There is no tab and no meter. |
| Pitch | Higher or lower, in semitones (12 is an octave, 0.5 a quarter tone). The sound keeps its length, also when the pitch changes while it plays. The pitch is made where the sound is read, so an effect before the Pitch acts as if it came after it. On a Chip of an instrument it moves the frequency of the Chip itself. Its wet takes the semitones from 0 up to all of them, see below. In the graph the node is drawn as the Gain's device in green (#390), with one knob for Semitones: 0 stands straight up, and an octave up or down is a little under half of either side of the turn, so small steps near 0 are easy to set and the full ±64 is still there. A linked Semitones greys the knob. |
| Parametric EQ | Up to 10 bands, each a filter of its own type, frequency, gain, Q and slope, drawn together as one curve. The node shows its editor right in the graph, and a double-click on the node's title opens the same in a tab. Both show the spectrum of what passes the node. A new node has no band and does nothing. See below. |
| Filter | Type Low-pass (the default) takes away what is above frequency (Hz, 1000 by default), High-pass what is below it, Band-pass all but a band around it and Notch just that band. Low shelf and High shelf raise or lower everything below or above it by gain (dB), Peaking a band around it; only these three have the gain row. All-pass leaves every frequency as loud as it was and only turns its phase. Q (0.707 by default) says how sharp it is: at 0.707 a low- or high-pass has no bump, higher it rings at the frequency, and a band, a notch or a peak gets narrower. Slope makes a low-pass, high-pass, band-pass or notch steeper: 12 dB/oct is one filter, 24 two and 48 four in a row, and only the last of them has the Q. In an instrument an Envelope can move all three numbers while a note plays. The node draws itself as a curve and is edited right in the graph, and a double-click on the node's title opens the same in a tab. See below. |
| Mix | Blends the sounds at a and b: at 0 only a is heard, at 1 only b (0.5 is the default). Curve says what lies between: Crossfade (the default) gives half of each at 0.5, Both at center keeps each full up to the middle. See below. In the graph the node is drawn as a blue device of 180x200 graph units (#390): a lever for Curve on top, down for Crossfade and up, with its lamp lit, for Both at center, and below it one knob for Mix with A and B engraved at the ends of its turn. A flip of the lever is one undo step. A linked Mix greys the knob. |
| Delay | An echo: the sound comes back after time, again and again, each time feedback times as loud. Sync says what time counts: Seconds, or Beats in quarter notes at the tempo of the voice. damping makes every echo duller than the last. wet (0.5) is the sound alone at 0, the sound and its echoes in full at 0.5, the echoes alone at 1. Ping-pong sends the echoes left and right in turn. The node is drawn as a device with its echoes on a screen (#393). See below. |
| Bitcrusher | Makes the sound rough, like an old console. bits rounds it to fewer steps (8 by default), rate takes a sample only so many times a second and holds it until the next (8000 Hz by default), wet is how much of the crushed sound is heard instead of the sound itself (1 by default). The node is drawn as a pink device whose screen draws a sine in the pixels of its bits and its rate (#399). See below. |
| Phaser | Sweeps notches through the sound, the swirl of a phaser pedal. Stages (2, 4, 6, 8 or 12, 4 by default) is how many allpass stages it has, every two make one notch. They sit around frequency (800 Hz by default), and an LFO moves them depth octaves wide (2), rate times a second (0.5 Hz). feedback (0.4) sharpens the notches, wet (1) is how much is heard. The node is drawn as a mustard device whose screen swirls its notches (#398). See below. |
| Flanger | Blends the sound with a copy a few ms late: the jet sound of a flanger, and what sfxr calls its phaser. offset is how late the copy is (1 ms by default), sweep moves that on by so many ms a second from the start of the sound (0), an LFO adds up to depth ms (2) rate times a second (0.25 Hz). feedback (0.5) and wet (1) as on the Phaser. The node is drawn as a steel blue device whose screen draws its comb (#398). See below. |
| Pan | Moves the sound left or right: pan -1 is all left, 1 all right, 0 (the default) leaves it in the middle. A mono sound is spread on both sides and then moved (each side 0.707 at 0). A stereo sound keeps the side it goes to and the other side moves over into it, as Web Audio's StereoPannerNode does: at 1 both sides are heard on the right. In an instrument an Envelope can move it. Measured (e2e 34, Chrome offline): a mono sine at 0.5 came out 0.5 / 0 at -1 and 0.354 / 0.354 at 0, a stereo one at 0.5 came out 0.354 / 0.854. In the graph the node is drawn as the Gain's device in cyan (#392), with one knob for Pan: the middle stands straight up, the turn goes evenly to L and R engraved at its ends. A linked Pan greys the knob. |
| Compressor | Squeezes the loudness together: what goes above threshold (-24 dB by default) comes out only 1/ratio (4) as much louder. knee (6 dB) eases into it, attack (0.003 s) and release (0.25 s) say how fast it turns down and lets go. makeup (0 dB) raises it afterwards. Auto-gain (off) gives back what it takes from a sound at -18 dB, with the makeup on top. The node is drawn as a device with a meter of what it takes away, and a double-click opens a tab with its curve and the level going in and out. See below. |
| Limiter | Keeps every peak below threshold (-6 dB): it looks 5 ms ahead, so even a sudden peak never gets through, and comes back up in release (0.1 s). Auto-gain (on) raises the limited sound to ceiling (-1 dB). Nothing ever goes above the ceiling. The node is drawn as a brick device whose screen holds how much it took away and when (#397). See below. |
| Saturation | Bends the sound, from warmth to a hard distortion. Type: Soft (tanh) (the default), Cubic, Hard clip, Tube, Fuzz, Foldback, Sine fold or Rectify. drive (12 dB) is how hard the sound is pushed into it, tone (1) a low-pass after it from 1 kHz (0) to wide open (1). Auto-gain (on) keeps the bent sound about as loud as it came in, output (0 dB) is added on top. wet (1) how much of it is heard instead of the sound itself. The node is drawn as a copper device whose screen glows hotter the harder it is driven (#396). See below. |
| Tremolo | Makes the volume swing rate times a second (5 Hz by default), from full down by depth percent (50) and back. Shape: Sine (the default), Triangle, Square or Saw. The node is drawn as a cream device with the volume on a screen (#398). See below. |
| Vibrato | Makes the pitch swing rate times a second (5 Hz), depth semitones up and as far down (0.5). Shape as on the Tremolo; a Square jumps between two pitches. The node is drawn as a plum device with the pitch on a screen (#398). See below. |
| Reverb | The sound in a space. Type: Room (the default), Hall, Cave, Plate, Custom or Sound. Custom shows the rows decay (s, 1.5 by default), damping (0.4) and pre-delay (s, 0.01); Sound picks a sound asset as the impulse response of a real space, with pre-delay. wet (0.3) is how much of the reverb is heard instead of the sound itself. The node is drawn as a device with a round screen of its tail (#389). See below. |
Every number of these nodes is an input: typed in while nothing is connected, or fed from a
parameter of the Input. Each is held to its range (Gain -120 to +24 dB, Pitch -64 to +64,
Filter frequency 20 to 20000 Hz, Q 0.1 to 30 and gain -40 to +40 dB, Mix 0 to 1, Delay
time 0.01 to 8 and never more than 2 s, feedback 0 to 0.95, damping 0 to 1,
Bitcrusher bits 1 to 16, rate 100 to 48000 Hz, Phaser frequency 20 to
20000 Hz, rate 0 to 10 Hz, depth 0 to 6 octaves, Flanger offset 0 to 10 ms, sweep -125 to
125 ms/s, rate 0 to 10 Hz, depth 0 to 10 ms, both with feedback 0 to 0.95,
Pan -1 to 1, Compressor threshold -100 to 0 dB, ratio 1 to 20, knee 0 to 40 dB, attack and
release 0 to 1 s, makeup -24 to +24 dB, Limiter threshold -60 to 0 dB, ceiling -30 to 0 dB,
release 0.001 to 2 s, Saturation drive 0 to 48 dB, tone 0 to 1, output -24 to +24 dB, Tremolo rate 0 to 20 Hz and depth 0 to 100 %,
Vibrato rate 0.5 to 20 Hz and depth 0 to 4 semitones, Reverb decay 0.1 to 10 s, damping
0 to 1, pre-delay 0 to 0.5 s, and every wet 0 to 1),
whichever way the value arrives.
Auto-gain of the Compressor, the Limiter and the Saturation is a switch on the node, not an input (#378). Until then it was a number from 0 to 1 that blended between the node's own gain and the auto-gain. A project from before reads such a number as on when it is above 0.
Bypass and Wet
Every effect has a Bypass and a wet (#354), all but the Mix, the Input and the Output.
- Bypass is a setting of the node (a checkbox). A bypassed node is left out when the graph is
compiled, as if it were not there: it costs nothing and holds nothing back. It is drawn faded,
and its body says Bypass: true (nothing while it is off). It cannot be switched while a sound
plays;
wet0 can. wetis an input like the other numbers: typed in, fed from a parameter, moved with Set sound FX parameters while the sound plays, and in an instrument moved by an Envelope where the node lets one move its other numbers (Gain, Filter, Pan, Bitcrusher, Phaser, Flanger). It blends what the node does with the sound as it came in:(1 - wet) * sound + wet * effect. 1 is the default, and awettyped in at 1 costs nothing. Typed in at 0 the node is left out; fed from a parameter it stays, at whatever value.- A node that is late (Compressor, Limiter, Vibrato) has the sound as it came in held back as far, so the two meet in step and nothing is combed out.
- The rows that were called
mixon six nodes are nowwet, under the same key: values, parameters and edges saved before stay as they were. On the Bitcrusher, Saturation and Reverb it means what it did. On the Phaser and Flanger the effect itself is half sound and half copy, since the notches only come from mixing them:wet1 is that, asmix1 was. Their defaults stay: Delay 0.5, Reverb 0.3, the others 1.
Two nodes take it their own way:
wet |
Delay: sound / echoes | Pitch: semitones of a Pitch of 12 |
|---|---|---|
| 0 | 1 / 0 | 0 |
| 0.25 | 1 / 0.5 | 3 |
| 0.5 | 1 / 1 | 6 |
| 0.75 | 0.5 / 1 | 9 |
| 1 | 0 / 1 | 12 |
- The Delay crossfades like the Both at center curve of a Mix: the
sound stays full up to 0.5 and the echoes from 0.5 on. A Delay saved before #354 sounds
different: it had the sound always in full and the echoes at
mix. It sounds the same again at half its old value (mix1 →wet0.5, 0.5 → 0.25). - The Pitch takes the semitones by
wet. An Envelope moves the semitones, not thewet.
Worth knowing: on a Gain wet only makes it louder or quieter (0.5 of -6 dB is about
-2.5 dB). On a Tremolo it acts like a smaller depth. On a Limiter below 1 the peaks of
the sound as it came in pass its limit. A Filter, an EQ or a Vibrato blended with the
sound gives phase effects, a Vibrato a chorus.
Measured (e2e 34, Chrome offline): a steady 0.5 through a Gain of 0 at wet 0.25 came out 0.375.
A Delay of 0.1 s without feedback on it gave sound / sound with echo 0.5 / 0.75 at wet 0.25,
0.25 / 0.75 at 0.75 and 0 / 0.5 at 1. A Compressor at 1:1 blended half and half at 1083 Hz, where
its 6 ms would cancel the sound, came out 0.00 dB, as loud as it went in.
How Pitch keeps the length (#317). A way with a Pitch is played by an AudioWorklet on the
audio thread, a phase vocoder: the sound is cut into frames of about 43 ms (2048 samples at
48 kHz, four overlapping), and the spectrum of each is moved up or down, peak by peak with the
bins around it, so a tone stays one tone (phase locking after Laroche and Dolson). The worklet
reads the sound itself, so it can look ahead and starts with the very first sample, with no
latency, and every way of a Mix stays in step. The pitch is an AudioParam of the worklet, in
semitones: it glides, ramps and eases like a Gain, and the sound never gets longer or shorter. A
looping sound is read on across its seam, so the loop stays clean.
- At exactly 0 a frame is the sound itself, so a Pitch fed by a parameter that sits at 0 plays the sound untouched and costs next to nothing. Coming back to 0 from another pitch, the phases are pulled round to the sound's for about half a second (the pitch wavers by a fifth of a semitone at most) and then it switches to the sound itself, without a dip.
- A change of pitch is heard about 20 ms late, half a frame: a frame takes the pitch at the sample it starts on. A sweep over the whole sound misses the last 20 ms of its way.
- Far from 0 it gets rough, as every phase vocoder: a hard attack is smeared over a frame, everything moved above half the sample rate is dropped, and far down the peaks crowd into the lowest bins until it is a hum. The range is not limited further than the -64 to +64 of the node.
- Cost: a voice with a Pitch other than 0 works out two FFTs per frame and channel while it plays. Measured (2026-10-06): in Node on an i5-7500T, one stereo voice at +5 takes about 53 ms per second (5 % of a core), at 0 about 5 ms. In e2e 34, headless Chrome on the CI's Windows machine rendered 16 stereo voices at +5 for 2 s in 1.3 s, so about 24 such voices fill the audio thread; fewer leave room for the rest of the mix. There is no limit on how many play at once: past that the sound starts to break up.
- Without
AudioWorklet(or if it cannot be loaded) a way with a Pitch plays at its own pitch, and the console says why. The worklet is loaded with the first play that has a Pitch, which waits for it as it waits for its bytes.
Ways and the Mix. An input takes one edge, but an output feeds as many as you like: the Input
can start two ways with effects of their own, and a Mix joins them again. Only a Mix joins
ways, and a node whose sound never reaches the Output is simply not heard. A node on the way with
nothing coming in (a Mix needs both a and b), or a graph without an Output, does not compile:
the node says why, and the tab's header says it too (otherwise it counts the effects on the way).
A Pitch on one way only works: every way has a source of its own, all started at the same moment, a way with a Pitch its own worklet and one without the plain sound. A Pitch behind the Mix goes to both of its sounds. A Mix typed in at exactly 0 or 1 leaves the silent way out, so that one is not even played. A Mix fed by a parameter keeps both ways, because the value may still change while the voice plays.
The curve of a Mix
mix |
Crossfade a / b |
Both at center a / b |
|---|---|---|
| 0 | 1 / 0 | 1 / 0 |
| 0.25 | 0.75 / 0.25 | 1 / 0.5 |
| 0.5 | 0.5 / 0.5 | 1 / 1 |
| 0.75 | 0.25 / 0.75 | 0.5 / 1 |
| 1 | 0 / 1 | 0 / 1 |
Between the rows it runs in a straight line. Both at center is what a DJ mixer calls a sharp crossfader curve: one side only starts to fade once the other is full.
- At 0.5 both sounds are added in full, as two sounds on top of each other. Where both are loud at the same moment that is up to twice as loud (+6 dB) and can clip; a Gain after the Mix takes it down. For a drum kit, where only one side sounds per note, it changes nothing.
- Curve is a setting, not an input: it holds for the whole voice.
mixitself can still be fed by a parameter and moved with Set sound FX parameters. - A glide follows the curve. With Both at center the voice carries
mixitself on the audio thread (aConstantSourceNode), and twoWaveShaperNodes make the shares ofaandbfrom it. A glide from 0 to 1 keepsafull for its first half, rather than ramping both shares straight and dipping to half each at the middle. - Mixes saved before the curve existed are Crossfade and sound as they did.
Delay and the tail of a voice
A Delay (#327) is a delay line whose output goes through a low-pass (the damping), back into
the line at feedback, and out. wet shares out the sound and the echoes (#354): the sound alone
at 0, both in full at 0.5, the echoes alone at 1, see Bypass and Wet.
Defaults: time 0.25 s, feedback 0.4, damping 0.3, wet 0.5, Ping-pong off.
feedbackstops at 0.95: at 1 the echoes would never fade, above it they would grow.dampingis the cutoff of the low-pass, evenly in octaves: 0 is 20 kHz (nothing heard), 1 is 500 Hz. Every round goes through it once more, so each echo is duller than the one before.- Ping-pong takes the sound into the left of the line only (a stereo sound mixed to one) and swaps the sides every round: the first echo is left, the second right. It is a setting of the node, not an input, because it changes how the voice is wired.
- A
wettyped in at 0 leaves the Delay out. Fed from a parameter it stays, at whatever value. - Sync (#332): with Seconds (the default)
timeis seconds. With Beats it is quarter notes at the tempo of the voice: 1 is a quarter, 0.5 an eighth, 0.75 a dotted eighth, 0.333 an eighth triplet. A voice started byPlay soundorPlay notetakes its tempo from their inputtempo(120 BPM where nothing is connected), a note the scheduler plays the tempo of its sequence. It is a number like any other, so it can be fed from a parameter and moved while the voice plays. Either way an echo comes back after 2 s at most, the length of the line: a whole note (4) at 60 BPM would be 4 s and is held to 2. The tempo is taken when the voice starts; a later change of tempo does not reach a voice already playing. - In Chrome every round comes 128 samples (2.7 ms at 48 kHz) later than
time: a feedback loop costs one render quantum each time round (measured in e2e 34: echoes 1 to 4 at 0, 128, 256 and 384 samples late). After ten echoes at 0.3 s that is 27 ms. It is left so, because a browser without it would then hear the echoes early. - The device (#393): in the graph the node is a plate of 680x224 graph units in the
Compressor's brushed aluminium, its inputs on the left and its output on the right.
- The screen (Echoes over time) draws the sound as a white bar at the left and every echo
after it as a bar, one step of
timeapart, with a faint line at each step. A bar is as tall as the echo is loud, evenly in dB down to 60 dB below the sound. Its colour is how much of the highs is left: warm white for the first rounds, rust oncedampinghas taken them. Each round goes through the low-pass once more, so each bar is a little more rust than the last. - With Ping-pong the bars take turns above the middle line (L, the first) and below it (R). Without it every bar stands both ways.
- It draws at most 24 echoes. Where more are heard it says how many more (
+59). - Under the screen it says the step (
EVERY 250 ms, on beatsEVERY 1/8. · 375 ms AT 120 BPM) and when the last echo has faded (ENDS 2 s, the same count as the tail below), above it where the first echo's highs end (HIGHS 6.6 kHz). Atwet0 it saysNO ECHOES. - A light runs over the screen in real time, from the sound to the end and round again, so it passes the bars at the very beat the echoes come at. A bar glows as it is passed, and the green Echo lamp beside the name blinks with every echo, not with the sound. The node keeps the editor drawing for that while it is in the picture.
- On beats the screen counts at 120 BPM: the tempo of the voice is not known in the graph. Everything else is drawn from the settings, nothing is listened to while the sound plays.
- The screen (Echoes over time) draws the sound as a white bar at the left and every echo
after it as a bar, one step of
- On the plate: two levers, Sync (down seconds, up beats) and Ping-pong (down off,
up L · R), each one undo step, and four chrome knobs,
Timeunder Time,FeedbackandDampingunder Echoes,Wetunder Mix.- The knobs work as the Compressor's do: drag up or down, a double-click resets one, each turn one undo step, the wheel zooms the graph. A wired number locks its knob.
Timegives its short end most of the turn: a quarter note, or a second, in the middle, a quarter second at a third. On beats its number is the note (1/4,1/8.dotted,1/8Ttriplet), else its beats. In seconds anything from 2 s up reads2 s max, the length of the line.- At
wet0 the other knobs and the levers are drawn faint: they do nothing then. They still turn.
A voice lasts until its echoes have faded (#324). Without an echo a voice ends with its sound,
as it always did. With one, it rings on for its tail: until the last echo is 60 dB below the
sound, worked out from time, feedback and wet (echo k is echo * feedback^(k-1) as loud,
echo being its share of wet;
damping only makes it quieter, so it is left out and the tail is never too short). Echoes behind
each other add up, and the two ways of a Mix take the longer. A time of 0.3 s with feedback
0.5 and wet 0.5 is ten echoes, 3 s. While it rings the voice still counts as playing and is
still reached by Set sound FX parameters; a change there counts the tail again from where the
change ends. The end is timed on the clock of the sound, so a paused run holds a tail where it is.
Stop soundfades the whole voice out in 10 ms, echoes included. Let ring fades only the sound (10 ms as well) and the voice stays until its echoes are gone; aStop soundwithout it afterwards cuts them short. A voice without echoes is simply faded out either way.- Destroying the behavior that owns a voice, and the end of a run, cut it as
Stop sounddoes, echoes included. - A loop with an echo never ends by itself: it rings on only once it is stopped with Let ring.
- Cost: a Delay is a handful of Web Audio's own nodes per voice, no worklet. In e2e 34 headless Chrome on the CI's Windows machine rendered 64 stereo voices with a ping-pong Delay for 2 s in about 0.3 s, so about 400 of them play at once (2026-10-06). There is no limit on how many voices play, also now that they live longer. Only the ways on the pitch worklet are held to 12, see Instruments.
- What has ended is let go. A voice without echoes leaves its bus as soon as its sound and what its stages hold back are through, one with echoes once they have faded. A way or stage on the worklet closes its port when it ends or is stopped. Before that a port that was listened to was never collected, and through it neither was its voice: every node of it stayed on its bus and was worked on in every block. Measured (2026-10-08, a copy of a project, a song of 4 tracks played for 12 s and stopped, Chrome on stein2): 740 nodes were left behind each time, 4469 after six times, and the load of the audio thread at rest grew from 1 % to 6 %. Since then 7 are left, the master and one bus, however often it is played.
Filter
A Filter (#335, #318) is Web Audio's BiquadFilterNode, every type it has.
Qis a factor for every type, 0.1 to 30. Web Audio takes the Q of a low- or high-pass in dB, the game hands it on as20 * log10(Q), so 0.707 is a flat -3 dB there. A shelf has no Q in Web Audio, the row does nothing for it.- Slope puts 2 or 4 filters of the same type in a row. They share one
frequency, carried by one source into all of them, so a sweep moves them together. Only the last hasQ, the others sit at 0.707, so a resonance is as high at 48 dB/oct as at 12. Shelves, peaks and the all-pass have no Slope row: in a row they would only add up their gain or their phase. frequencymoves in octaves,Qin its ratio (in dB for a low- or high-pass),gainin dB. All three move with Set sound FX parameters, and an Envelope can move them in an instrument.- A Filter before #318 had Resonance from 0 to 1 instead of
Q. No project used it. The Chip Presets keep their sound: their resonance is now the Q it gave before. - Measured (2026-10-07, e2e 34, Chrome offline): a 2 kHz sine through a low-pass at 1 kHz with Q 0.707 came out 12.37 dB quieter at 12 dB/oct and 49.5 dB quieter at 48 dB/oct. A peak of +6 dB at 1 kHz raised a 1 kHz sine by 6.00 dB, a notch there took it down by 124 dB.
- The editor (#385) is the curve of the Parametric EQ with the Filter as its one
band, the all-pass drawn flat. The node shows it between its inputs and its output, 560 x 280
until it is resized, and a double-click on its title or its edge opens the
same in a tab of its own (
Filter,Filter 2… as for the EQ). Dragging the handle setsfrequencyand, for a shelf or a peak,gain, one undo step for both. Below the curve are Type,Hz,dB,Qand Slope. A number fed by a link is greyed out and the handle leaves it alone. The mouse wheel turnsQover the handle and zooms the dB elsewhere, in the tab only, as for the EQ. Delete in the Filter's tab does nothing: there is no band to take, and the node stays. - Spectrum: as for the EQ, while the node or its tab is seen, the spectrum of what passes the Filter is drawn behind its curve, faint as it comes in and strong as it goes on. In an instrument every voice passing the Filter feeds it.
Parametric EQ
A Parametric EQ (#367) is up to 10 filters in a row, one per band. Its node shows the editor right in the graph (#381), and a double-click on the node opens the same editor in a tab, in a sound FX and in an instrument alike.
-
The editor is a tab of its own (#370), named after its asset, with
EQ 2,EQ 3… when the asset has more than one. Every Parametric EQ gets its own tab, and a second double-click brings the open one up. The tab closes with its node, its asset, or when another project is loaded. Play is in the sound FX's tab, not in the EQ's: float one of the two (see The tab row in Concepts) to have both in view. -
Delete in the EQ's tab removes the picked band, never the node.
-
The curve takes the room of the tab and shows 20 Hz to 20 kHz in octaves and, unless zoomed, -24 to +24 dB. The thick line is all bands together, each band has a thin line in its own colour and a numbered handle.
-
Zoom (#376): the mouse wheel on free space zooms the dB the curve shows, always the same either side of 0: ±3, ±6, ±12, ±24 or ±48 dB. Up zooms in. The lines across the curve follow, every 1, 2, 3, 6 or 12 dB. A band whose gain is beyond the range keeps its handle on the edge, where it can still be grabbed. The zoom is saved on the node (
range), so it is there again after loading, and it is no undo step: Ctrl+Z undoes edits, not a look at the curve. The spectrum keeps its own scale. -
In the node (#381) the editor sits between the inputs on the left and the output on the right, 720 x 360 until it is resized, the curve and the band's numbers. The line of help is left out there (#385). It is worked there as in the tab, and each edit is one undo step there as well. Three things differ:
- The mouse wheel zooms the graph wherever it turns, over a handle too (#383). Q and the dB zoom are set in the tab, and the node shows the dB the tab is zoomed to.
- A press on the curve neither picks nor moves the node, a press on its title or its edge does.
- A double-click on the curve adds a band. The tab opens with a double-click on the title or the edge.
It replaces the small curve below the rows of #375.
-
The spectrum (#374): while the EQ's tab is seen, in the tab row or in a window, or its node is in the picture of its graph, it draws behind the curve what passes the node right now. Faint and grey is the sound as it comes in, in the colour of a sound port as it goes on, so you see what the EQ takes away. It has its own scale: 0 dBFS at the top of the picture, -96 dBFS at its foot. Every sound passing the node counts: the sound FX's Play, an instrument's keys, a song, a run in the editor and the game. Each side is one analysis of 8192 points, about 6 Hz apart at 48 kHz, smoothed by the browser's analyser, so it falls away softly once the sound has gone. While neither the tab nor the node is seen, no analyser is fed.
-
With the mouse: a double-click on free space adds a peak there. Dragging a handle moves the band's frequency and, for a peak or a shelf, its gain. The mouse wheel over a handle makes the band a sixth of an octave narrower or wider, anywhere else it zooms (see above). A right-click on a handle removes the band.
-
Below the curve are the numbers of the picked band: Type,
Hz,dB,Qand Slope. A row the type does nothing with is greyed out. How much of it is drawn depends on the room, see Little room. -
Types: Peaking, Low shelf, High shelf, Low cut (a high-pass), High cut (a low-pass), Band-pass and Notch. Slope (12, 24 or 48 dB/oct) is there for the cuts, the band-pass and the notch, as on the Filter. A shelf has no Q.
-
Ranges: frequency 20 to 20000 Hz, gain -24 to +24 dB, Q 0.1 to 30.
-
One undo step for each drag, each scrub of a field, each wheel notch on a handle, each added or removed band.
-
What is heard: each band is one
BiquadFilterNodeper voice, or 2 or 4 for a steeper slope. A peak or a shelf at 0 dB is left out. The curve is worked out with the formulas Web Audio uses, at 48 kHz. In front of the bands and behind them every voice and every bus has a tap, a gain of 1 that does nothing to the sound. Only while the tab is seen do the taps of that node feed its analysers. A bypassed EQ keeps its taps. -
The bands are saved on the node as a list (
bands), the zoom as a number (range). They have no input, so a parameter of the Input or an Envelope cannot move them. Bypass andwetwork as on every effect.
Resizing a node
A Parametric EQ and a Filter node (#385) have a corner at their lower right, drawn like the one of a
floating window. Dragging it makes the editor in the node larger or smaller, down to 240 x 120,
with no upper limit. With the magnet on, the corner lands on the grid. Each drag is one undo step.
The size is saved on the node (size, the editor's own, without the ports beside it). A node that
was never resized keeps its full size: 720 x 360 for the EQ, 560 x 280 for the Filter.
Little room
The editors of the EQ and the Filter leave things out rather than cutting them (#385), in the node, in the tab and in a floating window alike:
- The row of numbers comes in only once the curve keeps 120 pixels of height above it. Smaller, only the curve is drawn.
- The help line of the tab comes in only once the row has come in as well.
- In the row, a group that does not fit is not drawn at all, the one on the right first: the
band count, then Slope,
Q,dB,Hz, Type. Nothing is cut in half or scrolled. - The labels of the curve: a frequency or a dB label that would run into the one before it is left out.
Compressor and Limiter
Both (#326) work on the whole sound passing them: on a voice that is one sound, on a bus everything playing through it.
-
Compressor is Web Audio's
DynamicsCompressorNode. Its knee is a curve the browser draws; the game takes Chromium's, so the numbers below are worked out the way Chrome compresses. -
The node adds a gain of its own: 0.6 of what it takes off a sound at full scale (the spec says so). The game takes that back out, so with
makeup0 and auto-gain off a compressed sound really is quieter. After the node the sound getsmakeup + AdB with auto-gain on,makeupwith it off.Ais what the curve takes off a sound at -18 dB, about where a mixed sound sits on average, so that one comes out as loud as it went in. Below the thresholdAis 0. Until #378Awas taken at 0 dB. With a low threshold that lifted everything far too much (+28.1 dB at -40 dB, 4:1, knee 6, against +14.6 dB now). -
Limiter runs on the FX worklet. It holds the sound back 5 ms, finds the gain every sample in that window needs to stay below the lower of
thresholdandceiling, and averages it over the window, so the gain is down smoothly by the time a peak is heard. Then it comes back up inrelease. Both sides of a stereo sound are turned down together, by the louder one. Auto-gain on raises what comes out byceiling - thresholddB, so the peaks sit on the ceiling. Off, they sit on the threshold. -
Both are late: the Compressor by the spec's look-ahead of 6 ms, the Limiter by 5 ms (240 samples at 48 kHz, 221 at 44.1 kHz). This is made up exactly, as for a Pitch on a bus: a voice starts as much earlier, the other way of a Mix and the other layers of a note are held back as far, and a bus counts it into how late it is. A voice with a Limiter is let go only once what the Limiter still holds is out.
-
Every number moves with Set sound FX parameters: dB values evenly, times and the ratio in their ratio.
-
The Compressor's tab (#378): a double-click on the node opens it, one tab per node, named and closed like a Parametric EQ's. It has three parts:
- Knobs, one for each of the six numbers. Drag up or down to turn one: 200 px is the whole turn, with Shift a tenth of that. A notch of the wheel turns it by a fiftieth. A double-click sets the default, and a click on the number below lets you type one. Attack, release and ratio give most of the turn to the low end, where they are used. A knob whose input is linked is greyed: the link decides its value.
- Auto-gain is a switch at the right of the knobs. Below it stands what it gives, for
example
+14.6 dB, oroff. A click is one undo step. - The curve, level in across and level out up, both -60 to 0 dB, before the makeup and
auto-gain. Two points are dragged: the one where it starts to bend sets
threshold, the one at its end sets theratiothat ends there. A dot on the curve is the level coming in now. With auto-gain on, a line at -18 dB in leads from the curve up to the diagonal: that is how far auto-gain lifts the sound. - The levels of the last 4 s, newest at the right, +6 to -60 dB: grey what comes in, in the colour of a sound port what goes out. Where the compressor takes more than 0.5 dB away, the stretch below the level coming in is tinted by as much as it takes. The makeup and auto-gain are left out of that, so a raised sound still shows what was squeezed. Every reading keeps the gain of its time, so changing the makeup does not repaint the past. A level is the loudest sample of the last 25 ms. The line across is the threshold.
Each drag, notch or typed number is one undo step. The levels come from the same taps as the EQ's spectrum: a gain of 1 in front of the node and behind it, fed into an analyser only while the tab is seen. An EQ's tab and a Compressor's can be listened to at once.
The knobs stay on the bottom line, centred. The levels fill the height above them. The curve is a square at their right, centred up and down. It grows with the tab from 180 to 360 px, but takes no more than 40 % of its width. A floated Compressor's window cannot be made smaller than what shows every knob and the smallest pictures (560x414 px in the test browser).
-
The node is the device (#379, #380): in the graph a Compressor is drawn as a brushed aluminium plate, 820x280 graph units, with its inputs on the left of it and its output on the right. The node is that large on purpose: the graph's zoom makes it smaller. Its settings and numbers are not listed as rows, the panel shows them. All of the panel is drawn by the editor, there are no pictures.
- Chrome knobs: threshold large on the left, makeup large on the right, ratio and knee small
beside the meter on the left, attack and release on its right. They turn and reset as the
tab's knobs do, each one undo step, and the orange arc is the value. The number below a knob
is a plain label, centred right under its scale, the ratio as
4:1(#382). Typing goes in the tab and in the inspector. A press on a knob or the lever neither picks nor moves the node. The mouse wheel over a knob zooms the graph and leaves the value alone (#383). - The meter in the middle shows what the Compressor takes away, 0 dB at the right to 24 dB at the left, red from 10 dB on. The scale runs by the square root, so the first dB is about as wide as the last nine. The needle moves like a VU's: most of the way to a reading in 300 ms. The LCD above shows the asset's name and the same reading as a number.
- Auto-gain is a lever with a lamp at the top right. Beside it stands what it gives.
- The LEDs at the right edge show the level going out, one every 3 dB from -39 to 0 dB: the top two red, the next three amber. They light at once and go out by 20 dB a second.
- The meter and the LEDs run while the node is in the picture of a sound FX or instrument graph that is seen, from the same taps as the tab. Out of the picture, folded or with the graph hidden, the node lets go of them. A tab that is open but not seen lets go of them once, so the node keeps hearing.
- The pictures of the tab, curve and levels, are not on the panel. A double-click on the node opens the tab for them.
- Chrome knobs: threshold large on the left, makeup large on the right, ratio and knee small
beside the meter on the left, attack and release on its right. They turn and reset as the
tab's knobs do, each one undo step, and the orange arc is the value. The number below a knob
is a plain label, centred right under its scale, the ratio as
-
The Limiter's node is a device as well (#397): a plate of brushed brick, 720x280 graph units, its inputs on the left and its output on the right. It has no tab.
- Chrome knobs: threshold large, ceiling and release small beside it. They turn and reset
as the Compressor's do, each one undo step. A knob whose input is linked is greyed. At wet 0
the knobs and the lever are pale and the screen says
DRY. - Auto-gain is a lever at the top right. Beside it stands what it gives,
ceiling - threshold, for example+5 dB, oroff. - The lamp
LIMITbeside the name lights red while the Limiter takes something away. - The screen catches what the Limiter takes away from the moment a sound starts: the reduction hangs down from the top, 0 to 24 dB by the square root like the Compressor's meter, with marks at 1, 3, 6, 12 and 24 dB. Along the bottom runs the level coming in, -60 to 0 dB, orange above the line of the limit. Time runs across from the start of the sound. The screen shows 1 s and grows to 2, 4 and 8 s as the sound goes on. A longer sound rolls, and the last 8 s of it are kept. The times stay counted from its start.
- It is held: once nothing has come in for 0.4 s, the capture stands still, the silence
after the sound left off, until the next sound starts a new one. Above the screen stands
WAITING FOR SOUND,LIVEorHELD. Under it the most taken away is written with when, for exampleMOST -11.9 dB AT 0 ms, and the time counts from the first reading that is heard. On the right standsNOW -3.9 dBwhile live andLASTED 1.47 sonce held. A white line and dot mark the most. A capture is also held when the node leaves the picture. - How it is read: from the same taps as the Compressor's, a gain of 1 in front of the node
and behind it. Each frame reads the loudest sample since the last frame, at least 25 ms, at
most 150 ms. The side in front is read 5 ms earlier, as late as the Limiter is, so a peak
going in is met by what it became. What is taken away is the level in less the level out,
without the auto-gain. Below 1, Wet blends the limited sound with what came in, and the
reading is solved for the limited part:
out / in = wet * gain * auto + (1 - wet). Less than 0.2 dB counts as none. - Measured (2026-10-10, live, stein2): the seeded blip at -4.4 dBFS through a threshold of -12 dB showed 7.56 dB taken away. A sound of three hits at 0, -8 and -2 dBFS showed 11.9 dB at the first and lasted 1.47 s on the screen (the file is 1.5 s, the first hit 50 ms in). In e2e 34 (test Chrome) -10.44 dBFS in against a limit of -20 dB showed 9.56 dB.
- Chrome knobs: threshold large, ceiling and release small beside it. They turn and reset
as the Compressor's do, each one undo step. A knob whose input is linked is greyed. At wet 0
the knobs and the lever are pale and the screen says
-
Measured (2026-10-07, e2e 34, Chrome offline): an impulse came out of the Compressor 288 samples late and out of the Limiter 240. A 1 kHz sine at -18 dB through a hard-knee Compressor at -24 dB and 4:1 came out at -17.92 dB with auto-gain on and at -22.42 dB with it off (the curve says -22.5; measured 2026-10-09, #378). A sine at 2 through a Limiter at -6 dB peaked at exactly -6.00 dB, with auto-gain on at -1.00 dB. Chrome's own gain far below the threshold was measured at four settings: 1.524 at -24 dB, knee 30, 12:1; 1.862 at -12 dB, knee 0, 4:1; 1.675 at -20 dB, knee 10, 2:1; 1.000 at 0 dB. The curve gives all four to three places (
tests/core/dynamics.test.ts).
Saturation
A Saturation (#323) is a WaveShaperNode with a fixed curve per type, oversampled 4x against
aliasing.
- The types: Soft is
tanh, Cubic1.5 * (x - x³/3)up to full scale, both round the peaks off. Hard clip cuts them at ±1. Tube istanhshifted, so one half bends harder than the other (even harmonics). Fuzz is harder still and lopsided. Foldback mirrors what goes past full scale back in, again and again, Sine fold does that smoothly. Rectify turns the lower half up, which sounds an octave higher. A 10 Hz high-pass after the curve takes out the DC the lopsided types make. driveis a gain in front of the curve, not a curve of its own: the curve spans 256 times full scale and the sound is turned down as far before it. So a drive glides like any gain.outputand auto-gain: the bent sound getsoutput + AdB with auto-gain on,outputwith it off.Ais the gain that brings a sine at -12 dBFS, driven and bent, back to the RMS it came in with.toneis a low-pass after the curve, from 1 kHz at 0 to 20 kHz at 1, evenly in octaves.wetblends the bent sound with the sound itself. The oversampling makes the bent sound 192 samples late (Chrome, at 48 and at 44.1 kHz alike), so the sound itself is held back as far and the two meet without a comb. The node as a whole is late by that much, which a voice, a Mix and a bus make up for as for a Compressor. Awettyped in at 0 leaves the node out.- The device (#396): in the graph the node is a plate of 720x280 graph units in brushed
copper, its inputs on the left and its output on the right.
- The screen (A sine at -12 dBFS) draws a sine at the level auto-gain is reckoned at as a
grey ghost, and over it the wave that comes out: driven into the curve of the type, its DC taken
out, auto-gain and
outputafter it, blended with the sine bywet. So it shows the shape and the level. The edges are -6 dBFS, a louder wave is cut off there.toneis left out of the picture, it would only round the edges. - Under the screen it says what auto-gain gives (
AUTO-GAIN -10.2 dB,AUTO-GAIN OFF, atwet0DRY) and the loudest point out (PEAK -12.6 dBFS). - The heat:
driveover its 48 dB, as far aswetlets it be heard. The colour of the wave goes from cold steel through ember red, orange and yellow to white, above the screen it saysCOLD,WARM,HOT,GLOWINGorWHITE HOT. A glow rises from the bottom of the screen and flickers, and from about 8 dB of drive on embers fly up, more and faster the hotter it runs (24 at white heat). The Heat lamp beside the name glows with it. The node keeps the editor drawing for that while there is heat and it is in the picture; cold it draws nothing. - Everything is drawn from the settings, nothing is listened to while the sound plays.
- The screen (A sine at -12 dBFS) draws a sine at the level auto-gain is reckoned at as a
grey ghost, and over it the wave that comes out: driven into the curve of the type, its DC taken
out, auto-gain and
- On the plate: a button for each type (Soft, Cubic, Hard, Tube, Fuzz, Foldback,
Sine, Rectify), the Auto-gain lever,
Driveas a big chrome knob beside the screen andTone,OutputandWetsmall. A type, the lever and each turn are one undo step. The knobs work as the Compressor's do, a wired number locks its knob.Tonereads as the cutoff (20 kHz),Outputhas 0 straight up. Atwet0 everything else is drawn faint. - Measured (2026-10-07, e2e 34, Chrome offline): an impulse came out 192 samples late at 48 and at 44.1 kHz. Half bent and half dry at 1125 Hz, where 192 samples off would cancel, the sine came out at 0.00 dB. A sine at -12 dBFS driven 24 dB into Soft came out at 0.00 dB with auto-gain on.
Tremolo and Vibrato
Both (#320) run on the FX worklet with an LFO that is at phase 0 on the first sample of its
voice, so every sound starts its swing the same way. On a bus the LFO starts when the bus gets its
FX. A depth typed in at 0 leaves the node out.
- The shapes, over one period: Sine rises from 0, Triangle too, Square is up for the first half and down for the second, Saw rises from 0 to the top, drops to the bottom and rises again.
- Tremolo: the volume is
1 - depth * (1 - LFO) / 2, so full at the top of the LFO and1 - depthat its bottom. A sine starts halfway down. - Vibrato moves the pitch through a short delay whose time swings: a delay getting shorter is
read faster, so higher. That works on a bus too, where there is no source to speed up, and the
sound keeps its length. The delay is the LFO summed up, so its slope is the LFO and the pitch
follows the shape: a Square is a trill between two pitches. At the top the sound is read
2^(depth/12)times as fast, at the bottom as much slower as that is faster. - The delay swings around a center, as far as it swings out at the
rateanddepththe voice starts with: about 1 ms at 5 Hz and half a semitone, 83 ms for a sine and 130 ms for a square at 0.5 Hz and 4 semitones. That center is how late the Vibrato is, and a voice, a Mix and a bus make up for it as for a Compressor. A sound started now in a game is heard that much later. rate(in its ratio) anddepthmove with Set sound FX parameters. Turning the depth up or the rate down after the start asks for a delay below 0 at the low end of the swing: there it stays at 0, and the lower half of the swing flattens.- The devices (#398): in the graph a Tremolo is a plate of 640x280 graph units in the cream of
an old amp's front, a Vibrato one of the same size in plum, their inputs on the left and their
output on the right.
- The screen draws the first seconds of a sound, two swings at least: 1, 2, 4 or 8 s. The
Tremolo's (Volume over time) is a wave the volume shapes, the volume as its outline,
wetblended in. Above itDOWN TO 50%, under it the period (EVERY 200 ms) and how deep it dips (DIPS -6 dB). The Vibrato's (Pitch over time) is the pitch the sound is read at, in semitones, cool below and warm above, on a scale of 0.5, 1, 2, 4 or 8 semitones either way. Under awetbelow 1 the pitch the sound came at stands beside it, the brighter the lesswet. Above itUP 0.5 st DOWN 0.51 st, under it the period and how late the voice is (LATE 0.93 ms). - A light plays it in real time, over and over, with a dot where the volume or the pitch is. The Rate lamp beside the name goes from dark at the bottom of the swing to lit at its top. When nothing swings nothing moves, and the node lets the editor rest.
- Everything is drawn from the settings, nothing is listened to. The pictures use the LFO the worklet runs: the unit tests hold them against its samples.
- The screen draws the first seconds of a sound, two swings at least: 1, 2, 4 or 8 s. The
Tremolo's (Volume over time) is a wave the volume shapes, the volume as its outline,
- On the plate: a button for each shape,
RateandDepthas big chrome knobs,Wetsmall. A shape and each turn are one undo step, a wired number locks its knob.Rategives its slow end more of the turn (5 Hz in the middle of a Tremolo's). Atwet0 everything else is drawn faint, atdepth0 everything but the depth. - Measured (2026-10-07, e2e 34, Chrome offline): a Tremolo square at 10 Hz, depth 100 %, on a steady 0.5 for a voice starting at 0.1 s was silent before it, then 0.5 for 50 ms and 0 for 50 ms. A Vibrato square at 5 Hz and one semitone on a 1 kHz sine read 1059.5 Hz in the first half of a period and 940.5 Hz in the second.
Reverb
A Reverb (#328) is a ConvolverNode: the sound folded with an impulse response, the way a
space answers a click. It works on a voice and on a bus; a shared one on a bus is far cheaper than
one on every voice (see Buses and the master, and #351 for sending only a
share of a sound there).
-
Type picks the space:
Type decaydampingpre-delayRoom 0.6 s 0.5 5 ms Hall 2.5 s 0.35 20 ms Cave 5 s 0.15 40 ms Plate 1.6 s 0.1 0 Custom shows the three rows to set them yourself, Sound the pick of a sound asset and
pre-delay. The rows are only there for those types (they are ports of their own, so an edge into one that goes away is a ghost port). -
The made impulse response is noise, its own on either side, that has fallen by 60 dB after
decayseconds (up to 10). A low-pass closes over that time, from 20 kHz towards 500 Hz as far asdampingsays, so the highs of the tail die first. It is made once for its numbers and kept. -
A sound as the impulse response is loaded with the voice, or before a bus gets its FX. Web Audio normalizes it, so a loud recording is not louder than a quiet one. A sound that cannot be loaded leaves the reverb silent and the sound dry.
-
wet:(1 - wet)of the sound itself,wetof the reverb. A bus that only carries reverb wants 1. Awettyped in at 0 leaves the node out. -
Only
wetmoves while the sound plays.decay,dampingandpre-delayare set when a voice starts, on a bus when it gets its FX: a new impulse response would click. -
The voice lasts until the reverb has faded: its pre-delay and its decay, or as long as its sound.
-
Cost: every voice with a Reverb folds its sound with the whole impulse response. Many short sounds at once, each with a long decay, are better sent through one bus.
-
The device (#389): in the graph the node is a plate of 640x280 graph units in pale lavender, its inputs on the left and its output on the right.
- The round screen draws the tail as rings out from the sound, the white dot in the middle. How far out a ring sits is its time, as the root of its seconds, the same scale for every Reverb with faint rings at 0.1, 0.5, 1, 2, 5 and 10 s. So a Room is a small disc and a Cave fills two thirds. The dark gap round the dot is the pre-delay, the dashed ring is where the tail has fallen by 60 dB.
- A ring is as bright as the tail is loud there, evenly in dB. Its colour is how much of the
highs is left: ice while they are there, violet once
dampinghas closed them towards 500 Hz. Dots scattered over it stand for the reflections and grow thicker further out. - The lower
wetis, the fainter the tail and the brighter the dot. - Under the screen it says the pre-delay and when the tail has died (
ENDS 2.52 s), above it where the highs end up (HIGHS 5.5 kHz). - A Sound draws the peaks of its sound as the rings, each against its loudest. The screen knows
nothing of the highs there, so the rings are a neutral blue. It says
LOADINGuntil the sound is there, andNO SOUNDwithout one. - It moves (#401), only for the look of it. The sound rings out again and again in real
time: the dot flashes, a thin front runs out over the rings as fast as the tail does and fades
as it falls, then a short quiet of 0.8 s. The reflections it passes light up for a moment.
The reflections drift slowly round the dot, each at its own pace, sway a little and twinkle,
but stay at the time they come. With
wetat 0 nothing moves.
-
On the plate: a button for each type, the sound for Sound and four chrome knobs,
Decay,DampingandPre-delayunder Space,Wetunder Mix.- The knobs work as the Compressor's do: drag up or down, a double-click resets one, each turn one undo step, the wheel zooms the graph. The decay gives its first seconds most of the turn, the pre-delay its first tens of milliseconds.
- A knob the type does not take is greyed but still turns: it goes over to Custom with the numbers the type had and the one turned, as one undo step. So turning the damping of a Hall gives a Hall with that damping.
- Picking a sound from the list under the buttons makes the type Sound, also one undo step.
- A linked input greys its knob.
-
Measured (2026-10-07, e2e 34, Chrome offline): an impulse through a Custom reverb of 1 s with 50 ms pre-delay was silent up to 50 ms after it, and fell by 35.9 dB from 0.2-0.3 s to 0.8-0.9 s after it (36 dB to be expected). At
wet0.5 the impulse itself came through at half. With a sound as the impulse response that has one sample at 0.2 s, the impulse came back exactly 0.25 s later and nowhere else.
Bitcrusher
A Bitcrusher (#322) runs on the same worklet as Pitch, sample by sample:
rateis sample-and-hold: a sample is takenratetimes a second and held until the next. 48000 Hz leaves the sound as it is, 8000 Hz is dull and metallic, a few hundred Hz a buzz of its own. It never takes samples more often than the audio device makes them.bitsrounds what is held to2^(bits-1)steps either side of 0: 16 is hardly heard, 8 crackles, 4 and below hiss and buzz, 1 leaves only up, middle and down. Bits need not be whole, 3.5 lies between 3 and 4, so a ramp on them never jumps.wetblends: at 1 only the crushed sound is heard, at 0.5 half of each. Awettyped in at 0 leaves the Bitcrusher out. Fed from a parameter it stays, at whatever value.- All three are numbers of the worklet, so Set sound FX parameters moves them while the sound
plays (
ratein octaves, the other two in what they hold), and in an instrument an Envelope can move them while a note plays. - The device (#399): in the graph a Bitcrusher is a plate of 680x220 graph units in bubblegum
pink, its inputs on the left and its output on the right.
BitsandRateare big knobs,Weta small one.Rategives its low end more of the turn (8 kHz just past the middle). Each turn is one undo step, a wired number locks its knob, and atwet0 the other two are drawn faint.- The screen sends a 440 Hz sine at 0 dBFS through the node's own crush at 48 kHz, so the
steps and holds drawn are the ones heard. What comes out is drawn in cells: each as tall
as a step of the bits and as wide as a hold of the rate, so the coarser the crush, the coarser
the picture. Where a step or a hold is 5 pixels or more, a grid of them lies behind, and the
cells get a gap round them. Where the steps are finer, a hold is a thin bar on its level. A
cell is never more than twice as tall as it is wide, so 1 bit gives bars at -1, 0 and 1. The
sine going in is a grey ghost behind it. Below 1
wetthe line that is heard is drawn over the cells. - The screen shows two periods of the sine (4.5 ms), or 6 holds where the rate is so low that fewer would show (20 ms at 300 Hz). Its span is written in the corner.
- Above the screen how crushed it is, from
CLEANoverGRITTY,CRUNCHYandCRUSHEDtoBROKEN: whichever of the bits (16 to 1) or the rate (48 kHz to 100 Hz, in octaves) takes more of the sound, timeswet. Under itNOISE -50 dB(6 dB a bit, for a sine at full scale) andFOLDS AT 4 kHz(half the rate: above it the highs fold back down). Atwet0 it saysDRY. - Bits kept: 16 lamps, the highest bit on the left, lit for each bit that is kept and partly for a fraction. The Crush lamp beside the name glows the more it crushes.
- The screen sends a 440 Hz sine at 0 dBFS through the node's own crush at 48 kHz, so the
steps and holds drawn are the ones heard. What comes out is drawn in cells: each as tall
as a step of the bits and as wide as a hold of the rate, so the coarser the crush, the coarser
the picture. Where a step or a hold is 5 pixels or more, a grid of them lies behind, and the
cells get a gap round them. Where the steps are finer, a hold is a thin bar on its level. A
cell is never more than twice as tall as it is wide, so 1 bit gives bars at -1, 0 and 1. The
sine going in is a grey ghost behind it. Below 1
- Without
AudioWorklet(or if it cannot be loaded) the sound goes through it as it is, and the console says why. The worklet is loaded with the first play that needs it. - Cost: in e2e 34 headless Chrome on the CI's Windows machine rendered 64 stereo voices through a Bitcrusher for 2 s in 1.9 s, so about 68 of them fill the audio thread (2026-10-06). There is no limit on how many play at once.
Phaser and Flanger
Both (#341) run on the same worklet as Pitch, sample by sample, and both blend the sound with a
shifted copy of itself, so some frequencies cancel. wet 0 leaves the sound as it is, 1 is half
sound and half copy: there the notches are deepest, and the peaks stay at the level of the sound,
so neither ever makes it louder. A wet typed in at 0 leaves the node out, fed from a parameter it
stays.
- Phaser: a chain of first-order allpasses, each turning the phase by 90° at its frequency.
Where the copy is turned by 180° it cancels the sound: two stages make one notch at
frequency, four make two, around it, with a peak atfrequencyitself. A sine LFO moves the frequency of every stagedepthoctaves wide aroundfrequency(half up, half down),ratetimes a second; atdepth0 orrate0 the notches stand still.feedbacksends what leaves the chain back in, a sample later, which sharpens the notches into a ring. Stages is a setting, not an input. - Flanger: a delay line, read with linear interpolation. The delay is
offset, plus whatsweephas added since the sound started (in ms per second, either way), plus up todepthms from an LFO that starts at 0. As in sfxr the delay is mirrored at 0: anoffsetof 1 ms sweeping down at 10 ms/s is 0 after 0.1 s and 1 ms again after 0.2 s. It never goes past 20 ms. Atdepth0 the copy only sweeps, as sfxr's does.feedbacksends the copy back into the line, every round exactly one delay later (the worklet needs no render quantum per round, unlike a Delay). - sfxr's phaser is this Flanger with
depth,feedbackandrateat 0. sfxr adds the copy at the same level as the sound, the Flanger atwet1 at half of each, so the same sound comes out 6 dB quieter. sfxr's delay ends at 2.9 ms, the Flanger's at 20 ms, so a long sweep goes on further. frequencyandoffsetmove in what they hold (the frequency in octaves), and in an instrument an Envelope can movefrequency,offsetandwetwhile a note plays. The other numbers move with Set sound FX parameters as well.- The devices (#398): in the graph a Phaser is a plate of 760x280 graph units in mustard, a
Flanger one of 680x280 in steel blue, their inputs on the left and their output on the right.
- The screen draws what is left of each frequency, 20 Hz to 20 kHz in octaves, +12 to -36 dB: the notches of the Phaser, the comb of the Flanger, and the peaks feedback rings, as one thin line. A comb finer than the screen zigzags over the columns it runs through instead of aliasing. It uses the allpasses, the feedback and the delay of the worklet, at 48 kHz: the unit tests find the same dB as the worklet on a sine, within 0.005 dB.
- The Flanger's comb is reckoned exactly, through the ends of each column and the peaks and notches in it, not read at points (#398). Behind the line a faint trail shows what it swept over the last 1/20 s, and over the last 1/40 s once more, so it fades with age. High up its teeth pass faster than frames are drawn, and a sharp line of them would flicker. Where they move more than a quarter of a tooth within the trail, the line fades out, and from half a tooth on only the trail is left there: a calm band from the deepest notch to the highest peak. Where the eye can follow the teeth, the line stays sharp and moves. A Flanger that stands still has no trail.
- Under it a lane shows where that goes over the first seconds of a sound: the Phaser's turn in Hz, the Flanger's delay in ms (up to 2.5, 5, 10 or 20). A light runs over the lane in real time and the response above is drawn where it is, so the notches swirl and the comb jets as they will. The span is two swings of the LFO at least, for a Flanger as long as its sweep takes to reach 20 ms. The range they move through is shaded, a thin line marks the Phaser's turn or the Flanger's first notch (500 Hz at 1 ms).
- Above the screen
2 NOTCHES(half the stages) orDELAY 1 ms TO 3 ms, under itTURNS 400 Hz TO 1.6 kHzandEVERY 2 s, orLFO EVERY 4 sandSWEEP +5 ms/s. Atwet0 it saysDRY. The Rate lamp swings with the light as on the Tremolo.
- On the plate: the Phaser has a button for each number of stages and
Freq,Depth,Rate,FeedbackandWetin a row. The Flanger hasOffsetandSweep,RateandDepth,FeedbackandWetin three columns of two.Freqgives its low end more of the turn (800 Hz just under the middle),Sweepstands straight up at 0. A button and each turn are one undo step, a wired number locks its knob. Atwet0 everything else is drawn faint, withoutdepththeRate. - Without
AudioWorkletthe sound goes through both as it is, as through the Bitcrusher. - Measured (2026-10-07, e2e 34, Chrome offline on the Windows runner): a 1 kHz sine at 0.8 through a Phaser at 1 kHz came out at an RMS of 0.0000 with two stages and 0.5657 with four (0.8 * 0.707, untouched). Swept 4 octaves wide once a second, the RMS over 25 ms windows ran between 0.07 and 0.501 (0.8 * 0.707 * cos(28°) = 0.496 two octaves off). A Flanger at 1 ms gave an impulse back after 48 samples at 0.5. Sweeping 100 ms/s from the start of its voice, an impulse 0.1 s in came back 533.3 samples later, as worked out.
- Cost: in the same run 64 stereo voices rendered for 2 s say about 47 Phasers of 12 stages with feedback, or about 63 Flangers with an LFO, fill the audio thread. There is no limit on how many play at once.
Listening in the tab. The header picks one of the project's sounds and plays it through the
graph as it stands — the same library as a running game, so it sounds the way the game will.
Stop there ends only what the tab started, as closing the tab and starting the game do. Play is
offered only while the graph compiles.
Every parameter has a number field in the header, starting at its default: what Play hands in.
Moving a field while the tab plays is heard at once on that sound. Those values are the tab's own
and never written into the asset.
A Pitch on a bus
A sound FX on a track, a pattern item or a whole Play pattern /
Play song (#346) has no file to read: it gets the sound of many notes as it plays. There the Pitch is a
pitch shifter of its own, the same phase vocoder fed as it goes. It cannot look ahead, so it holds
one frame back before it hands anything out: it is 2048 samples late (43 ms at 48 kHz, 46 ms at
44.1 kHz), at every pitch, 0 too, where it is the sound itself, exactly that much later. Because the
latency never changes, it is made up exactly:
- Inside the FX, the other way of a Mix is held back as far as the Pitch makes one late, so an octave doubling (Pitch on one way only) stays together.
- In a song, every note and clip starts as much earlier as the buses it is heard through are late (item, track and the whole playing added up). So everything is heard where it was planned: a track with a shifter in step with one without, and with the playhead. The scheduler plans that much further ahead, and the first note after Play comes that much later.
See Songs for where a bus gets its FX.
Parameters
The Input declares parameters in the inspector (+ Parameter): a name and a Default, numbers only. Each one is an output of the Input that can feed any number input of the graph, so "how low" or "how loud" is decided when the sound is played, not when the FX is built.
In a behavior, a Sound FX parameters node names the FX and picks the parameters it sets, the
way a Variables node picks variables: each picked one is a number input, starting at the
default the Input gives it. Its params output goes into fx params of Play sound.
- A parameter not picked, or nothing at
fx params, plays at its default. - A name the played FX does not know is left out. Where both nodes name their FX in place and the
two differ,
Play soundwarns: The parameters are for another sound FX. - Renaming a parameter at the Input renames it in every Sound FX parameters node naming that FX, edges and typed-in values included. A removed one stays on those nodes in red.
- A parameter keeps its place in the chain even at 0 (a Gain at 0 dB, a band at 0), so a value handed in later still finds it.
Changing a playing sound
Every play of Play sound is a sound of its own, and its output voice is the number of exactly
that one. Set sound FX parameters takes such a voice and an fx params bundle from a Sound
FX parameters node, and moves that one voice to the new values: an engine whose pitch follows the
speed, or a loop that gets quieter.
- Keep the number in a variable (a Number) to reach the voice from another event:
On start→Play sound→Variables(setEngineVoicefromvoice), thenOn update→Set sound FX parameterswithvoicefromEngineVoice. Run it every frame, or once when something happens. - Two plays of the same sound, from one object or from two, are two voices with two numbers, and each is changed on its own. A number can also be handed to another behavior like any other.
- The number is there at once, before the sound has loaded. A number is never given out twice in a run, so a kept number of a voice that has ended reaches nothing, never a newer sound.
- A parameter not in the bundle keeps the value it has now, not its default.
- With
timeat 0, every value glides there in about 0.1 s (setTargetAtTime, 30 ms time constant), so a jump does not click. A value that did not change is not sent again, so running it every frame costs next to nothing. timeis how many seconds the values take to get there: a pitch in semitones, a Gain in dB, the damping of a Delay in octaves, a band, a Mix and the other numbers of a Delay in what they hold. Moving a Delay'stimewhile it plays bends the pitch of the echoes on their way, as a tape delay does. It runs on the clock of the sound itself, not on the game's, so a sweep sounds the same every time, also the first.- Easing and Direction say how the values get there, with the same choice an
animation key and a
Lerphave, and like there Direction and Curve only show where they matter. Linear (the default) runs evenly, Step holds the old value for the wholetimeand then jumps, the ten curves from Sine to Bounce and a drawn Curve bend the way. Linear and Step are ramps of the audio clock itself, every other easing is handed to it as a curve of 200 samples a second (setValueCurveAtTime). Back, Elastic and a curve may shoot past the target and come back. The easing says nothing whiletimeis 0. For a sweep, set it once right afterPlay sound:On enable→Play sound→Set sound FX parameters(voicefromPlay sound,pitch-64,time1.28) falls by 50 semitones a second. Counting a value down inOn updateorOn fixed updateinstead runs on game time, and the sound drifts away from it. - To span the whole sound, wire
lengthofPlay soundintotime: a fade or a filter sweep then ends with the last sample.lengthis one run of the sound (one pass of a loop), whatever the pitch does. It is 0 while the sound has not loaded yet, which only happens right after a project is opened, andtime0 is the quick glide. A pitch sweep overlengthends with the sound too, only heard about 20 ms behind (see How Pitch keeps the length). - A new change starts from where the voice is right now; whatever was still under way is dropped.
- A sound that has ended and the
voice0 of a play that played nothing are not reached. A detached voice is reached like any other. A voice still waiting for its bytes starts with the new values, and a change with atimestarts with the voice, so the whole of it is heard.
In the game the fx input of Play sound names the asset. An FX that does not compile, or
one that was deleted, plays the sound dry rather than not at all: a broken effect should not
silence the game. One asset can serve any number of sounds, so "everything in the cave sounds
hollow" is one asset and one pick per node.
Notes. To play a sound as notes — higher and lower, held and let go, several sounds as one
kit — make it the Sample of an instrument and play that with Play note.
Known limits. There is no Set volume on a playing voice (a Gain fed by a parameter does the
job), no Is playing, no fade and no positional audio. A bus has no pan and no send yet (sends come
with a reverb, #328). And nothing
collects sound files a project stopped referencing; they stay on the server, which is what makes
deleting a sound asset undoable.
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