이어폰 「왼쪽만」 은 두 가지다 — StereoPanner(옮기기)와 ChannelSplitter(고르기)
pan: -1 은 오른쪽 소리를 버리지 않고 왼쪽으로 합친다. 실측으로 본 차이
「왼쪽 이어폰에서만 소리 나게」 는 웹페이지에서 충분히 정할 수 있다. 다만 방법이 두 개고 결과가 다르다. StereoPannerNode 의 pan: -1 은 소리 전체를 왼쪽으로 옮기고, ChannelSplitter 는 원본의 왼쪽 채널만 고른다. 이 글을 읽으면 어느 쪽을 써야 하는지, 그리고 왜 「오른쪽 악기 소리가 왼쪽에서 나요」 같은 리포트가 오는지 알 수 있다.
이어폰은 판단하지 않는다
이어폰은 받은 L 을 왼쪽에서, R 을 오른쪽에서 틀 뿐이다. 좌우는 그 앞에서 이미 정해져서 온다. 정해지는 곳은 네 군데다.
| 층 | 누가 정하나 |
|---|---|
| ① 음원 파일 | 녹음 · 믹싱할 때 L/R 에 다른 소리를 넣는다 (스테레오, ASMR) |
| ② 플레이어 · 앱 | 재생하면서 바꾼다. 웹이면 Web Audio API |
| ③ OS | 좌우 밸런스, 모노 오디오 (접근성 설정) |
| ④ 이어폰 | 받은 대로 튼다 |
개발자가 손대는 곳은 ② 다. ③ 은 사용자 설정이라 앱이 못 바꾸고, 오히려 ③ 이 ② 를 덮을 수 있다. 사용자가 모노 오디오를 켜면 OS 가 마지막에 좌우를 섞으니 앱의 「왼쪽만」 은 무력해질 것이다. 이건 아직 macOS 에서 직접 확인하지 못했다.
웹에서 하는 법 두 가지
A. StereoPannerNode — 옮기기
const ctx = new AudioContext()
const src = ctx.createMediaElementSource(videoEl)
const panner = new StereoPannerNode(ctx, { pan: -1 }) // -1 왼쪽, 0 가운데, +1 오른쪽
src.connect(panner).connect(ctx.destination)
B. ChannelSplitter + Merger — 고르기
const splitter = ctx.createChannelSplitter(2)
const merger = ctx.createChannelMerger(2)
src.connect(splitter)
splitter.connect(merger, 0, 0) // 원본 L → 출력 L. 원본 R 은 아무 데도 안 잇는다
merger.connect(ctx.destination)
실측 — 차이는 왼쪽에 880Hz 가 있느냐
L 에만 440Hz, R 에만 880Hz 가 나오는 테스트 신호를 넣고 출력 채널을 분석기로 쟀다. Chrome 152 / macOS, 2026-09-19.
| 출력 L | 출력 R | |
|---|---|---|
| 원본 | 440 | 880 |
| pan = -1 | 440 + 880 | 무음 |
| 왼쪽만 (Splitter) | 440 | 무음 |
| 좌우 교차 | 880 | 440 |
처음엔 pan -1 이면 오른쪽 소리가 그냥 꺼질 줄 알았다. 스펙 계산식을 보면 그렇지 않다. 스테레오 입력에 pan ≤ 0 이면 gainL = cos(x·π/2), outL = inL + inR × gainL 이다(x = pan + 1). pan = -1 이면 gainL = 1 이라 오른쪽 소리가 왼쪽에 통째로 더해진다.
| 하고 싶은 것 | 쓸 것 |
|---|---|
| 음악 전체를 왼쪽 귀로 (한쪽만 꼈을 때) | A. Panner pan: -1 |
| 원본의 왼쪽 채널만 (오른쪽 소리는 버림) | B. Splitter |
| 왼쪽엔 원어, 오른쪽엔 통역 (음원 2개) | 음원마다 Panner, -1 / +1 |
| 좌우 바꾸기 | B. Splitter 에서 0→1, 1→0 교차 연결 |
소리가 아예 안 나는 경우
createMediaElementSource 를 거는 순간 무음이 되는 경우가 있다. 에러도 안 난다.
CORS 헤더 없는 다른 도메인 미디어: 스펙상 교차 출처 오디오는 스크립트에 넘겨지지 않고 무음으로 채워진다. 직접 재현해서 콘솔 문구를 확인한 건 아직이다
DRM 콘텐츠(EME): 보호된 오디오는 Web Audio 로 꺼낼 수 없다
클릭 전에 만든 AudioContext: 자동재생 정책으로
suspended. 클릭 안에서ctx.resume()YouTube iframe: 남의 페이지 안의
<video>라 접근 자체가 안 된다
그래서 「넷플릭스를 왼쪽 귀로만」 같은 건 웹페이지에서 못 한다. OS 밸런스 쪽 얘기가 된다.
곁가지 — 5.1 을 이어폰으로 접으면 LFE 가 사라진다
같은 데모에 채널마다 주파수가 다른 5.1ch WAV 를 넣어 봤다. 센터와 서라운드는 약 -3dB(0.707배)로 양쪽에 섞였고, 저음 전용 LFE 채널은 -141dB, 사실상 버려졌다. 스펙의 speakers 다운믹스 공식 그대로다. 영화 믹싱에서 중요한 저음을 메인 채널에도 넣는 이유가 이거다.
모노 오디오가 접근성 메뉴에 있는 데도 이유가 있다. 한쪽 귀가 안 들리는 사람은 스테레오에서 한쪽 악기를 통째로 놓친다. 왼쪽에만 중요한 정보를 싣는 설계는 피한다.
어학 앱의 좌우 분리 재생이나 이어폰 좌우 테스트 같은 기능을 만들고 있다면 negabaro 에 물어봐도 된다. 위 표는 우리 데모에서 직접 잰 값이다.
핵심 포인트
이어폰은 L/R 을 틀기만 한다 — 좌우는 음원 · 앱 · OS 에서 정해진다
pan: -1 은 오른쪽 소리를 왼쪽으로 합친다 (outL = inL + inR)
원본 왼쪽 채널만 원하면 ChannelSplitter 로 0번만 잇는다
CORS 없는 외부 미디어 · DRM · YouTube iframe 은 Web Audio 로 못 다룬다