JavaScript Web Audio API
Web Audio API в JavaScript: аудиограф из AudioContext и AudioNode, загрузка и воспроизведение звуков, громкость и генерация тонов.
Web Audio API позволяет генерировать, загружать, обрабатывать и воспроизводить звук прямо в браузере, без каких-либо плагинов. В этом руководстве разбираются основные строительные блоки API: как создать аудиоконтекст, загрузить и воспроизвести звуковые файлы, управлять громкостью, генерировать тона с помощью осциллятора и обойти политику автовоспроизведения в браузерах. Каждый пример небольшой и самодостаточный, так что его можно вставить на страницу и сразу услышать результат.
Введение в Web Audio API
Web Audio API — это JavaScript API для обработки и синтеза звука в веб-приложениях. В отличие от обычного элемента <audio>, который просто проигрывает файл от начала до конца, Web Audio API предоставляет граф связанных узлов, которые в реальном времени маршрутизируют, смешивают и преобразуют звук. Поэтому он подходит для игр, музыкальных приложений, синтезаторов и аудиовизуализаторов.
Мыслим аудиографами
Любой звук в Web Audio API проходит по цепочке узлов — от источника до получателя (destination):
source → (effects) → destination
(file, (gain, (your
oscillator) filter) speakers)Три понятия, с которых стоит начать:
- AudioContext — контейнер, которому принадлежит весь граф и аудиочасы. Вы создаёте один контекст на страницу и переиспользуете его.
- AudioNode — модульная единица обработки. Узлы-источники производят звук (буфер или осциллятор), узлы-эффекты преобразуют его (gain, filter, panner), а узел destination отправляет его на динамики.
- AudioBuffer — декодированные аудиоданные в памяти. Идеально подходят для коротких звуков (эффектов, сэмплов), которые нужно запускать многократно и с точным таймингом.
Граф строится вызовами node.connect(otherNode). Звук всегда заканчивается в audioContext.destination.
Начало работы с Web Audio API
Чтобы работать со звуком, сначала создайте AudioContext — контейнер для вашего аудиографа.
Создание AudioContext
const audioContext = new (window.AudioContext || window.webkitAudioContext)();Этот код инициализирует новый AudioContext. Запасной вариант webkit нужен для старых версий Safari; современные браузеры напрямую поддерживают AudioContext без префикса.
Подвох политики автовоспроизведения. Браузеры создают контекст в состоянии
"suspended"и отказываются воспроизводить звук, пока пользователь не взаимодействует со страницей (клик или нажатие клавиши). Всегда возобновляйте контекст внутри обработчика события, прежде чем играть звук:button.addEventListener("click", async () => { if (audioContext.state === "suspended") { await audioContext.resume(); } // ...now it is safe to play });Если звука нет, а в консоли нет ошибок, чаще всего причина в невозобновлённом контексте.
Загрузка и воспроизведение аудио
Чтобы воспроизвести звуковой файл, загрузите его в AudioBuffer, а затем подключите к контексту узел-источник.
Загрузка аудиофайлов
Здесь используются Fetch API и async/await: файл скачивается и декодируется:
async function loadAudioFile(url, audioContext) {
const response = await fetch(url);
const arrayBuffer = await response.arrayBuffer();
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
return audioBuffer;
}Функция загружает файл, читает его как «сырой» ArrayBuffer и передаёт в decodeAudioData, который распаковывает такие форматы, как MP3, WAV или OGG, в готовый к воспроизведению AudioBuffer. Декодирование выполняется вне основного потока и возвращает промис, поэтому его нужно дождаться через await.
Воспроизведение загруженного аудио
function playAudio(audioBuffer, audioContext) {
const sourceNode = audioContext.createBufferSource();
sourceNode.buffer = audioBuffer;
sourceNode.connect(audioContext.destination);
sourceNode.start();
}Код создаёт BufferSourceNode, указывает ему декодированный буфер, подключает его к динамикам (audioContext.destination) и запускает воспроизведение.
Узел-источник одноразовый.
AudioBufferSourceNodeможно запустить только один раз — после остановки для повторного воспроизведения того же буфера нужно создать новый узел. СамAudioBufferпригоден для повторного использования, поэтому храните буфер и каждый раз пересоздавайте дешёвый узел-источник:// ❌ Throws after the first play: "cannot call start more than once" // sourceNode.start(); // sourceNode.start(); // ✅ One fresh source node per playback function playOnce(buffer, ctx) { const src = ctx.createBufferSource(); src.buffer = buffer; src.connect(ctx.destination); src.start(); }
Обработка звука
Web Audio API раскрывается в полную силу, когда между источником и получателем вставляются узлы-эффекты. Самый распространённый из них — узел gain для управления громкостью.
Создание узла Gain
function createGainNode(audioContext, volume) {
const gainNode = audioContext.createGain();
gainNode.gain.value = volume; // 1 = full volume, 0 = silent, 0.5 = half
return gainNode;
}GainNode умножает сигнал на значение gain. Свойство gain — это AudioParam, поэтому вместо резкой установки значения можно запланировать плавное изменение во времени (для затуханий и нарастаний):
// Fade from silent to full volume over 2 seconds, avoiding clicks/pops
gainNode.gain.setValueAtTime(0, audioContext.currentTime);
gainNode.gain.linearRampToValueAtTime(1, audioContext.currentTime + 2);Соединение узлов для аудиоэффектов
function applyEffects(audioBuffer, volume, audioContext) {
const sourceNode = audioContext.createBufferSource();
sourceNode.buffer = audioBuffer;
const gainNode = createGainNode(audioContext, volume);
// source → gain → destination
sourceNode.connect(gainNode);
gainNode.connect(audioContext.destination);
sourceNode.start();
}Так строится граф из трёх узлов: источник подаёт сигнал на узел gain, а узел gain — на получателя. Другие узлы-эффекты добавляются так же — например, BiquadFilterNode для формирования тембра:
const filter = audioContext.createBiquadFilter();
filter.type = "lowpass"; // let low frequencies through, cut highs
filter.frequency.value = 800; // cutoff in Hz
sourceNode.connect(filter);
filter.connect(gainNode);
gainNode.connect(audioContext.destination);Порядок имеет значение: сигнал идёт в том направлении, в котором вы вызываете connect.
Рабочий пример: генератор тона
Для самодостаточной демонстрации, которой не нужен аудиофайл, вот генератор тона на основе осциллятора. Он также учитывает политику автовоспроизведения, возобновляя контекст при первом клике.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Simple Tone Generator</title>
</head>
<body>
<h1>Simple Tone Generator</h1>
<button onclick="playTone()">Play Tone</button>
<button onclick="stopTone()">Stop Tone</button>
<script>
let audioContext;
let oscillator;
async function playTone() {
if (!audioContext) {
audioContext = new (window.AudioContext ||
window.webkitAudioContext)();
}
// Satisfy the autoplay policy: resume on user gesture
if (audioContext.state === "suspended") {
await audioContext.resume();
}
if (!oscillator) {
oscillator = audioContext.createOscillator();
oscillator.type = "sine"; // Sine wave — other values are 'square', 'sawtooth', 'triangle'
oscillator.frequency.setValueAtTime(440, audioContext.currentTime); // A4 note, 440 Hz
oscillator.connect(audioContext.destination);
oscillator.start();
}
}
function stopTone() {
if (oscillator) {
oscillator.stop();
oscillator.disconnect();
oscillator = null; // Reset the oscillator to allow a new one to be created next time
}
}
</script>
</body>
</html>Как это работает:
- Узел осциллятора — генерирует чистую синусоиду частотой 440 Гц (нота ля первой октавы, A4, по которой настраивают инструменты). Другие значения
type—"square","sawtooth"и"triangle", и у каждого свой тембр. - Управляющие функции —
playToneвозобновляет контекст и запускает осциллятор;stopToneостанавливает его, отключает и сбрасывает переменную, чтобы в следующий раз можно было создать новый осциллятор. - Зачем сбрасывать
oscillator = null? Как и узел-источник буфера, осциллятор одноразовый: после остановки его нельзя перезапустить, поэтому для следующего тона создаётся новый.
Когда использовать Web Audio API
| Сценарий | Что выбрать |
|---|---|
| Воспроизвести одну фоновую композицию или подкаст | Обычный элемент <audio> — проще и меньше кода |
| Звуковые эффекты в игре с точным таймингом | Web Audio API + AudioBuffer |
| Управление громкостью, плавные переходы, фильтры, панорамирование | Узлы gain/filter/panner из Web Audio API |
| Синтезаторы и сгенерированные тона | OscillatorNode из Web Audio API |
| Визуализаторы формы волны и частот в реальном времени | AnalyserNode из Web Audio API |
Если нужны только запуск и остановка воспроизведения, достаточно элемента <audio>. Выбирайте Web Audio API, как только вам понадобится обработка, точный тайминг или синтез.
Заключение
Web Audio API превращает браузер в программируемый микшерный пульт: вы соединяете граф узлов от источника через эффекты к динамикам и управляете каждым этапом в реальном времени. Запомните главное:
- Создавайте один
AudioContextи возобновляйте его по действию пользователя перед воспроизведением. - Переиспользуйте
AudioBuffer, но для каждого воспроизведения создавайте новый узел-источник или осциллятор. - Стройте эффекты, соединяя узлы через
connect()и всегда завершая цепочку вaudioContext.destination.
Дальше можно изучить другие возможности браузера, например Web MIDI API для подключения музыкальных инструментов или Web Animations API для синхронизации визуальных эффектов со звуком.