About

오디오 테스트는 화면 말고 AnalyserNode 로 — 백그라운드 탭에서 17개가 전부 「무음」 으로 나온 이유

그림은 멈춰도 소리는 난다. 피크 값을 재면 클리핑까지 잡힌다

오디오 코드는 화면이 아니라 AnalyserNode 로 검사한다. 백그라운드 탭에서는 그림이 멈춰도 소리는 난다. 그리고 피크 값을 재면 「소리가 나나」 뿐 아니라 「깨지나(클리핑)」 까지 잡힌다. 이 글의 스니펫 열 줄이면 오디오 데모를 자동으로 검사할 수 있다.

17개가 전부 무음이었다

학습용 앱에 실행 상자 17개를 만들고, 전부 소리를 내는지 브라우저 자동화로 확인하려 했다. 처음엔 화면의 오실로스코프 캔버스를 읽어서 「파형이 그려졌나」 로 판단했다. 결과는 17개 전부 무음. 그런데 손으로 누르면 다 소리가 났다.

자동화가 띄운 탭은 document.visibilityState = "hidden", 즉 백그라운드 탭이었다. 거기서는 이런 일이 생긴다.

백그라운드 탭에서
requestAnimationFrame 멈춤 → 캔버스 오실로스코프가 안 그려짐
Turbo.visit 화면 교체가 rAF 에 걸려 있어 turbo:load 가 안 온다
Tone.getDraw().schedule rAF 기반이라 실행 안 됨
setInterval 1초 단위로 늦춰짐
AudioContext 계속 돈다. 소리는 난다

그림을 그리는 쪽만 멈추고, 소리를 만드는 쪽은 멀쩡했다. 화면을 보고 판단하면 틀릴 수밖에 없다.

document.visibilityState = "hidden" AudioContext: running canvas oscilloscope drawn by requestAnimationFrame: paused reads "silent" (wrong) AnalyserNode getFloatTimeDomainData() peak 0.2 = gain 0.2 01.0 silentsound, OK clipping: 1.17, 1.23

검사 코드

페이지가 만드는 analyser 를 가로채서 잡아 두고, 시간 영역 데이터의 절댓값 최댓값을 본다.

const orig = BaseAudioContext.prototype.createAnalyser
BaseAudioContext.prototype.createAnalyser = function () {
  const a = orig.call(this)
  window.__lastAnalyser = a
  return a
}

const peak = () => {
  const buf = new Float32Array(__lastAnalyser.fftSize)
  __lastAnalyser.getFloatTimeDomainData(buf)
  return buf.reduce((m, v) => Math.max(m, Math.abs(v)), 0)
}
// 0 이면 무음, 1 을 넘으면 클리핑

페이지에 analyser 가 없으면 검사용으로 하나 만들어 출력 앞에 끼우면 된다. 페이지 스크립트보다 먼저 이 코드를 주입해야 가로챌 수 있다.

재 보니 나온 것

로컬 Chrome, 백그라운드 탭(2026-09-19). 캔버스 기준으로는 17개 전부 무음(오판)이었다. AnalyserNode 기준으로는 코드에 적은 볼륨이 그대로 나왔다. gain 0.2 → peak 0.2, 0.3 → 0.3, 0.15 → 0.15.

덤으로 클리핑이 3건 나왔다. 귀로 들을 땐 몰랐던 것들이다.

  • 직접 짠 16스텝 드럼 머신: 1.17. 킥 0.8 + 스네어 0.4 + 햇 0.2 가 같은 박에 겹쳤다

  • Tone.js Synth 기본 볼륨(0dB): 0.99. 아슬아슬하다

  • Tone.js 베이스 + 킥: 1.23

전부 볼륨을 내려서 고쳤다. 디지털 오디오에서 ±1 을 넘는 값은 잘려 나가고, 그게 「찌직」 하는 깨짐이다. 소리 여러 개가 같은 순간에 겹치는 드럼은 특히 잘 넘는다.

다른 방법과 비교하면

방법 백그라운드 탭 클리핑 메모
사람이 듣기 – 놓치기 쉽다 0.99 와 1.17 은 귀로 구분이 어렵다
캔버스 파형 읽기 오판 못 잡는다 rAF 가 멈춘다
AnalyserNode 피크 된다 잡는다 페이지에 analyser 가 있거나 끼워야 한다

「소리 기능 E2E 테스트」 를 처음 짤 때 스크린샷 비교부터 떠올리기 쉬운데, 오디오는 오디오 쪽에서 재는 게 맞다.

아직 확인 못 한 것

AudioContext 는 스크립트로 버튼을 눌러 만들었는데도 running 이었다. 그 전에 실제 클릭이 한 번 있었기 때문으로 보인다(sticky activation, 한 번 사용자 조작이 있으면 그 뒤로 남는 허가). 실제 클릭 없이도 running 이 되는지, Playwright 헤드리스에서 같은 검사가 되는지, OfflineAudioContext 로 렌더해서 CI 에 넣을 수 있는지는 아직이다.

오디오가 들어간 웹 기능을 자동 테스트로 묶고 싶다면 negabaro 에 상담해도 된다. 우리도 이 검사로 실제 버그를 세 개 잡았다.

핵심 포인트

1

백그라운드 탭에서는 rAF 가 멈춰 캔버스 파형으로 판단하면 무음으로 오판한다

2

createAnalyser 를 가로채 getFloatTimeDomainData 의 절댓값 최댓값을 본다

3

피크 0 은 무음, 1 초과는 클리핑 — 드럼처럼 겹치는 소리에서 잘 나온다

사용 사례

오디오 데모 · 효과음이 있는 화면의 자동 점검 믹스 볼륨이 1 을 넘는지 개발 중에 확인
← 이어폰 「왼쪽만」 은 두 가지다 — StereoPanner(옮기기)와 ChannelSplitter(고르기) Strudel 은 비트 스케치엔 최고, 곡 완성엔 약하다 — 그리고 lazy import 하면 이펙트가 조용히 빠진다 →