Звуки в игре без единого аудиофайла
В игре нет ни одного звукового файла. Мяуканье, писк мыши, гром и гул пылесоса собираются прямо в браузере через Web Audio API. Весит это несколько десятков строк, а не мегабайты mp3.
Простой тон
Основа всего — осциллятор и регулятор громкости, который быстро затухает:
function toneAt(freq, dur, type, vol, t) {
var osc = audio.createOscillator();
var gain = audio.createGain();
osc.type = type || 'triangle';
osc.frequency.setValueAtTime(freq, t);
gain.gain.setValueAtTime(vol || 0.05, t);
gain.gain.exponentialRampToValueAtTime(0.0001, t + dur);
osc.connect(gain);
gain.connect(audio.destination);
osc.start(t);
osc.stop(t + dur + 0.02);
}Из таких коротких тонов собраны звуки поимки рыбы, бонусов и проигрыша. Звук пойманной рыбы чуть повышается с ростом множителя серии, поэтому серию слышно, даже не глядя на счёт.
Мяу
Мяуканье — это тон, который плавно едет вверх, а потом вниз:
sweep(520, 860, 0.13, 'triangle', 0.05); // «мя…»
sweep(860, 470, 0.32, 'triangle', 0.05, 0.12); // «…у»Внутри sweep та же схема, только частота меняется через exponentialRampToValueAtTime. Писк мыши устроен так же, просто вдвое выше и короче.
Гром
Гром тоном не сделать, нужен шум. Я один раз заполняю буфер случайными числами, а потом пропускаю его через фильтр низких частот, который постепенно закрывается:
filter.type = 'lowpass';
filter.frequency.setValueAtTime(500, t);
filter.frequency.exponentialRampToValueAtTime(125, t + dur);Короткий резкий щелчок плюс длинный глухой раскат дают вполне правдоподобный удар молнии.
Что важно не забыть
Браузер не даст воспроизвести звук, пока человек не нажал что-нибудь на странице. Поэтому AudioContext создаётся только по нажатию «Играть», а не при загрузке.
Ещё у exponentialRampToValueAtTime нельзя указывать ноль: экспонента до нуля не доходит, и браузер выдаёт ошибку. Поэтому везде стоит 0.0001.