generating-sounds-with-ai
Back to list

raphaelsalaja/generating-sounds-with-ai

Generating Sounds With Ai

GitHub

Audit Web Audio API code for procedural sound synthesis quality, UX decisions, and parameter best practices.

101 installsInteractionTestingFrontend4 demos

Real-world examples

Live HTML demos for this skill — rendered directly in the page. 4 examples.

  1. 01

    Single context & cleanup

    One shared AudioContext, resume when suspended, disconnect nodes onended — vs creating a new context per click and leaking nodes.

  2. 02

    Exponential envelope

    setValueAtTime then exponentialRampToValueAtTime(0.001) for natural decay — A/B against linear ramps and illegal zero targets.

  3. 03

    Filtered noise click

    8ms noise burst through a 4kHz bandpass (Q≈3, gain≤1) — crisp UI tap vs a tonal sine beep that fails design-noise-for-percussion.

  4. 04

    Pitch-sweep confirmation

    Oscillator with exponential frequency sweep for tonal UI confirms — vs a static-frequency beep that ignores design-oscillator-for-tonal.

Skill markdown
# Generating Sounds with AI

Review Web Audio API code for sound synthesis best practices.

## How It Works

1. Read the specified files (or prompt user for files/pattern)
2. Check against all rules below
3. Output findings in `file:line` format

## Rule Categories

| Priority | Category | Prefix |
|----------|----------|--------|
| 1 | Context Management | `context-` |
| 2 | Decay & Envelope | `envelope-` |
| 3 | Sound Design | `design-` |
| 4 | Parameters | `param-` |

## Rules

### Context Management Rules

#### `context-reuse-single`
Reuse a single AudioContext instance; do not create new ones per sound.

**Fail:**
```ts
function playSound() {
  const ctx = new AudioContext();
  // Creates new context every call
}
```

**Pass:**
```ts
let audioContext: AudioContext | null = null;

function getAudioContext(): AudioContext {
  if (!audioContext) {
    audioContext = new AudioContext();
  }
  return audioContext;
}
```

#### `context-resume-suspended`
Check and resume suspended AudioContext before playing.

**Fail:**
```ts
function playSound() {
  const ctx = getAudioContext();
  // Plays immediately without checking state
}
```

**Pass:**
```ts
function playSound() {
  const ctx = getAudioContext();
  if (ctx.state === "suspended") {
    ctx.resume();
  }
}
```

#### `context-cleanup-nodes`
Disconnect and clean up audio nodes after playback.

**Fail:**
```ts
source.start();
// Nodes remain connected after sound ends
```

**Pass:**
```ts
source.start();
source.onended = () => {
  source.disconnect();
  gain.disconnect();
};
```

### Envelope Rules

#### `envelope-exponential-decay`
Use exponential ramps for natural decay, not linear.

**Fail:**
```ts
gain.gain.linearRampToValueAtTime(0, t + 0.05);
```

**Pass:**
```ts
gain.gain.exponentialRampToValueAtTime(0.001, t + 0.05);
```

#### `envelope-no-zero-target`
Exponential ramps cannot target 0; use 0.001 or similar small value.

**Fail:**
```ts
gain.gain.exponentialRampToValueAtTime(0, t + 0.05);
```

**Pass:**
```ts
gain.gain.exponentialRampToValueAtTime(0.001, t + 0.05);
```

#### `envelope-set-initial-value`
Set initial value before ramping to avoid glitches.

**Fail:**
```ts
gain.gain.exponentialRampToValueAtTime(0.001, t + 0.05);
// No setValueAtTime before ramp
```

**Pass:**
```ts
gain.gain.setValueAtTime(0.3, t);
gain.gain.exponentialRampToValueAtTime(0.001, t + 0.05);
```

### Sound Design Rules

#### `design-noise-for-percussion`
Use filtered noise for clicks/taps, not oscillators.

**Fail:**
```ts
// Click sound using sine oscillator
const osc = ctx.createOscillator();
osc.type = "sine";
// Results in tonal "beep" not "click"
```

**Pass:**
```ts
// Click sound using noise burst
const buffer = ctx.createBuffer(1, ctx.sampleRate * 0.008, ctx.sampleRate);
const data = buffer.getChannelData(0);
for (let i = 0; i < data.length; i++) {
  data[i] = (Math.random() * 2 - 1) * Math.exp(-i / 50);
}
```

#### `design-oscillator-for-tonal`
Use oscillators with pitch movement for tonal sounds (pops, confirmations).

**Fail:**
```ts
// Confirmation sound using static frequency
osc.frequency.value = 400;
```

**Pass:**
```ts
// Confirmation sound with pitch sweep
osc.frequency.setValueAtTime(400, t);
osc.frequency.exponentialRampToValueAtTime(600, t + 0.04);
```

#### `design-filter-for-character`
Apply bandpass filter to shape percussive sounds.

**Fail:**
```ts
// Raw noise without filtering
source.connect(gain).connect(ctx.destination);
```

**Pass:**
```ts
const filter = ctx.createBiquadFilter();
filter.type = "bandpass";
filter.frequency.value = 4000;
filter.Q.value = 3;
source.connect(filter).connect(gain).connect(ctx.destination);
```

### Parameter Rules

#### `param-click-duration`
Click/tap sounds should be 5-15ms duration.

**Fail:**
```ts
const buffer = ctx.createBuffer(1, ctx.sampleRate * 0.1, ctx.sampleRate);
// 100ms is too long for a click
```

**Pass:**
```ts
const buffer = ctx.createBuffer(1, ctx.sampleRate * 0.008, ctx.sampleRate);
// 8ms is appropriate for a click
```

#### `param-filter-frequency-range`
Bandpass filter for clicks should be 3000-6000Hz.

**Fail:**
```ts
filter.frequency.value = 500; // Too low, sounds muffled
```

**Pass:**
```ts
filter.frequency.value = 4000; // Crisp, present
```

#### `param-reasonable-gain`
Gain values should not exceed 1.0 to prevent clipping.

**Fail:**
```ts
gain.gain.setValueAtTime(1.5, t);
```

**Pass:**
```ts
gain.gain.setValueAtTime(0.3, t);
```

#### `param-q-value-range`
Filter Q for clicks should be 2-5 for focused but not harsh sound.

**Fail:**
```ts
filter.Q.value = 15; // Too resonant, harsh
```

**Pass:**
```ts
filter.Q.value = 3; // Focused but natural
```

## Output Format

When reviewing files, output findings as:

```
file:line - [rule-id] description of issue

Example:
lib/sounds.ts:23 - [envelope-exponential-decay] Using linearRampToValueAtTime instead of exponential
lib/sounds.ts:45 - [context-reuse-single] Creating new AudioContext on each call
```

## Summary Table

After findings

More from raphaelsalaja