Skip to content

Layering Sounds

A single sine wave is the plainest sound a speaker can make. It is fine for a tick and not much else.

A layer is another source that plays along with the first: a different wave shape, an octave up or down, a little quieter, and a few milliseconds late. Stack three or four and the note stops sounding like a test tone. Play the note below, then add layers.

0100200300400 ms

Each bar is one layer on a timeline. Two things make the stack read as one sound instead of several: the layers start within about 40ms of each other, and each one is quieter than the one before it, from a gain of 0.08 down to 0.02. Push the spacing to 80ms and you start to hear the seams.

Every layer should add something the others don't. Two copies of the same wave at the same pitch mostly just make it louder. A triangle adds softer harmonics, an octave up adds air, an octave down adds weight.

From chord to arpeggio

Layers don't have to share a pitch. This site's success sound is four square waves on C, E, G, and C, each starting 60ms after the last. Drag the spacing to hear where a chord turns into an arpeggio.

C
E
G
C

At 0ms all four notes land together, which is heavier than a confirmation needs to be. Between 40ms and 80ms you hear one rising gesture. Past 120ms it becomes a melody, and a cue that takes half a second asks for more attention than a copy button deserves.

Mixing textures

Not every layer is a note. A short burst of filtered noise has no pitch, but it has an attack, and that is what makes a sound feel like something was pressed. Flip the switch with each texture selected.

Do not disturb

The tone on its own is a beep. The noise on its own is a tap. Together they make a click with a pitch. The noise layer is under 30ms long and passes through a bandpass filter at 2600Hz, so it adds edge without hiss.

Usage

app.ts
import { definePatch } from "@web-kits/audio";

export const patch = definePatch({
  name: "ui",
  sounds: {
    // one note, three layers
    note: {
      layers: [
        {
          source: { type: "sine", frequency: 523 },
          envelope: { attack: 0.002, decay: 0.09, sustain: 0, release: 0.03 },
          gain: 0.08,
        },
        {
          source: { type: "triangle", frequency: 523 },
          envelope: { attack: 0.002, decay: 0.09, sustain: 0, release: 0.03 },
          gain: 0.05,
          delay: 0.04,
        },
        {
          source: { type: "noise", color: "white" },
          filter: { type: "bandpass", frequency: 2600 },
          envelope: { attack: 0, decay: 0.018, sustain: 0, release: 0.01 },
          gain: 0.06,
        },
      ],
    },
  },
});

// patch.play("note") from a click handler

Build one layer at a time. Get the first one right on its own, then add the next at a lower gain and keep it only if you can hear what it adds. If muting a layer changes nothing, delete it.

Resources

Search concepts

Search for a command to run...