A jump sound

The task: Make a jump sound.

The workshop synthesizer plays a sound written as a recipe, and measures it. The model can’t hear it either.

7 generations · 1 came out worse than its parent · llama-3.1-8b-instruct-fp8-fast

The root

Base model, bare prompt

Refused. The synthesizer’s meter (learn/public/tinysound.js).

  • Fail No JSON object in the reply.
The model’s reply, as written
*BOOP*

Editor’s note

The starting point. The whole reply: “*BOOP*”. Honest, and not a sound.

How it was made: 1 call, 0.3 neurons
  1. answer @cf/meta/llama-3.1-8b-instruct-fp8-fast

    max_tokens 500, temperature 0.6, seed 7 · 2026-10-14 21:57 UTC · 0.2 s · 0.3 neurons · cache 406d8d086188

    No system text.

    User

    Make a jump sound.

    Output

    *BOOP*

Generation 2 · from Base model, bare prompt

+ the synthesizer’s format

learn/public/tinysound.js

Refused. The synthesizer’s meter (learn/public/tinysound.js).

  • Pass A square at 200 Hz, 310 ms.
  • Pass 10 ms in, a smooth fade out.
  • Pass 310 ms.
  • Fail Peaks at -5.4 dBFS. Keep it at −6 or below.
The model’s reply, as written
{"wave":"square","pitch":200,"arp":{"notes":[-12,0,12],"step":0.1},"noise":0,"filter":{"type":"lowpass","cutoff":200},"attack":0.01,"decay":0.1,"sustain":0.5,"hold":0,"release":0.2,"volume":-6,"event":"player jump","why":"player lands on a platform"}
Before: what “Base model, bare prompt” made

Refused. The synthesizer’s meter (learn/public/tinysound.js).

  • Fail No JSON object in the reply.
The model’s reply, as written
*BOOP*

Editor’s note

Better. Given the synthesizer’s format, it wrote a recipe: a square wave through a 200 Hz filter, stepping through three octaves. Refused for peaking at −5.4 dBFS. Its own “why” says it’s for landing.

What changed in the input

+789 −0 characters

Added: 789 characters (show)System: You write sounds for a synthesizer that plays a sound described as one JSON object (a recipe): A sound (a recipe): wave 'sine' | 'triangle' | 'square' | 'sawtooth' | 'noise' pitch Hz at the start (30–4000; not needed for noise) slide? Hz at the end (an exponential glide) arp? { notes: [semitones], step: seconds } steps through the notes (a chime, a power-up) noise? 0–1, how much noise is mixed into the wave filter? { type: 'lowpass' | 'highpass' | 'bandpass', cutoff: Hz (40–8000), q?: 0.3–12 } attack, decay, sustain (0–1 level), hold?, release the envelope, in seconds volume dB at the envelope's peak (−48 to 0) Also include "event" (the game event it is for) and "why" (one sentence). Reply with the JSON object only.
User: Make a jump sound.
How it was made: 1 call, 5.9 neurons
  1. answer @cf/meta/llama-3.1-8b-instruct-fp8-fast

    max_tokens 300, temperature 0.6, seed 7 · 2026-10-14 21:57 UTC · 0.8 s · 5.9 neurons · cache 5a0b22b8664c

    System text (781 characters)
    You write sounds for a synthesizer that plays a sound described as one JSON object (a recipe):
    A sound (a recipe):
      wave        'sine' | 'triangle' | 'square' | 'sawtooth' | 'noise'
      pitch       Hz at the start (30–4000; not needed for noise)    slide?  Hz at the end (an exponential glide)
      arp?        { notes: [semitones], step: seconds }   steps through the notes (a chime, a power-up)
      noise?      0–1, how much noise is mixed into the wave
      filter?     { type: 'lowpass' | 'highpass' | 'bandpass', cutoff: Hz (40–8000), q?: 0.3–12 }
      attack, decay, sustain (0–1 level), hold?, release   the envelope, in seconds
      volume      dB at the envelope's peak (−48 to 0)
    Also include "event" (the game event it is for) and "why" (one sentence). Reply with the JSON object only.

    User

    Make a jump sound.

    Output

    {"wave":"square","pitch":200,"arp":{"notes":[-12,0,12],"step":0.1},"noise":0,"filter":{"type":"lowpass","cutoff":200},"attack":0.01,"decay":0.1,"sustain":0.5,"hold":0,"release":0.2,"volume":-6,"event":"player jump","why":"player lands on a platform"}

Generation 3 · from + the synthesizer’s format

+ the sound-as-recipe module

Pathway 4 · Module 23: Generic sound slop Pathway 4 · Module 24: A sound is a recipe

Playable, by the checker’s rules. The synthesizer’s meter (learn/public/tinysound.js).

  • Pass A sawtooth sliding 880→220 Hz, 75 ms.
  • Pass 5 ms in, a smooth fade out.
  • Pass 75 ms.
  • Pass Peaks at -13 dBFS.
The model’s reply, as written
{"event":"jump","wave":"sawtooth","pitch":880,"slide":220,"arp":{"notes":[0,4,7],"step":0.08},"noise":0.2,"filter":{"type":"highpass","cutoff":2000},"attack":0.005,"decay":0.05,"sustain":0.8,"release":0.02,"volume":-12,"why":"A bright wave sweeping up, a little too long, a little too loud."}
Before: what “+ the synthesizer’s format” made

Refused. The synthesizer’s meter (learn/public/tinysound.js).

  • Pass A square at 200 Hz, 310 ms.
  • Pass 10 ms in, a smooth fade out.
  • Pass 310 ms.
  • Fail Peaks at -5.4 dBFS. Keep it at −6 or below.
The model’s reply, as written
{"wave":"square","pitch":200,"arp":{"notes":[-12,0,12],"step":0.1},"noise":0,"filter":{"type":"lowpass","cutoff":200},"attack":0.01,"decay":0.1,"sustain":0.5,"hold":0,"release":0.2,"volume":-6,"event":"player jump","why":"player lands on a platform"}

Editor’s note

Mixed. With the two sound lessons it passes the meter, but it slides down from 880 to 220 Hz on a sawtooth, and its “why” is the lesson’s description of the bad jump sound, copied: “a little too long, a little too loud”. It learned the words, not the point.

What changed in the input

+4,109 −0 characters

