Loading...
Loading...
Audit Web Audio API code for sound synthesis best practices. Use when reviewing procedural audio, implementing UI sounds, or checking audio parameter quality. Outputs file:line findings.
npx skill4agent add raphaelsalaja/userinterface-wiki generating-sounds-with-aifile:line| Priority | Category | Prefix |
|---|---|---|
| 1 | Context Management | |
| 2 | Decay & Envelope | |
| 3 | Sound Design | |
| 4 | Parameters | |
context-reuse-singlefunction playSound() {
const ctx = new AudioContext();
// Creates new context every call
}let audioContext: AudioContext | null = null;
function getAudioContext(): AudioContext {
if (!audioContext) {
audioContext = new AudioContext();
}
return audioContext;
}context-resume-suspendedfunction playSound() {
const ctx = getAudioContext();
// Plays immediately without checking state
}function playSound() {
const ctx = getAudioContext();
if (ctx.state === "suspended") {
ctx.resume();
}
}context-cleanup-nodessource.start();
// Nodes remain connected after sound endssource.start();
source.onended = () => {
source.disconnect();
gain.disconnect();
};envelope-exponential-decaygain.gain.linearRampToValueAtTime(0, t + 0.05);gain.gain.exponentialRampToValueAtTime(0.001, t + 0.05);envelope-no-zero-targetgain.gain.exponentialRampToValueAtTime(0, t + 0.05);gain.gain.exponentialRampToValueAtTime(0.001, t + 0.05);envelope-set-initial-valuegain.gain.exponentialRampToValueAtTime(0.001, t + 0.05);
// No setValueAtTime before rampgain.gain.setValueAtTime(0.3, t);
gain.gain.exponentialRampToValueAtTime(0.001, t + 0.05);design-noise-for-percussion// Click sound using sine oscillator
const osc = ctx.createOscillator();
osc.type = "sine";
// Results in tonal "beep" not "click"// 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// Confirmation sound using static frequency
osc.frequency.value = 400;// Confirmation sound with pitch sweep
osc.frequency.setValueAtTime(400, t);
osc.frequency.exponentialRampToValueAtTime(600, t + 0.04);design-filter-for-character// Raw noise without filtering
source.connect(gain).connect(ctx.destination);const filter = ctx.createBiquadFilter();
filter.type = "bandpass";
filter.frequency.value = 4000;
filter.Q.value = 3;
source.connect(filter).connect(gain).connect(ctx.destination);param-click-durationconst buffer = ctx.createBuffer(1, ctx.sampleRate * 0.1, ctx.sampleRate);
// 100ms is too long for a clickconst buffer = ctx.createBuffer(1, ctx.sampleRate * 0.008, ctx.sampleRate);
// 8ms is appropriate for a clickparam-filter-frequency-rangefilter.frequency.value = 500; // Too low, sounds muffledfilter.frequency.value = 4000; // Crisp, presentparam-reasonable-gaingain.gain.setValueAtTime(1.5, t);gain.gain.setValueAtTime(0.3, t);param-q-value-rangefilter.Q.value = 15; // Too resonant, harshfilter.Q.value = 3; // Focused but naturalfile: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| Rule | Count | Severity |
|---|---|---|
| 1 | HIGH |
| 3 | MEDIUM |
| 1 | LOW |
| User Says | Parameter Change |
|---|---|
| "too harsh" | Lower filter frequency, reduce Q |
| "too muffled" | Higher filter frequency |
| "too long" | Shorter duration, faster decay |
| "cuts off abruptly" | Use exponential decay |
| "more mechanical" | Higher Q, faster decay |
| "softer" | Lower gain, triangle wave |