Синхронизация анимации с Web Audio API

Синхронизация Lottie-анимации с аудиопотоком строится на точном сопоставлении временной шкалы Web Audio API и кадровой шкалы Lottie. Ключевым фактором становится единый источник времени, исключающий дрейф между визуальным и аудиальным воспроизведением.

Web Audio API предоставляет монотонную временную ось через AudioContext.currentTime. Lottie Web оперирует кадровой моделью, где анимация выражается через прогресс от 0 до durationFrames. Связка этих двух моделей требует промежуточного слоя, преобразующего секунды в кадры с учётом FPS композиции.


Временная модель Web Audio API

Аудиограф строится вокруг AudioContext, который обеспечивает точное планирование событий воспроизведения:

const audioContext = new AudioContext();
const audioBufferSource = audioContext.createBufferSource();

audioBufferSource.buffer = decodedAudioBuffer;
audioBufferSource.connect(audioContext.destination);

Временная шкала формируется через:

  • audioContext.currentTime — абсолютное время в секундах
  • планируемые события через audioBufferSource.start(when)
  • точное управление задержками воспроизведения

Ключевое свойство: currentTime не зависит от нагрузки UI-потока и остаётся стабильной опорой для синхронизации.


Преобразование аудио-времени в кадры Lottie

Lottie-анимация задаётся количеством кадров и частотой frameRate, полученной из JSON-композиции:

const frameRate = animationData.fr;
const durationFrames = animationData.op - animationData.ip;

Перевод времени в кадры:

[ frame = timeSeconds frameRate]

Практическая формула синхронизации:

const frame = (audioContext.currentTime - startTime) * frameRate;
lottieInstance.goToAndStop(frame, true);

Такой подход обеспечивает линейную привязку аудио-времени к анимации.


Основной цикл синхронизации

Реализация стабильной синхронизации строится на requestAnimationFrame, где каждый кадр UI обновляет позицию анимации на основе аудиовремени:

let startTime = 0;
let isPlaying = false;

function syncLoop() {
  if (!isPlaying) return;

  const elapsed = audioContext.currentTime - startTime;
  const frame = elapsed * frameRate;

  lottieInstance.goToAndStop(frame, true);

  requestAnimationFrame(syncLoop);
}

Ключевой принцип заключается в том, что источник истины — не визуальный цикл, а аудиочас.


Инициализация синхронного запуска

Критический момент синхронизации — старт аудио и анимации без расхождения по времени:

async function play(audioBuffer) {
  const source = audioContext.createBufferSource();
  source.buffer = audioBuffer;

  startTime = audioContext.currentTime + 0.05;

  source.connect(audioContext.destination);
  source.start(startTime);

  lottieInstance.goToAndStop(0, true);

  isPlaying = true;
  requestAnimationFrame(syncLoop);
}

Небольшая задержка позволяет гарантировать одинаковый стартовый момент для всех подсистем.


Обработка смещения и дрейфа

Со временем возможны микросмещения из-за нагрузки рендеринга. Для компенсации применяется периодическая коррекция:

const drift = frame - lottieInstance.currentFrame;

if (Math.abs(drift) > 1) {
  lottieInstance.goToAndStop(frame, true);
}

Подобная коррекция предотвращает накопление ошибки синхронизации.


Анализ аудио через AnalyserNode

Для более сложной синхронизации используется спектральный анализ сигнала:

const analyser = audioContext.createAnalyser();
source.connect(analyser);

const dataArray = new Uint8Array(analyser.frequencyBinCount);

Получение амплитудных данных:

function getVolume() {
  analyser.getByteFrequencyData(dataArray);

  let sum = 0;
  for (let i = 0; i < dataArray.length; i++) {
    sum += dataArray[i];
  }

  return sum / dataArray.length;
}

Амплитуда используется для динамического управления параметрами Lottie-анимации, такими как масштаб, прозрачность или выбор сегмента.


Реакция на аудиопики

Выделение ударов (beats) позволяет формировать событийную модель синхронизации:

let previousVolume = 0;

function detectBeat(volume) {
  const threshold = 20;

  if (volume - previousVolume > threshold) {
    triggerAnimationPulse();
  }

  previousVolume = volume;
}

Такая схема создаёт дискретные триггеры поверх непрерывной анимации.


Сегментное управление анимацией

Lottie поддерживает воспроизведение фрагментов анимации, что удобно для привязки к музыкальным событиям:

lottieInstance.playSegments([10, 30], true);

Сегменты могут соответствовать тактам, фразам или переходам в аудиодорожке.


Привязка музыкальной структуры к анимации

Аудио-композиция разбивается на структурные элементы:

  • такты
  • доли
  • фразы
  • переходы

Каждый элемент отображается в диапазон кадров Lottie:

const beatFrame = frameRate * (60 / bpm);
const barFrames = beatFrame * 4;

Такая модель позволяет строить детерминированную анимацию, основанную на темпе.


Управление скоростью Lottie через аудиоданные

В некоторых сценариях используется динамическое управление скоростью:

const volume = getVolume();

const speed = 1 + volume / 100;
lottieInstance.setSpeed(speed);

Однако данный подход применяется ограниченно, так как нарушает строгую временную синхронизацию и может вызывать дрейф.


Комбинированная модель синхронизации

Наиболее стабильная архитектура объединяет три слоя:

  1. Web Audio Time Layer

    • источник абсолютного времени
  2. Frame Mapping Layer

    • преобразование секунд в кадры
  3. Reactive Layer

    • анализ аудио и событийные триггеры
const time = audioContext.currentTime - startTime;
const frame = time * frameRate;

lottieInstance.goToAndStop(frame, true);

const volume = getVolume();
detectBeat(volume);

Буферизация и точность воспроизведения

Для минимизации рассинхронизации используется предварительная загрузка и декодирование:

const response = await fetch(url);
const arrayBuffer = await response.arrayBuffer();
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);

Декодированный буфер позволяет избежать задержек во время воспроизведения, которые критичны для синхронизации с анимацией.


Итоговая модель поведения системы

Система синхронизации формируется как потоковое отображение времени аудио на визуальную ось кадров, где:

  • Web Audio API обеспечивает стабильный тайминг
  • Lottie Web выполняет кадровую визуализацию
  • requestAnimationFrame обеспечивает частоту обновления
  • AnalyserNode добавляет реактивный слой управления

Такая архитектура исключает зависимость от кадровой частоты интерфейса и позволяет сохранять устойчивую синхронизацию даже при высокой нагрузке рендеринга.