System: You write sounds for a synthesizer that plays a sound described as one JSON object (a recipe): A sound (a recipe): wave 'sine' | 'triangle' | '… 436 unchanged characters … release the envelope, in seconds volume dB at the envelope's peak (−48 to 0) Also include "event" (the game event it is for) and "why" (one sentence). Apply
Added: 4,104 characters (show)this teaching: Module 23, Generic sound slop: What “chill music” and “a jump sound” make, and why you’ve heard them before. The default sound Ask a machine for “chill background music” and you get the average of every playlist with “chill” in its name: a soft piano, a lazy beat, a bit of vinyl crackle, and no reason to be in your game rather than anyone else’s. Ask for “a jump sound” and you get the one everybody has heard: a buzzy wave sweeping up, a little too long, a little too loud. It’s pathway 1 again, for the ears. Every decision you don’t make gets the most common answer. In sound, the most common answer is often slightly broken as well: it clicks, it clips, it lasts too long, or it sounds as if it came from a different game to everything around it. How this site’s sound was made Every sound on slopped.site was written by an AI, as code: Scroll’s ambient music, the games’ effects, Drip’s chiptunes. No audio files, no samples. And the AI that wrote them can’t hear them. So the decisions were written down (one key per piece of music, effects short and quiet, pitch rising with achievement), and every change was measured with a tool that records the page and prints the numbers. The first pass of Scroll’s music had 68–89% of its energy below 200 Hz: mud. Nobody heard it; the meter did. This pathway teaches that way of working, and it’s honest about it: AI-assisted, decided by you, checked by a tool. The brief: five lines Brief sound the way you’d brief an image. Game: what it is, in a line. Events: the moments that need a sound (jump, coin, hurt, lose…). Palette: what the sounds are made of (“soft square-wave blips, filtered noise for splashes”). Music: decisions (“slow and minor, a sparse triangle tune over a pulsing bass”). Never: what’s banned (“no reverb tails”, “nothing over half a second”). This pathway builds one sound pack in layers, and every lab after this one is checked against the ones before it: the first sound against these events, the pack against that sound, the music against the palette, and so on to the end. It works for any small game, including the one you built in pathway 3. --- Module 24, A sound is a recipe: Wave, envelope, filter, pitch: four decisions per sound. Four decisions per sound A synthesized sound is a recipe, and a short one. An oscillator makes a tone: a wave repeating at a pitch, in hertz. A sine is pure and soft; a triangle is round; a square is hollow and chippy (Drip’s melodies are close cousins); a sawtooth is bright and buzzy. Noise has no pitch at all: it’s splashes, hisses, impacts and rain. An envelope shapes the loudness over time: attack (how fast it starts), decay (how fast it falls to the sustain level), sustain (the level it holds), release (how it fades out). A filter takes part of the sound away: a lowpass removes the top, so a square goes from harsh to warm. And the pitch can move: a slide up feels like going up, a slide down like falling. { "event": "dive", "wave": "triangle", "pitch": 520, "slide": 260, "attack": 0.004, "decay": 0.12, "sustain": 0, "release": 0.03, "filter": { "type": "lowpass", "cutoff": 1800 }, "volume": -16, "why": "A soft wave sliding down an octave because the tin goes down; short, because you dive every second." } (One recipe from the example pack. Times in seconds, pitch in hertz, volume in decibels below full scale (dBFS).) Clicks and headroom Two rules come from physics, not taste. A sound that starts or stops instantly makes a click: the speaker has to jump. Three milliseconds of attack and ten of fade-out are enough to stop it. Drip once made full-scale one-sample spikes because a filter was swept from its default setting at the instant a noise burst began; the measuring tool found them, and the fix went into its skill. And a sound needs headroom: room below full scale (0 dBFS) for everything else that plays at the same moment. A filter with a high Q (a sharp resonance) can make a sound louder than its volume setting says, so the checker renders your recipe and measures the real peak. Keep each effect at −6 dBFS or below.
Reply with the JSON object only. User: Make a jump sound.
How it was made: 1 call, 10.6 neurons
  1. answer @cf/meta/llama-3.1-8b-instruct-fp8-fast

    max_tokens 300, temperature 0.6, seed 7 · 2026-10-14 21:57 UTC · 1.0 s · 10.6 neurons · cache 6c4764e846f8

    System text (4,894 characters)
    You write sounds for a synthesizer that plays a sound described as one JSON object (a recipe):
    A sound (a recipe):
      wave        'sine' | 'triangle' | 'square' | 'sawtooth' | 'noise'
      pitch       Hz at the start (30–4000; not needed for noise)    slide?  Hz at the end (an exponential glide)
      arp?        { notes: [semitones], step: seconds }   steps through the notes (a chime, a power-up)
      noise?      0–1, how much noise is mixed into the wave
      filter?     { type: 'lowpass' | 'highpass' | 'bandpass', cutoff: Hz (40–8000), q?: 0.3–12 }
      attack, decay, sustain (0–1 level), hold?, release   the envelope, in seconds
      volume      dB at the envelope's peak (−48 to 0)
    Also include "event" (the game event it is for) and "why" (one sentence).
    
    Apply this teaching:
    
    Module 23, Generic sound slop: What “chill music” and “a jump sound” make, and why you’ve heard them before.
    
    The default sound
    Ask a machine for “chill background music” and you get the average of every playlist with “chill” in its name: a soft piano, a lazy beat, a bit of vinyl crackle, and no reason to be in your game rather than anyone else’s. Ask for “a jump sound” and you get the one everybody has heard: a buzzy wave sweeping up, a little too long, a little too loud.
    It’s pathway 1 again, for the ears. Every decision you don’t make gets the most common answer. In sound, the most common answer is often slightly broken as well: it clicks, it clips, it lasts too long, or it sounds as if it came from a different game to everything around it.
    
    How this site’s sound was made
    Every sound on slopped.site was written by an AI, as code: Scroll’s ambient music, the games’ effects, Drip’s chiptunes. No audio files, no samples. And the AI that wrote them can’t hear them.
    So the decisions were written down (one key per piece of music, effects short and quiet, pitch rising with achievement), and every change was measured with a tool that records the page and prints the numbers. The first pass of Scroll’s music had 68–89% of its energy below 200 Hz: mud. Nobody heard it; the meter did. This pathway teaches that way of working, and it’s honest about it: AI-assisted, decided by you, checked by a tool.
    
    The brief: five lines
    Brief sound the way you’d brief an image. Game: what it is, in a line. Events: the moments that need a sound (jump, coin, hurt, lose…). Palette: what the sounds are made of (“soft square-wave blips, filtered noise for splashes”). Music: decisions (“slow and minor, a sparse triangle tune over a pulsing bass”). Never: what’s banned (“no reverb tails”, “nothing over half a second”).
    This pathway builds one sound pack in layers, and every lab after this one is checked against the ones before it: the first sound against these events, the pack against that sound, the music against the palette, and so on to the end. It works for any small game, including the one you built in pathway 3.
    
    ---
    
    Module 24, A sound is a recipe: Wave, envelope, filter, pitch: four decisions per sound.
    
    Four decisions per sound
    A synthesized sound is a recipe, and a short one. An oscillator makes a tone: a wave repeating at a pitch, in hertz. A sine is pure and soft; a triangle is round; a square is hollow and chippy (Drip’s melodies are close cousins); a sawtooth is bright and buzzy. Noise has no pitch at all: it’s splashes, hisses, impacts and rain.
    An envelope shapes the loudness over time: attack (how fast it starts), decay (how fast it falls to the sustain level), sustain (the level it holds), release (how it fades out). A filter takes part of the sound away: a lowpass removes the top, so a square goes from harsh to warm. And the pitch can move: a slide up feels like going up, a slide down like falling.
    
    {
      "event": "dive",
      "wave": "triangle",
      "pitch": 520, "slide": 260,
      "attack": 0.004, "decay": 0.12, "sustain": 0, "release": 0.03,
      "filter": { "type": "lowpass", "cutoff": 1800 },
      "volume": -16,
      "why": "A soft wave sliding down an octave because the tin goes down; short, because you dive every second."
    }
    (One recipe from the example pack. Times in seconds, pitch in hertz, volume in decibels below full scale (dBFS).)
    
    Clicks and headroom
    Two rules come from physics, not taste. A sound that starts or stops instantly makes a click: the speaker has to jump. Three milliseconds of attack and ten of fade-out are enough to stop it. Drip once made full-scale one-sample spikes because a filter was swept from its default setting at the instant a noise burst began; the measuring tool found them, and the fix went into its skill.
    And a sound needs headroom: room below full scale (0 dBFS) for everything else that plays at the same moment. A filter with a high Q (a sharp resonance) can make a sound louder than its volume setting says, so the checker renders your recipe and measures the real peak. Keep each effect at −6 dBFS or below.
    
    Reply with the JSON object only.

    User

    Make a jump sound.

    Output

    {"event":"jump","wave":"sawtooth","pitch":880,"slide":220,"arp":{"notes":[0,4,7],"step":0.08},"noise":0.2,"filter":{"type":"highpass","cutoff":2000},"attack":0.005,"decay":0.05,"sustain":0.8,"release":0.02,"volume":-12,"why":"A bright wave sweeping up, a little too long, a little too loud."}

Generation 4 · from + the sound-as-recipe module

+ the meter, in a loop

Pathway 4 · Module 28: Mix it, measure it learn/public/tinysound.js

Playable, by the checker’s rules. The synthesizer’s meter (learn/public/tinysound.js), after 1 attempt.

  • Pass A sawtooth sliding 880→220 Hz, 75 ms.
  • Pass 5 ms in, a smooth fade out.
  • Pass 75 ms.
  • Pass Peaks at -13 dBFS.
The model’s reply, as written
{"event":"jump","wave":"sawtooth","pitch":880,"slide":220,"arp":{"notes":[0,4,7],"step":0.08},"noise":0.2,"filter":{"type":"highpass","cutoff":2000},"attack":0.005,"decay":0.05,"sustain":0.8,"release":0.02,"volume":-12,"why":"A bright wave sweeping up, a little too long, a little too loud."}
Before: what “+ the sound-as-recipe module” made

Playable, by the checker’s rules. The synthesizer’s meter (learn/public/tinysound.js).

  • Pass A sawtooth sliding 880→220 Hz, 75 ms.
  • Pass 5 ms in, a smooth fade out.
  • Pass 75 ms.
  • Pass Peaks at -13 dBFS.
The model’s reply, as written
{"event":"jump","wave":"sawtooth","pitch":880,"slide":220,"arp":{"notes":[0,4,7],"step":0.08},"noise":0.2,"filter":{"type":"highpass","cutoff":2000},"attack":0.005,"decay":0.05,"sustain":0.8,"release":0.02,"volume":-12,"why":"A bright wave sweeping up, a little too long, a little too loud."}

Editor’s note

No real change. It passed the meter on the first attempt, so the loop had nothing to send back. Same sound.

What changed in the input

The input is identical. What changed is how the node was made (see below).

