Describe the Delay node and the tail of a voice #324 #327

- Sounds: Play sound hands out tail, Stop sound takes a voice, fades
  in 10 ms and has Let ring.
- Sounds: the Delay node in the table of FX nodes and its ranges.
- Sounds: new section on the Delay and how long a voice rings on,
  with the one render quantum per round in Chrome and the cost.
- Home: names delay and the tail on the Sounds line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
adminandClaude Opus 5.5 committed 2026-10-06 09:05:24 +02:00
1 parent 6591188476
commit c11e3109b0
2 files changed
+49 -6

No files matched your search

+1 -1
@@ -21,7 +21,7 @@ Live: <https://nockal.com/game-maker/> · Widget playground: <https://nockal.com
| [Vector art](Vector-Art) | The vector art asset, canvas and shapes, the Pen and Select tools, the shape list, how a drawing is worn |
| [Pixel art](Pixel-Art) | The pixel art asset and its palette, the tools, layers and frames, onion skin and the preview, resizing, how frames are worn and where the pixels are stored |
| [Fonts](Fonts) | Uploading a typeface, what a text wears, what the editor falls back to |
| [Sounds](Sounds) | Uploading a sound, what the asset says, `Play sound` and `Stop sound`, sound FX (gain, pitch, EQ, mix), what a pause and a stop do to a voice |
| [Sounds](Sounds) | Uploading a sound, what the asset says, `Play sound` and `Stop sound`, sound FX (gain, pitch, EQ, mix, delay) and how long a voice rings on, what a pause and a stop do to a voice |
| [Animations](Animations) | Animation assets, the Animation tab, how they run |
| [Curves](Curves) | Easings drawn by hand, the Curve tab, using one on a key or a `Lerp` |
| [Tiles](Tiles) | The tile asset, plain tiles and the two autotile kinds, the pictures as animation steps, the Tile tab |
+48 -5
@@ -36,8 +36,8 @@ so what the inspector plays is what the graph will play.
| 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` names a [sound FX](#sound-fx) to play it through, and `fx params` takes values for that FX's [parameters](#parameters). Its output `voice` is the number of this one play, for changing it [while it plays](#changing-a-playing-sound), and `length` how many seconds one run of the sound lasts; both 0 where nothing was played. |
| **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. |
| **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` names a [sound FX](#sound-fx) to play it through, and `fx params` takes values for that FX's [parameters](#parameters). Its output `voice` is the number of this one play, for changing it [while it plays](#changing-a-playing-sound), `length` how many seconds one run of the sound lasts, and `tail` that length with the [echoes](#delay-and-the-tail-of-a-voice) 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. |
| **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](#delay-and-the-tail-of-a-voice) fade as they would. |
| **Set sound FX parameters** | Hands new values to the [parameters](#changing-a-playing-sound) of one voice that is already playing, by the `voice` number its `Play sound` gave out, at once or over `time` seconds. |
| **Sound** | A constant sound, for either of the two to work on through an edge. |
@@ -70,7 +70,7 @@ in flight wins: the sound does not start afterwards on its own. Values a graph s
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 — a sound is something a thing in the world is *doing*, not a
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](Scenes) swaps away: every behavior in it is destroyed. A behavior on an object with
@@ -105,10 +105,12 @@ Input and the Output once each; their pins carry sound and fit nothing but each
| **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. |
| **5-Band-EQ** | Five bands in dB: 80 Hz and 12 kHz as shelves, 250 Hz, 1 kHz and 4 kHz as peaks. A band at 0 does nothing. |
| **Mix** | Blends the sounds at `a` and `b`: at 0 only `a` is heard, at 1 only `b`, at 0.5 half of each (0.5 is the default). |
| **Delay** | An echo: the sound comes back after `time` seconds, again and again, each time `feedback` times as loud. `damping` makes every echo duller than the last, `mix` is how loud the echoes are next to the sound, which stays as it is. **Ping-pong** sends the echoes left and right in turn. See [below](#delay-and-the-tail-of-a-voice). |
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, the EQ
bands -60 to +24 dB, Mix 0 to 1), whichever way the value arrives.
bands -60 to +24 dB, Mix 0 to 1, Delay `time` 0.01 to 2 s, `feedback` 0 to 0.95, `damping` and
`mix` 0 to 1), whichever way the value arrives.
**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
@@ -151,6 +153,45 @@ goes to both of its sounds. A Mix typed in at exactly 0 or 1 leaves the silent w
one is not even played. A Mix fed by a parameter keeps both ways, because the value may still
change while the voice plays.
### 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 at `mix`. The sound itself goes on past it untouched, so a Delay
never makes the sound quieter: `mix` 0.5 is echoes at half its level. Defaults: `time` 0.25 s,
`feedback` 0.4, `damping` 0.3, `mix` 0.5, Ping-pong off.
- **`feedback` stops at 0.95**: at 1 the echoes would never fade, above it they would grow.
- **`damping`** is 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 `mix` typed in at 0 leaves the Delay out. Fed from a parameter it stays, at whatever value.
- **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.
**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 `mix` (echo *k* is `mix * feedback^(k-1)` as loud;
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 `mix` 0.5 is nine echoes, 2.7 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 sound`** fades 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; a `Stop sound` without
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 sound`
does, 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.
**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. Play is offered only while the graph compiles.
@@ -195,7 +236,9 @@ speed, or a loop that gets quieter.
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.
- **`time`** is how many seconds the values take to get there: a pitch in semitones, a Gain in
dB, a band and a Mix in what they hold. It runs on the clock of the sound itself, not on the
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's `time` while 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](Animations) and a `Lerp` have, and like there *Direction* and *Curve* only show