How it was made: 1 call, 10.6 neurons
  1. attempt 1 @cf/meta/llama-3.1-8b-instruct-fp8-fast

    max_tokens 300, temperature 0.6, seed 7 · 2026-10-14 21:57 UTC · 1.0 s · 10.6 neurons · cache 6c4764e846f8

    System text (4,894 characters)
    You write sounds for a synthesizer that plays a sound described as one JSON object (a recipe):
    A sound (a recipe):
      wave        'sine' | 'triangle' | 'square' | 'sawtooth' | 'noise'
      pitch       Hz at the start (30–4000; not needed for noise)    slide?  Hz at the end (an exponential glide)
      arp?        { notes: [semitones], step: seconds }   steps through the notes (a chime, a power-up)
      noise?      0–1, how much noise is mixed into the wave
      filter?     { type: 'lowpass' | 'highpass' | 'bandpass', cutoff: Hz (40–8000), q?: 0.3–12 }
      attack, decay, sustain (0–1 level), hold?, release   the envelope, in seconds
      volume      dB at the envelope's peak (−48 to 0)
    Also include "event" (the game event it is for) and "why" (one sentence).
    
    Apply this teaching:
    
    Module 23, Generic sound slop: What “chill music” and “a jump sound” make, and why you’ve heard them before.
    
    The default sound
    Ask a machine for “chill background music” and you get the average of every playlist with “chill” in its name: a soft piano, a lazy beat, a bit of vinyl crackle, and no reason to be in your game rather than anyone else’s. Ask for “a jump sound” and you get the one everybody has heard: a buzzy wave sweeping up, a little too long, a little too loud.
    It’s pathway 1 again, for the ears. Every decision you don’t make gets the most common answer. In sound, the most common answer is often slightly broken as well: it clicks, it clips, it lasts too long, or it sounds as if it came from a different game to everything around it.
    
    How this site’s sound was made
    Every sound on slopped.site was written by an AI, as code: Scroll’s ambient music, the games’ effects, Drip’s chiptunes. No audio files, no samples. And the AI that wrote them can’t hear them.
    So the decisions were written down (one key per piece of music, effects short and quiet, pitch rising with achievement), and every change was measured with a tool that records the page and prints the numbers. The first pass of Scroll’s music had 68–89% of its energy below 200 Hz: mud. Nobody heard it; the meter did. This pathway teaches that way of working, and it’s honest about it: AI-assisted, decided by you, checked by a tool.
    
    The brief: five lines
    Brief sound the way you’d brief an image. Game: what it is, in a line. Events: the moments that need a sound (jump, coin, hurt, lose…). Palette: what the sounds are made of (“soft square-wave blips, filtered noise for splashes”). Music: decisions (“slow and minor, a sparse triangle tune over a pulsing bass”). Never: what’s banned (“no reverb tails”, “nothing over half a second”).
    This pathway builds one sound pack in layers, and every lab after this one is checked against the ones before it: the first sound against these events, the pack against that sound, the music against the palette, and so on to the end. It works for any small game, including the one you built in pathway 3.
    
    ---
    
    Module 24, A sound is a recipe: Wave, envelope, filter, pitch: four decisions per sound.
    
    Four decisions per sound
    A synthesized sound is a recipe, and a short one. An oscillator makes a tone: a wave repeating at a pitch, in hertz. A sine is pure and soft; a triangle is round; a square is hollow and chippy (Drip’s melodies are close cousins); a sawtooth is bright and buzzy. Noise has no pitch at all: it’s splashes, hisses, impacts and rain.
    An envelope shapes the loudness over time: attack (how fast it starts), decay (how fast it falls to the sustain level), sustain (the level it holds), release (how it fades out). A filter takes part of the sound away: a lowpass removes the top, so a square goes from harsh to warm. And the pitch can move: a slide up feels like going up, a slide down like falling.
    
    {
      "event": "dive",
      "wave": "triangle",
      "pitch": 520, "slide": 260,
      "attack": 0.004, "decay": 0.12, "sustain": 0, "release": 0.03,
      "filter": { "type": "lowpass", "cutoff": 1800 },
      "volume": -16,
      "why": "A soft wave sliding down an octave because the tin goes down; short, because you dive every second."
    }
    (One recipe from the example pack. Times in seconds, pitch in hertz, volume in decibels below full scale (dBFS).)
    
    Clicks and headroom
    Two rules come from physics, not taste. A sound that starts or stops instantly makes a click: the speaker has to jump. Three milliseconds of attack and ten of fade-out are enough to stop it. Drip once made full-scale one-sample spikes because a filter was swept from its default setting at the instant a noise burst began; the measuring tool found them, and the fix went into its skill.
    And a sound needs headroom: room below full scale (0 dBFS) for everything else that plays at the same moment. A filter with a high Q (a sharp resonance) can make a sound louder than its volume setting says, so the checker renders your recipe and measures the real peak. Keep each effect at −6 dBFS or below.
    
    Reply with the JSON object only.

    User

    Make a jump sound.

    Output

    {"event":"jump","wave":"sawtooth","pitch":880,"slide":220,"arp":{"notes":[0,4,7],"step":0.08},"noise":0.2,"filter":{"type":"highpass","cutoff":2000},"attack":0.005,"decay":0.05,"sustain":0.8,"release":0.02,"volume":-12,"why":"A bright wave sweeping up, a little too long, a little too loud."}

Generation 4 · from + the sound-as-recipe module

+ the sound-design skill

.claude/skills/sound-design/SKILL.md · the exact text

Refused. The synthesizer’s meter (learn/public/tinysound.js).

  • Pass A sawtooth sliding 520→520 Hz, 151 ms.
  • Fail It starts in 1 ms: give "attack" at least 3 ms.
  • Pass 151 ms.
  • Pass Peaks at -6.8 dBFS.
The model’s reply, as written
{"arp":{"notes":[-12,0,12],"step":0.1},"attack":0.001,"decay":0.05,"event":"jump","filter":{"cutoff":2200,"q":6,"type":"lowpass"},"noise":0.1,"pitch":520,"release":0.1,"slide":520,"sustain":0.5,"volume":-12,"wave":"sawtooth","why":"A bright, buzzy wave sweeping up, a little too long, a little too loud."}
Before: what “+ the sound-as-recipe module” made

Playable, by the checker’s rules. The synthesizer’s meter (learn/public/tinysound.js).

  • Pass A sawtooth sliding 880→220 Hz, 75 ms.
  • Pass 5 ms in, a smooth fade out.
  • Pass 75 ms.
  • Pass Peaks at -13 dBFS.
The model’s reply, as written
{"event":"jump","wave":"sawtooth","pitch":880,"slide":220,"arp":{"notes":[0,4,7],"step":0.08},"noise":0.2,"filter":{"type":"highpass","cutoff":2000},"attack":0.005,"decay":0.05,"sustain":0.8,"release":0.02,"volume":-12,"why":"A bright wave sweeping up, a little too long, a little too loud."}

Editor’s note

Worse. The sound-design skill on top, and it starts in 1 ms, which clicks. Refused. More instructions, one more mistake.

What changed in the input

+7,984 −0 characters

System: You write sounds for a synthesizer that plays a sound described as one JSON object (a recipe): A sound (a recipe): wave 'sine' | 'triangle' | '… 4,550 unchanged characters …ce) can make a sound louder than its volume setting says, so the checker renders your recipe and measures the real peak. Keep each effect at −6 dBFS or below.
Added: 7,984 characters (show)And follow this skill: --- name: sound-design description: Sound design and mixing for slopped.site's Web Audio - synthesis recipes (pads, bells, formant choir, noise beds for water and rain, whooshes, game SFX), routing (filters, reverb, delay, compressor), level and spectrum targets, browser rules (user gesture, tab visibility, node budget), and listen.mjs, which records a page's audio to WAV and measures loudness, peaks, brightness and stereo. Use when making or changing any sound on the sites (Scroll's ambient.js, SK8's SFX), when something sounds muddy, harsh, quiet, clicky or too loud, or before shipping audio. --- # Sound design for slopped.site All audio is synthesised with Web Audio: no files. Scroll's ambient music is `scroll/public/ambient.js`; SK8's sound effects are the `SFX` table in `games/public/sk8/index.html` (see game-feel for which verb gets which sound). Composition (notes, chords, form) is the music-composition skill; this one is about *how things sound* and *how they sit together*. ## You can't hear it: measure it ```sh node .claude/skills/sound-design/scripts/listen.mjs scroll spa 20 [out-dir] # one mood, 20 s node .claude/skills/sound-design/scripts/listen.mjs scroll choir 16 [out-dir] --scroll # while falling fast node .claude/skills/sound-design/scripts/listen.mjs scroll brown 16 # a noise on its own node .claude/skills/sound-design/scripts/listen.mjs scroll spa+brown 16 # music over a noise ``` It serves the site, starts the sound in headless Chrome (autoplay allowed), records the master output, writes a **WAV the user can play**, and prints: | Measure | Target for ambient beds | What it catches | | --- | --- | --- | | RMS loudness | about −24 dBFS, moods within ±2 dB of each other | too quiet, too loud, jumps between moods | | Peak / clipped samples | peak below −6 dBFS, 0 clipped | distortion | | Crest factor | 10–16 dB | squashed (low) or spiky (high) | | Per-2 s RMS | steady after the fade-in (±3 dB) | pumping, dropouts, runaway build-ups | | Spectral centroid | 300–1000 Hz warm ambient; rain/noise moods higher | dull (low) or harsh (high) | | Energy below 200 Hz | under ~55% | mud from drones, low noise and pad roots | | Energy above 4 kHz | a few % (more for rain) | no "air", or hiss | | L/R correlation | 0.1–0.7 | mono (1) or phase problems (below 0) | | Longest silence | under ~1 s after the fade-in | something not playing | The first pass of Scroll's moods measured 68–89% of energy below 200 Hz and nothing above 4 kHz: the drones were too loud and a shared lowpass was muffling the bells. **Always run it after changing a sound, and report the numbers plus the WAV path; never say a sound is good from code alone.** ## Recipes (all in ambient.js) - **Pad**: two sawtooths per note detuned ±6–7 cents (chorus), root as a triangle, slow attack (35% of the chord), through a dark lowpass (~1.5 kHz) and a highpass (~110 Hz) so it sits above the drone. Pan the notes across the field. - **Bell / chime**: sine fundamental plus inharmonic partials (×2.76 at 25%, ×5.4 at 6%), 10 ms attack, exponential decay ~3.5 s, into a feedback delay (~0.48 s, feedback 0.42, lowpassed) and the reverb. Keep bells out of the dark pad filter or they lose their shimmer. - **Choir "aah/ooh"**: sawtooth with ~5 Hz vibrato (0.6% depth) through three bandpass formants (ah 800/1150/2900 Hz, oo 350/800/2400 Hz, Q 9/7/5, levels 1/0.55/0.2), the formants gliding from one vowel to the other over the note. Several staggered voices make a choir. - **Ocean**: brown noise, band-limited 160–1300 Hz (surf, not rumble), amplitude swelling with a ~0.09 Hz LFO. - **Rain**: white noise band-limited 1.4–5.2 kHz, plus occasional droplets (a sine sweeping down from 2–3.4 kHz in 60 ms). - **Drone**: root and fifth very low (D2 + A2), quiet (0.012–0.016): felt more than heard. - **Noise layer** (`NOISES`, its own gain `noiseOut`, never reacting to scroll): **white** (lowpassed at 12 kHz so it isn't a hiss), **pink** (Paul Kellet's filter on white noise, −3 dB/octave), **brown** (leaky integrator; highpass 30 Hz), **waves** (two brown-noise swells on different LFOs, 0.075 and 0.11 Hz, panned apart, so the surf never lines up the same way), **rainfall** (white noise 0.9–6 kHz plus a brown "roof" rumble under 500 Hz). Steady-state targets: about −26 to −29 dBFS. Steady noise feels louder than music at the same RMS, so white sits lowest. - **Noise loops** are 4–8 s stereo buffers, different per ear, with the last 0.25 s crossfaded into the start: brown noise drifts, so a plain loop clicks at the seam. - **Whoosh** (speed): looped white noise through a bandpass (~900 Hz), level ∝ speed². - **Reverb**: a convolver with generated stereo noise decaying as (1 − t)³ over ~7 s. Send 0.55–0.75 for ambient. - **Master**: music (`out`) and noise (`noiseOut`) → `master` (the Volume slider, squared so it follows perceived loudness) → highpass 45 Hz (no sub rumble on laptop speakers) → compressor (−18 dB threshold, 3:1) → output. Each mood has a `trim` that level-matches it; `listen.mjs` measures after the master, at volume 100. - **Mud check for low moods**: dropping a whole pad an octave puts its root near 50 Hz (Night first measured 74% below 200 Hz). Keep the pad in place and darken it with a lower `padCut` instead; a high `trim` on a sparse mood (Glass) also boosts its drone, so cut the drone when you raise the trim. | Sound | Measured RMS (steady) | Below 200 Hz | | --- | --- | --- | | Spa / Choir / Rain | −24 to −25 dBFS | 37–44% | | Night / Glass | about −25.5 / −26.5 dBFS | 36% / 37% | | White / Pink / Brown / Waves / Rainfall | −29 / −27 / −26 / −28 (swelling) / −27 dBFS | — | ## Rules - **Fades, not switches.** Every gain change ramps (`linearRampToValueAtTime` / `setTargetAtTime`), and every ramp starts from an explicit `setValueAtTime` (a ramp with no earlier event can jump). Stop sources after their fade, never mid-sound: clicks. - **User gesture first.** Browsers start audio only from a click or key. A remembered "sound on" waits for the first pointerdown or keydown. Default is off. - **Tab hidden → `ctx.suspend()`**, visible → resume if on. - **Node budget.** Create short-lived nodes per note and let them stop; keep long-running beds few (≤ 6). Schedule ahead with a ~200 ms timer and a 0.5 s look-ahead rather than per frame. - **Mix by role.** Bed (noise, drone) lowest, pad under decorations, decorations (bells, voice lines) on top but sparse. If something must be heard, make it *brighter* or *sparser* before making it louder. - **SK8 effects** are short and quiet because they layer constantly. Pitch rises with achievement (multiplier ticks, bank arpeggios). Everything goes through `sfx()`, so `M` mutes all of it. ## See it (for reviewers who can't hear) `python3 .claude/skills/sound-design/scripts/see.py <file.wav ...> [--out dir]` turns WAVs (from listen.mjs, or a game's own `listen.mjs`) into `<name>.see.png` plus a line of numbers each: - **The image:** loudness over time (RMS and peak, with clipping marked), a log-frequency spectrogram, and a piano roll of the strongest notes (C2–C7). - **The numbers:** peak/RMS dBFS, crest, clipped samples, low-end share under 200 Hz, centroid, a key estimate, a tempo estimate, and the loop seam (judged only when the file doesn't start or end in silence). It flags clipping, under 1 dB of headroom, more than 60% of the energy below 200 Hz (muddy on laptops, gone on phone speakers), and loop seams that click or jump in level. **Look at the PNG:** - a tune is a readable line in the piano roll; - an empty band between bass and melody is a hollow mix; - a smear under 300 Hz is mud; - vertical stripes are clicks. First run (2026-10-14, Always On's 12 songs): five had 62–75% of their energy under 200 Hz, and the battle theme's piano roll showed melody at C6–C7 and bass at C2–C3 with nothing in the middle octaves. Loudness numbers alone had passed all of them.
Reply with the JSON object only. User: Make a jump sound.
How it was made: 1 call, 20.1 neurons
  1. answer @cf/meta/llama-3.1-8b-instruct-fp8-fast

    max_tokens 300, temperature 0.6, seed 7 · 2026-10-14 21:57 UTC · 1.1 s · 20.1 neurons · cache 64c8323831b8

    System text (12,880 characters)
    You write sounds for a synthesizer that plays a sound described as one JSON object (a recipe):
    A sound (a recipe):
      wave        'sine' | 'triangle' | 'square' | 'sawtooth' | 'noise'
      pitch       Hz at the start (30–4000; not needed for noise)    slide?  Hz at the end (an exponential glide)
      arp?        { notes: [semitones], step: seconds }   steps through the notes (a chime, a power-up)
      noise?      0–1, how much noise is mixed into the wave
      filter?     { type: 'lowpass' | 'highpass' | 'bandpass', cutoff: Hz (40–8000), q?: 0.3–12 }
      attack, decay, sustain (0–1 level), hold?, release   the envelope, in seconds
      volume      dB at the envelope's peak (−48 to 0)
    Also include "event" (the game event it is for) and "why" (one sentence).
    
    Apply this teaching:
    
    Module 23, Generic sound slop: What “chill music” and “a jump sound” make, and why you’ve heard them before.
    
    The default sound
    Ask a machine for “chill background music” and you get the average of every playlist with “chill” in its name: a soft piano, a lazy beat, a bit of vinyl crackle, and no reason to be in your game rather than anyone else’s. Ask for “a jump sound” and you get the one everybody has heard: a buzzy wave sweeping up, a little too long, a little too loud.
    It’s pathway 1 again, for the ears. Every decision you don’t make gets the most common answer. In sound, the most common answer is often slightly broken as well: it clicks, it clips, it lasts too long, or it sounds as if it came from a different game to everything around it.
    
    How this site’s sound was made
    Every sound on slopped.site was written by an AI, as code: Scroll’s ambient music, the games’ effects, Drip’s chiptunes. No audio files, no samples. And the AI that wrote them can’t hear them.
    So the decisions were written down (one key per piece of music, effects short and quiet, pitch rising with achievement), and every change was measured with a tool that records the page and prints the numbers. The first pass of Scroll’s music had 68–89% of its energy below 200 Hz: mud. Nobody heard it; the meter did. This pathway teaches that way of working, and it’s honest about it: AI-assisted, decided by you, checked by a tool.
    
    The brief: five lines
    Brief sound the way you’d brief an image. Game: what it is, in a line. Events: the moments that need a sound (jump, coin, hurt, lose…). Palette: what the sounds are made of (“soft square-wave blips, filtered noise for splashes”). Music: decisions (“slow and minor, a sparse triangle tune over a pulsing bass”). Never: what’s banned (“no reverb tails”, “nothing over half a second”).
    This pathway builds one sound pack in layers, and every lab after this one is checked against the ones before it: the first sound against these events, the pack against that sound, the music against the palette, and so on to the end. It works for any small game, including the one you built in pathway 3.
    
    ---
    
    Module 24, A sound is a recipe: Wave, envelope, filter, pitch: four decisions per sound.
    
    Four decisions per sound
    A synthesized sound is a recipe, and a short one. An oscillator makes a tone: a wave repeating at a pitch, in hertz. A sine is pure and soft; a triangle is round; a square is hollow and chippy (Drip’s melodies are close cousins); a sawtooth is bright and buzzy. Noise has no pitch at all: it’s splashes, hisses, impacts and rain.
    An envelope shapes the loudness over time: attack (how fast it starts), decay (how fast it falls to the sustain level), sustain (the level it holds), release (how it fades out). A filter takes part of the sound away: a lowpass removes the top, so a square goes from harsh to warm. And the pitch can move: a slide up feels like going up, a slide down like falling.
    
    {
      "event": "dive",
      "wave": "triangle",
      "pitch": 520, "slide": 260,
      "attack": 0.004, "decay": 0.12, "sustain": 0, "release": 0.03,
      "filter": { "type": "lowpass", "cutoff": 1800 },
      "volume": -16,
      "why": "A soft wave sliding down an octave because the tin goes down; short, because you dive every second."
    }
    (One recipe from the example pack. Times in seconds, pitch in hertz, volume in decibels below full scale (dBFS).)
    
    Clicks and headroom
    Two rules come from physics, not taste. A sound that starts or stops instantly makes a click: the speaker has to jump. Three milliseconds of attack and ten of fade-out are enough to stop it. Drip once made full-scale one-sample spikes because a filter was swept from its default setting at the instant a noise burst began; the measuring tool found them, and the fix went into its skill.
    And a sound needs headroom: room below full scale (0 dBFS) for everything else that plays at the same moment. A filter with a high Q (a sharp resonance) can make a sound louder than its volume setting says, so the checker renders your recipe and measures the real peak. Keep each effect at −6 dBFS or below.
    
    And follow this skill:
    
    ---
    name: sound-design
    description: Sound design and mixing for slopped.site's Web Audio - synthesis recipes (pads, bells, formant choir, noise beds for water and rain, whooshes, game SFX), routing (filters, reverb, delay, compressor), level and spectrum targets, browser rules (user gesture, tab visibility, node budget), and listen.mjs, which records a page's audio to WAV and measures loudness, peaks, brightness and stereo. Use when making or changing any sound on the sites (Scroll's ambient.js, SK8's SFX), when something sounds muddy, harsh, quiet, clicky or too loud, or before shipping audio.
    ---
    
    # Sound design for slopped.site
    
    All audio is synthesised with Web Audio: no files. Scroll's ambient music is `scroll/public/ambient.js`; SK8's sound effects are the `SFX` table in `games/public/sk8/index.html` (see game-feel for which verb gets which sound). Composition (notes, chords, form) is the music-composition skill; this one is about *how things sound* and *how they sit together*.
    
    ## You can't hear it: measure it
    
    ```sh
    node .claude/skills/sound-design/scripts/listen.mjs scroll spa 20 [out-dir]            # one mood, 20 s
    node .claude/skills/sound-design/scripts/listen.mjs scroll choir 16 [out-dir] --scroll # while falling fast
    node .claude/skills/sound-design/scripts/listen.mjs scroll brown 16                     # a noise on its own
    node .claude/skills/sound-design/scripts/listen.mjs scroll spa+brown 16                 # music over a noise
    ```
    
    It serves the site, starts the sound in headless Chrome (autoplay allowed), records the master output, writes a **WAV the user can play**, and prints:
    
    | Measure | Target for ambient beds | What it catches |
    | --- | --- | --- |
    | RMS loudness | about −24 dBFS, moods within ±2 dB of each other | too quiet, too loud, jumps between moods |
    | Peak / clipped samples | peak below −6 dBFS, 0 clipped | distortion |
    | Crest factor | 10–16 dB | squashed (low) or spiky (high) |
    | Per-2 s RMS | steady after the fade-in (±3 dB) | pumping, dropouts, runaway build-ups |
    | Spectral centroid | 300–1000 Hz warm ambient; rain/noise moods higher | dull (low) or harsh (high) |
    | Energy below 200 Hz | under ~55% | mud from drones, low noise and pad roots |
    | Energy above 4 kHz | a few % (more for rain) | no "air", or hiss |
    | L/R correlation | 0.1–0.7 | mono (1) or phase problems (below 0) |
    | Longest silence | under ~1 s after the fade-in | something not playing |
    
    The first pass of Scroll's moods measured 68–89% of energy below 200 Hz and nothing above 4 kHz: the drones were too loud and a shared lowpass was muffling the bells. **Always run it after changing a sound, and report the numbers plus the WAV path; never say a sound is good from code alone.**
    
    ## Recipes (all in ambient.js)
    
    - **Pad**: two sawtooths per note detuned ±6–7 cents (chorus), root as a triangle, slow attack (35% of the chord), through a dark lowpass (~1.5 kHz) and a highpass (~110 Hz) so it sits above the drone. Pan the notes across the field.
    - **Bell / chime**: sine fundamental plus inharmonic partials (×2.76 at 25%, ×5.4 at 6%), 10 ms attack, exponential decay ~3.5 s, into a feedback delay (~0.48 s, feedback 0.42, lowpassed) and the reverb. Keep bells out of the dark pad filter or they lose their shimmer.
    - **Choir "aah/ooh"**: sawtooth with ~5 Hz vibrato (0.6% depth) through three bandpass formants (ah 800/1150/2900 Hz, oo 350/800/2400 Hz, Q 9/7/5, levels 1/0.55/0.2), the formants gliding from one vowel to the other over the note. Several staggered voices make a choir.
    - **Ocean**: brown noise, band-limited 160–1300 Hz (surf, not rumble), amplitude swelling with a ~0.09 Hz LFO.
    - **Rain**: white noise band-limited 1.4–5.2 kHz, plus occasional droplets (a sine sweeping down from 2–3.4 kHz in 60 ms).
    - **Drone**: root and fifth very low (D2 + A2), quiet (0.012–0.016): felt more than heard.
    - **Noise layer** (`NOISES`, its own gain `noiseOut`, never reacting to scroll): **white** (lowpassed at 12 kHz so it isn't a hiss), **pink** (Paul Kellet's filter on white noise, −3 dB/octave), **brown** (leaky integrator; highpass 30 Hz), **waves** (two brown-noise swells on different LFOs, 0.075 and 0.11 Hz, panned apart, so the surf never lines up the same way), **rainfall** (white noise 0.9–6 kHz plus a brown "roof" rumble under 500 Hz). Steady-state targets: about −26 to −29 dBFS. Steady noise feels louder than music at the same RMS, so white sits lowest.
    - **Noise loops** are 4–8 s stereo buffers, different per ear, with the last 0.25 s crossfaded into the start: brown noise drifts, so a plain loop clicks at the seam.
    - **Whoosh** (speed): looped white noise through a bandpass (~900 Hz), level ∝ speed².
    - **Reverb**: a convolver with generated stereo noise decaying as (1 − t)³ over ~7 s. Send 0.55–0.75 for ambient.
    - **Master**: music (`out`) and noise (`noiseOut`) → `master` (the Volume slider, squared so it follows perceived loudness) → highpass 45 Hz (no sub rumble on laptop speakers) → compressor (−18 dB threshold, 3:1) → output. Each mood has a `trim` that level-matches it; `listen.mjs` measures after the master, at volume 100.
    - **Mud check for low moods**: dropping a whole pad an octave puts its root near 50 Hz (Night first measured 74% below 200 Hz). Keep the pad in place and darken it with a lower `padCut` instead; a high `trim` on a sparse mood (Glass) also boosts its drone, so cut the drone when you raise the trim.
    
    | Sound | Measured RMS (steady) | Below 200 Hz |
    | --- | --- | --- |
    | Spa / Choir / Rain | −24 to −25 dBFS | 37–44% |
    | Night / Glass | about −25.5 / −26.5 dBFS | 36% / 37% |
    | White / Pink / Brown / Waves / Rainfall | −29 / −27 / −26 / −28 (swelling) / −27 dBFS | — |
    
    ## Rules
    
    - **Fades, not switches.** Every gain change ramps (`linearRampToValueAtTime` / `setTargetAtTime`), and every ramp starts from an explicit `setValueAtTime` (a ramp with no earlier event can jump). Stop sources after their fade, never mid-sound: clicks.
    - **User gesture first.** Browsers start audio only from a click or key. A remembered "sound on" waits for the first pointerdown or keydown. Default is off.
    - **Tab hidden → `ctx.suspend()`**, visible → resume if on.
    - **Node budget.** Create short-lived nodes per note and let them stop; keep long-running beds few (≤ 6). Schedule ahead with a ~200 ms timer and a 0.5 s look-ahead rather than per frame.
    - **Mix by role.** Bed (noise, drone) lowest, pad under decorations, decorations (bells, voice lines) on top but sparse. If something must be heard, make it *brighter* or *sparser* before making it louder.
    - **SK8 effects** are short and quiet because they layer constantly. Pitch rises with achievement (multiplier ticks, bank arpeggios). Everything goes through `sfx()`, so `M` mutes all of it.
    
    ## See it (for reviewers who can't hear)
    
    `python3 .claude/skills/sound-design/scripts/see.py <file.wav ...> [--out dir]` turns WAVs (from listen.mjs, or a game's own `listen.mjs`) into `<name>.see.png` plus a line of numbers each:
    - **The image:** loudness over time (RMS and peak, with clipping marked), a log-frequency spectrogram, and a piano roll of the strongest notes (C2–C7).
    - **The numbers:** peak/RMS dBFS, crest, clipped samples, low-end share under 200 Hz, centroid, a key estimate, a tempo estimate, and the loop seam (judged only when the file doesn't start or end in silence).
    
    It flags clipping, under 1 dB of headroom, more than 60% of the energy below 200 Hz (muddy on laptops, gone on phone speakers), and loop seams that click or jump in level. **Look at the PNG:**
    - a tune is a readable line in the piano roll;
    - an empty band between bass and melody is a hollow mix;
    - a smear under 300 Hz is mud;
    - vertical stripes are clicks.
    
    First run (2026-10-14, Always On's 12 songs): five had 62–75% of their energy under 200 Hz, and the battle theme's piano roll showed melody at C6–C7 and bass at C2–C3 with nothing in the middle octaves. Loudness numbers alone had passed all of them.
    
    Reply with the JSON object only.

    User

    Make a jump sound.

    Output

    {"arp":{"notes":[-12,0,12],"step":0.1},"attack":0.001,"decay":0.05,"event":"jump","filter":{"cutoff":2200,"q":6,"type":"lowpass"},"noise":0.1,"pitch":520,"release":0.1,"slide":520,"sustain":0.5,"volume":-12,"wave":"sawtooth","why":"A bright, buzzy wave sweeping up, a little too long, a little too loud."}

Generation 3 · from + the synthesizer’s format

+ the meter, in a loop

Pathway 4 · Module 28: Mix it, measure it learn/public/tinysound.js

Playable, by the checker’s rules. The synthesizer’s meter (learn/public/tinysound.js), after 2 attempts.

  • Pass A square at 200 Hz, 310 ms.
  • Pass 10 ms in, a smooth fade out.
  • Pass 310 ms.
  • Pass Peaks at -11.4 dBFS.
The model’s reply, as written
{"arp":{"notes":[-12,0,12],"step":0.1},"attack":0.01,"decay":0.1,"event":"player jump","filter":{"cutoff":200,"type":"lowpass"},"hold":0,"noise":0,"pitch":200,"release":0.2,"sustain":0.5,"volume":-12,"wave":"square","why":"player lands on a platform"}
Before: what “+ the synthesizer’s format” made

Refused. The synthesizer’s meter (learn/public/tinysound.js).

  • Pass A square at 200 Hz, 310 ms.
  • Pass 10 ms in, a smooth fade out.
  • Pass 310 ms.
  • Fail Peaks at -5.4 dBFS. Keep it at −6 or below.
The model’s reply, as written
{"wave":"square","pitch":200,"arp":{"notes":[-12,0,12],"step":0.1},"noise":0,"filter":{"type":"lowpass","cutoff":200},"attack":0.01,"decay":0.1,"sustain":0.5,"hold":0,"release":0.2,"volume":-6,"event":"player jump","why":"player lands on a platform"}

Editor’s note

Better. The meter said −5.4 dBFS was too hot; on the second attempt it turned the volume down to −12 and passed. It is still a low, muffled arpeggio that it thinks is a landing.

What changed in the input

The input is identical. What changed is how the node was made (see below).

How it was made: 2 calls, 10.7 neurons
  1. attempt 1 @cf/meta/llama-3.1-8b-instruct-fp8-fast

    max_tokens 300, temperature 0.6, seed 7 · 2026-10-14 21:57 UTC · 0.8 s · 5.9 neurons · cache 5a0b22b8664c

    System text (781 characters)
    You write sounds for a synthesizer that plays a sound described as one JSON object (a recipe):
    A sound (a recipe):
      wave        'sine' | 'triangle' | 'square' | 'sawtooth' | 'noise'
      pitch       Hz at the start (30–4000; not needed for noise)    slide?  Hz at the end (an exponential glide)
      arp?        { notes: [semitones], step: seconds }   steps through the notes (a chime, a power-up)
      noise?      0–1, how much noise is mixed into the wave
      filter?     { type: 'lowpass' | 'highpass' | 'bandpass', cutoff: Hz (40–8000), q?: 0.3–12 }
      attack, decay, sustain (0–1 level), hold?, release   the envelope, in seconds
      volume      dB at the envelope's peak (−48 to 0)
    Also include "event" (the game event it is for) and "why" (one sentence). Reply with the JSON object only.

    User

    Make a jump sound.

    Output

    {"wave":"square","pitch":200,"arp":{"notes":[-12,0,12],"step":0.1},"noise":0,"filter":{"type":"lowpass","cutoff":200},"attack":0.01,"decay":0.1,"sustain":0.5,"hold":0,"release":0.2,"volume":-6,"event":"player jump","why":"player lands on a platform"}
  2. attempt 2 @cf/meta/llama-3.1-8b-instruct-fp8-fast

    max_tokens 300, temperature 0.6, seed 7 · 2026-10-14 22:00 UTC · 0.8 s · 4.7 neurons · cache c44adf984d94

    System text (781 characters)
    You write sounds for a synthesizer that plays a sound described as one JSON object (a recipe):
    A sound (a recipe):
      wave        'sine' | 'triangle' | 'square' | 'sawtooth' | 'noise'
      pitch       Hz at the start (30–4000; not needed for noise)    slide?  Hz at the end (an exponential glide)
      arp?        { notes: [semitones], step: seconds }   steps through the notes (a chime, a power-up)
      noise?      0–1, how much noise is mixed into the wave
      filter?     { type: 'lowpass' | 'highpass' | 'bandpass', cutoff: Hz (40–8000), q?: 0.3–12 }
      attack, decay, sustain (0–1 level), hold?, release   the envelope, in seconds
      volume      dB at the envelope's peak (−48 to 0)
    Also include "event" (the game event it is for) and "why" (one sentence). Reply with the JSON object only.

    User

    Make a jump sound.
    
    Your previous answer:
    {"wave":"square","pitch":200,"arp":{"notes":[-12,0,12],"step":0.1},"noise":0,"filter":{"type":"lowpass","cutoff":200},"attack":0.01,"decay":0.1,"sustain":0.5,"hold":0,"release":0.2,"volume":-6,"event":"player jump","why":"player lands on a platform"}
    
    The checker refused it:
    - Peaks at -5.4 dBFS. Keep it at −6 or below.
    Fix exactly those problems. Reply with the corrected answer only.

    Output

    {"arp":{"notes":[-12,0,12],"step":0.1},"attack":0.01,"decay":0.1,"event":"player jump","filter":{"cutoff":200,"type":"lowpass"},"hold":0,"noise":0,"pitch":200,"release":0.2,"sustain":0.5,"volume":-12,"wave":"square","why":"player lands on a platform"}

Generation 2 · from Base model, bare prompt

+ the sound-design skill

.claude/skills/sound-design/SKILL.md · the exact text

Refused. The synthesizer’s meter (learn/public/tinysound.js).

  • Fail Not valid JSON: Expected property name or '}' in JSON at position 4 (line 2 column 3)
The model’s reply, as written
Here's a simple jump sound using Web Audio: ```javascript // Create a new audio context const audioContext = new AudioContext(); // Define the jump sound parameters const jumpSound = { frequency: 220, // A mid-range frequency for a jump sound attack: 0.01, // Fast attack to mimic the suddenness of a jump decay: 0.1, // Short decay to create a sharp sound sustain: 0, // No sustain to make the sound fade out quickly release: 0.1, // Short release to create a sharp sound volume: 0.5, // A moderate volume for the jump sound }; // Create an oscillator to generate the sound const oscillator = audioContext.createOscillator(); oscillator.type = 'sine'; // Use a sine wave to create a sharp sound oscillator.frequency.value = jumpSound.frequency; // Set the frequency oscillator.connect(audioContext.destination); // Connect the oscillator to the destination // Create a gain node to control the volume const gain = audioContext.createGain(); gain.gain.value = jumpSound.volume; // Set the initial volume oscillator.connect(gain); // Connect the oscillator to the gain node gain.connect(audioContext.destination); // Connect the gain node to the destination // Create a filter to add a sharp high-pass to the sound const filter = audioContext.createBiquadFilter(); filter.type = 'highpass'; // Use a high-pass filter filter.frequency.value = 200; // Set the cutoff frequency to 200 Hz gain.connect(filter); // Connect the gain node to the filter filter.connect(audioContext.destination); // Connect the filter to the destination // Play the jump sound oscillator.start(); setTimeout(() => { oscillator.stop(); }, 0.2); // Stop the oscillator after 0.2 seconds ``` This code creates a simple jump sound using a sine wave oscillator with a frequency of 220 Hz. The sound has a fast attack and decay, and a short release, which creates a sharp and sudden sound. The volume is set to a moderate level, and a high-pass filter is added to enhance the high-frequency content of the sound. You can adjust the parameters to create different types of jump sounds. For example, you can increase the frequency to create a higher-pitched sound, or decrease the attack and decay times to
Before: what “Base model, bare prompt” made

Refused. The synthesizer’s meter (learn/public/tinysound.js).

  • Fail No JSON object in the reply.
The model’s reply, as written
*BOOP*

Editor’s note

Mixed. The sound-design skill alone: a Web Audio program in JavaScript, with an envelope and a filter. More of a sound than “*BOOP*”, and still not one the synthesizer can play.

What changed in the input

+7,988 −0 characters

Added: 7,988 characters of teaching (show)System: Follow this skill: --- name: sound-design description: Sound design and mixing for slopped.site's Web Audio - synthesis recipes (pads, bells, formant choir, noise beds for water and rain, whooshes, game SFX), routing (filters, reverb, delay, compressor), level and spectrum targets, browser rules (user gesture, tab visibility, node budget), and listen.mjs, which records a page's audio to WAV and measures loudness, peaks, brightness and stereo. Use when making or changing any sound on the sites (Scroll's ambient.js, SK8's SFX), when something sounds muddy, harsh, quiet, clicky or too loud, or before shipping audio. --- # Sound design for slopped.site All audio is synthesised with Web Audio: no files. Scroll's ambient music is `scroll/public/ambient.js`; SK8's sound effects are the `SFX` table in `games/public/sk8/index.html` (see game-feel for which verb gets which sound). Composition (notes, chords, form) is the music-composition skill; this one is about *how things sound* and *how they sit together*. ## You can't hear it: measure it ```sh node .claude/skills/sound-design/scripts/listen.mjs scroll spa 20 [out-dir] # one mood, 20 s node .claude/skills/sound-design/scripts/listen.mjs scroll choir 16 [out-dir] --scroll # while falling fast node .claude/skills/sound-design/scripts/listen.mjs scroll brown 16 # a noise on its own node .claude/skills/sound-design/scripts/listen.mjs scroll spa+brown 16 # music over a noise ``` It serves the site, starts the sound in headless Chrome (autoplay allowed), records the master output, writes a **WAV the user can play**, and prints: | Measure | Target for ambient beds | What it catches | | --- | --- | --- | | RMS loudness | about −24 dBFS, moods within ±2 dB of each other | too quiet, too loud, jumps between moods | | Peak / clipped samples | peak below −6 dBFS, 0 clipped | distortion | | Crest factor | 10–16 dB | squashed (low) or spiky (high) | | Per-2 s RMS | steady after the fade-in (±3 dB) | pumping, dropouts, runaway build-ups | | Spectral centroid | 300–1000 Hz warm ambient; rain/noise moods higher | dull (low) or harsh (high) | | Energy below 200 Hz | under ~55% | mud from drones, low noise and pad roots | | Energy above 4 kHz | a few % (more for rain) | no "air", or hiss | | L/R correlation | 0.1–0.7 | mono (1) or phase problems (below 0) | | Longest silence | under ~1 s after the fade-in | something not playing | The first pass of Scroll's moods measured 68–89% of energy below 200 Hz and nothing above 4 kHz: the drones were too loud and a shared lowpass was muffling the bells. **Always run it after changing a sound, and report the numbers plus the WAV path; never say a sound is good from code alone.** ## Recipes (all in ambient.js) - **Pad**: two sawtooths per note detuned ±6–7 cents (chorus), root as a triangle, slow attack (35% of the chord), through a dark lowpass (~1.5 kHz) and a highpass (~110 Hz) so it sits above the drone. Pan the notes across the field. - **Bell / chime**: sine fundamental plus inharmonic partials (×2.76 at 25%, ×5.4 at 6%), 10 ms attack, exponential decay ~3.5 s, into a feedback delay (~0.48 s, feedback 0.42, lowpassed) and the reverb. Keep bells out of the dark pad filter or they lose their shimmer. - **Choir "aah/ooh"**: sawtooth with ~5 Hz vibrato (0.6% depth) through three bandpass formants (ah 800/1150/2900 Hz, oo 350/800/2400 Hz, Q 9/7/5, levels 1/0.55/0.2), the formants gliding from one vowel to the other over the note. Several staggered voices make a choir. - **Ocean**: brown noise, band-limited 160–1300 Hz (surf, not rumble), amplitude swelling with a ~0.09 Hz LFO. - **Rain**: white noise band-limited 1.4–5.2 kHz, plus occasional droplets (a sine sweeping down from 2–3.4 kHz in 60 ms). - **Drone**: root and fifth very low (D2 + A2), quiet (0.012–0.016): felt more than heard. - **Noise layer** (`NOISES`, its own gain `noiseOut`, never reacting to scroll): **white** (lowpassed at 12 kHz so it isn't a hiss), **pink** (Paul Kellet's filter on white noise, −3 dB/octave), **brown** (leaky integrator; highpass 30 Hz), **waves** (two brown-noise swells on different LFOs, 0.075 and 0.11 Hz, panned apart, so the surf never lines up the same way), **rainfall** (white noise 0.9–6 kHz plus a brown "roof" rumble under 500 Hz). Steady-state targets: about −26 to −29 dBFS. Steady noise feels louder than music at the same RMS, so white sits lowest. - **Noise loops** are 4–8 s stereo buffers, different per ear, with the last 0.25 s crossfaded into the start: brown noise drifts, so a plain loop clicks at the seam. - **Whoosh** (speed): looped white noise through a bandpass (~900 Hz), level ∝ speed². - **Reverb**: a convolver with generated stereo noise decaying as (1 − t)³ over ~7 s. Send 0.55–0.75 for ambient. - **Master**: music (`out`) and noise (`noiseOut`) → `master` (the Volume slider, squared so it follows perceived loudness) → highpass 45 Hz (no sub rumble on laptop speakers) → compressor (−18 dB threshold, 3:1) → output. Each mood has a `trim` that level-matches it; `listen.mjs` measures after the master, at volume 100. - **Mud check for low moods**: dropping a whole pad an octave puts its root near 50 Hz (Night first measured 74% below 200 Hz). Keep the pad in place and darken it with a lower `padCut` instead; a high `trim` on a sparse mood (Glass) also boosts its drone, so cut the drone when you raise the trim. | Sound | Measured RMS (steady) | Below 200 Hz | | --- | --- | --- | | Spa / Choir / Rain | −24 to −25 dBFS | 37–44% | | Night / Glass | about −25.5 / −26.5 dBFS | 36% / 37% | | White / Pink / Brown / Waves / Rainfall | −29 / −27 / −26 / −28 (swelling) / −27 dBFS | — | ## Rules - **Fades, not switches.** Every gain change ramps (`linearRampToValueAtTime` / `setTargetAtTime`), and every ramp starts from an explicit `setValueAtTime` (a ramp with no earlier event can jump). Stop sources after their fade, never mid-sound: clicks. - **User gesture first.** Browsers start audio only from a click or key. A remembered "sound on" waits for the first pointerdown or keydown. Default is off. - **Tab hidden → `ctx.suspend()`**, visible → resume if on. - **Node budget.** Create short-lived nodes per note and let them stop; keep long-running beds few (≤ 6). Schedule ahead with a ~200 ms timer and a 0.5 s look-ahead rather than per frame. - **Mix by role.** Bed (noise, drone) lowest, pad under decorations, decorations (bells, voice lines) on top but sparse. If something must be heard, make it *brighter* or *sparser* before making it louder. - **SK8 effects** are short and quiet because they layer constantly. Pitch rises with achievement (multiplier ticks, bank arpeggios). Everything goes through `sfx()`, so `M` mutes all of it. ## See it (for reviewers who can't hear) `python3 .claude/skills/sound-design/scripts/see.py <file.wav ...> [--out dir]` turns WAVs (from listen.mjs, or a game's own `listen.mjs`) into `<name>.see.png` plus a line of numbers each: - **The image:** loudness over time (RMS and peak, with clipping marked), a log-frequency spectrogram, and a piano roll of the strongest notes (C2–C7). - **The numbers:** peak/RMS dBFS, crest, clipped samples, low-end share under 200 Hz, centroid, a key estimate, a tempo estimate, and the loop seam (judged only when the file doesn't start or end in silence). It flags clipping, under 1 dB of headroom, more than 60% of the energy below 200 Hz (muddy on laptops, gone on phone speakers), and loop seams that click or jump in level. **Look at the PNG:** - a tune is a readable line in the piano roll; - an empty band between bass and melody is a hollow mix; - a smear under 300 Hz is mud; - vertical stripes are clicks. First run (2026-10-14, Always On's 12 songs): five had 62–75% of their energy under 200 Hz, and the battle theme's piano roll showed melody at C6–C7 and bass at C2–C3 with nothing in the middle octaves. Loudness numbers alone had passed all of them.
User: Make a jump sound.
How it was made: 1 call, 26.8 neurons
  1. answer @cf/meta/llama-3.1-8b-instruct-fp8-fast

    max_tokens 500, temperature 0.6, seed 7 · 2026-10-14 21:57 UTC · 3.1 s · 26.8 neurons · cache e8d6da767c0f

    System text (7,980 characters)
    Follow this skill:
    
    ---
    name: sound-design
    description: Sound design and mixing for slopped.site's Web Audio - synthesis recipes (pads, bells, formant choir, noise beds for water and rain, whooshes, game SFX), routing (filters, reverb, delay, compressor), level and spectrum targets, browser rules (user gesture, tab visibility, node budget), and listen.mjs, which records a page's audio to WAV and measures loudness, peaks, brightness and stereo. Use when making or changing any sound on the sites (Scroll's ambient.js, SK8's SFX), when something sounds muddy, harsh, quiet, clicky or too loud, or before shipping audio.
    ---
    
    # Sound design for slopped.site
    
    All audio is synthesised with Web Audio: no files. Scroll's ambient music is `scroll/public/ambient.js`; SK8's sound effects are the `SFX` table in `games/public/sk8/index.html` (see game-feel for which verb gets which sound). Composition (notes, chords, form) is the music-composition skill; this one is about *how things sound* and *how they sit together*.
    
    ## You can't hear it: measure it
    
    ```sh
    node .claude/skills/sound-design/scripts/listen.mjs scroll spa 20 [out-dir]            # one mood, 20 s
    node .claude/skills/sound-design/scripts/listen.mjs scroll choir 16 [out-dir] --scroll # while falling fast
    node .claude/skills/sound-design/scripts/listen.mjs scroll brown 16                     # a noise on its own
    node .claude/skills/sound-design/scripts/listen.mjs scroll spa+brown 16                 # music over a noise
    ```
    
    It serves the site, starts the sound in headless Chrome (autoplay allowed), records the master output, writes a **WAV the user can play**, and prints:
    
    | Measure | Target for ambient beds | What it catches |
    | --- | --- | --- |
    | RMS loudness | about −24 dBFS, moods within ±2 dB of each other | too quiet, too loud, jumps between moods |
    | Peak / clipped samples | peak below −6 dBFS, 0 clipped | distortion |
    | Crest factor | 10–16 dB | squashed (low) or spiky (high) |
    | Per-2 s RMS | steady after the fade-in (±3 dB) | pumping, dropouts, runaway build-ups |
    | Spectral centroid | 300–1000 Hz warm ambient; rain/noise moods higher | dull (low) or harsh (high) |
    | Energy below 200 Hz | under ~55% | mud from drones, low noise and pad roots |
    | Energy above 4 kHz | a few % (more for rain) | no "air", or hiss |
    | L/R correlation | 0.1–0.7 | mono (1) or phase problems (below 0) |
    | Longest silence | under ~1 s after the fade-in | something not playing |
    
    The first pass of Scroll's moods measured 68–89% of energy below 200 Hz and nothing above 4 kHz: the drones were too loud and a shared lowpass was muffling the bells. **Always run it after changing a sound, and report the numbers plus the WAV path; never say a sound is good from code alone.**
    
    ## Recipes (all in ambient.js)
    
    - **Pad**: two sawtooths per note detuned ±6–7 cents (chorus), root as a triangle, slow attack (35% of the chord), through a dark lowpass (~1.5 kHz) and a highpass (~110 Hz) so it sits above the drone. Pan the notes across the field.
    - **Bell / chime**: sine fundamental plus inharmonic partials (×2.76 at 25%, ×5.4 at 6%), 10 ms attack, exponential decay ~3.5 s, into a feedback delay (~0.48 s, feedback 0.42, lowpassed) and the reverb. Keep bells out of the dark pad filter or they lose their shimmer.
    - **Choir "aah/ooh"**: sawtooth with ~5 Hz vibrato (0.6% depth) through three bandpass formants (ah 800/1150/2900 Hz, oo 350/800/2400 Hz, Q 9/7/5, levels 1/0.55/0.2), the formants gliding from one vowel to the other over the note. Several staggered voices make a choir.
    - **Ocean**: brown noise, band-limited 160–1300 Hz (surf, not rumble), amplitude swelling with a ~0.09 Hz LFO.
    - **Rain**: white noise band-limited 1.4–5.2 kHz, plus occasional droplets (a sine sweeping down from 2–3.4 kHz in 60 ms).
    - **Drone**: root and fifth very low (D2 + A2), quiet (0.012–0.016): felt more than heard.
    - **Noise layer** (`NOISES`, its own gain `noiseOut`, never reacting to scroll): **white** (lowpassed at 12 kHz so it isn't a hiss), **pink** (Paul Kellet's filter on white noise, −3 dB/octave), **brown** (leaky integrator; highpass 30 Hz), **waves** (two brown-noise swells on different LFOs, 0.075 and 0.11 Hz, panned apart, so the surf never lines up the same way), **rainfall** (white noise 0.9–6 kHz plus a brown "roof" rumble under 500 Hz). Steady-state targets: about −26 to −29 dBFS. Steady noise feels louder than music at the same RMS, so white sits lowest.
    - **Noise loops** are 4–8 s stereo buffers, different per ear, with the last 0.25 s crossfaded into the start: brown noise drifts, so a plain loop clicks at the seam.
    - **Whoosh** (speed): looped white noise through a bandpass (~900 Hz), level ∝ speed².
    - **Reverb**: a convolver with generated stereo noise decaying as (1 − t)³ over ~7 s. Send 0.55–0.75 for ambient.
    - **Master**: music (`out`) and noise (`noiseOut`) → `master` (the Volume slider, squared so it follows perceived loudness) → highpass 45 Hz (no sub rumble on laptop speakers) → compressor (−18 dB threshold, 3:1) → output. Each mood has a `trim` that level-matches it; `listen.mjs` measures after the master, at volume 100.
    - **Mud check for low moods**: dropping a whole pad an octave puts its root near 50 Hz (Night first measured 74% below 200 Hz). Keep the pad in place and darken it with a lower `padCut` instead; a high `trim` on a sparse mood (Glass) also boosts its drone, so cut the drone when you raise the trim.
    
    | Sound | Measured RMS (steady) | Below 200 Hz |
    | --- | --- | --- |
    | Spa / Choir / Rain | −24 to −25 dBFS | 37–44% |
    | Night / Glass | about −25.5 / −26.5 dBFS | 36% / 37% |
    | White / Pink / Brown / Waves / Rainfall | −29 / −27 / −26 / −28 (swelling) / −27 dBFS | — |
    
    ## Rules
    
    - **Fades, not switches.** Every gain change ramps (`linearRampToValueAtTime` / `setTargetAtTime`), and every ramp starts from an explicit `setValueAtTime` (a ramp with no earlier event can jump). Stop sources after their fade, never mid-sound: clicks.
    - **User gesture first.** Browsers start audio only from a click or key. A remembered "sound on" waits for the first pointerdown or keydown. Default is off.
    - **Tab hidden → `ctx.suspend()`**, visible → resume if on.
    - **Node budget.** Create short-lived nodes per note and let them stop; keep long-running beds few (≤ 6). Schedule ahead with a ~200 ms timer and a 0.5 s look-ahead rather than per frame.
    - **Mix by role.** Bed (noise, drone) lowest, pad under decorations, decorations (bells, voice lines) on top but sparse. If something must be heard, make it *brighter* or *sparser* before making it louder.
    - **SK8 effects** are short and quiet because they layer constantly. Pitch rises with achievement (multiplier ticks, bank arpeggios). Everything goes through `sfx()`, so `M` mutes all of it.
    
    ## See it (for reviewers who can't hear)
    
    `python3 .claude/skills/sound-design/scripts/see.py <file.wav ...> [--out dir]` turns WAVs (from listen.mjs, or a game's own `listen.mjs`) into `<name>.see.png` plus a line of numbers each:
    - **The image:** loudness over time (RMS and peak, with clipping marked), a log-frequency spectrogram, and a piano roll of the strongest notes (C2–C7).
    - **The numbers:** peak/RMS dBFS, crest, clipped samples, low-end share under 200 Hz, centroid, a key estimate, a tempo estimate, and the loop seam (judged only when the file doesn't start or end in silence).
    
    It flags clipping, under 1 dB of headroom, more than 60% of the energy below 200 Hz (muddy on laptops, gone on phone speakers), and loop seams that click or jump in level. **Look at the PNG:**
    - a tune is a readable line in the piano roll;
    - an empty band between bass and melody is a hollow mix;
    - a smear under 300 Hz is mud;
    - vertical stripes are clicks.
    
    First run (2026-10-14, Always On's 12 songs): five had 62–75% of their energy under 200 Hz, and the battle theme's piano roll showed melody at C6–C7 and bass at C2–C3 with nothing in the middle octaves. Loudness numbers alone had passed all of them.

    User

    Make a jump sound.

    Output

    Here's a simple jump sound using Web Audio:
    
    ```javascript
    // Create a new audio context
    const audioContext = new AudioContext();
    
    // Define the jump sound parameters
    const jumpSound = {
      frequency: 220, // A mid-range frequency for a jump sound
      attack: 0.01, // Fast attack to mimic the suddenness of a jump
      decay: 0.1, // Short decay to create a sharp sound
      sustain: 0, // No sustain to make the sound fade out quickly
      release: 0.1, // Short release to create a sharp sound
      volume: 0.5, // A moderate volume for the jump sound
    };
    
    // Create an oscillator to generate the sound
    const oscillator = audioContext.createOscillator();
    oscillator.type = 'sine'; // Use a sine wave to create a sharp sound
    oscillator.frequency.value = jumpSound.frequency; // Set the frequency
    oscillator.connect(audioContext.destination); // Connect the oscillator to the destination
    
    // Create a gain node to control the volume
    const gain = audioContext.createGain();
    gain.gain.value = jumpSound.volume; // Set the initial volume
    oscillator.connect(gain); // Connect the oscillator to the gain node
    gain.connect(audioContext.destination); // Connect the gain node to the destination
    
    // Create a filter to add a sharp high-pass to the sound
    const filter = audioContext.createBiquadFilter();
    filter.type = 'highpass'; // Use a high-pass filter
    filter.frequency.value = 200; // Set the cutoff frequency to 200 Hz
    gain.connect(filter); // Connect the gain node to the filter
    filter.connect(audioContext.destination); // Connect the filter to the destination
    
    // Play the jump sound
    oscillator.start();
    setTimeout(() => {
      oscillator.stop();
    }, 0.2); // Stop the oscillator after 0.2 seconds
    ```
    
    This code creates a simple jump sound using a sine wave oscillator with a frequency of 220 Hz. The sound has a fast attack and decay, and a short release, which creates a sharp and sudden sound. The volume is set to a moderate level, and a high-pass filter is added to enhance the high-frequency content of the sound.
    
    You can adjust the parameters to create different types of jump sounds. For example, you can increase the frequency to create a higher-pitched sound, or decrease the attack and decay times to