"Left Ear Only" Means Two Things — StereoPanner Moves, ChannelSplitter Picks
pan: -1 doesn't drop the right channel, it folds it into the left. Measured
You can make sound come out of only the left earphone from a web page. The catch is there are two ways and they behave differently. StereoPannerNode with pan: -1 moves the whole sound to the left; ChannelSplitter picks the original left channel. After this you'll know which one you need, and why you get bug reports like "the instrument on the right is playing in my left ear."
Earphones don't decide anything
Earphones play L on the left and R on the right. Left and right are decided before that, in one of four places.
| Layer | Who decides |
|---|---|
| ① Media file | Recording and mixing put different sounds in L/R (stereo, ASMR) |
| ② Player / app | Changes it during playback. On the web, the Web Audio API |
| ③ OS | Balance, mono audio (accessibility) |
| ④ Earphones | Play what they get |
Developers work at ②. ③ is the user's setting; apps can't change it, and it can override ②. If the user turns on mono audio, the OS mixes left and right at the very end, so the app's "left only" should stop working. I haven't confirmed that on macOS yet.
Two ways to do it on the web
A. StereoPannerNode: move
const ctx = new AudioContext()
const src = ctx.createMediaElementSource(videoEl)
const panner = new StereoPannerNode(ctx, { pan: -1 }) // -1 left, 0 center, +1 right
src.connect(panner).connect(ctx.destination)
B. ChannelSplitter + Merger: pick
const splitter = ctx.createChannelSplitter(2)
const merger = ctx.createChannelMerger(2)
src.connect(splitter)
splitter.connect(merger, 0, 0) // source L → output L. Source R goes nowhere
merger.connect(ctx.destination)
Measured: the difference is whether the left has 880Hz
I fed a test signal with 440Hz only on L and 880Hz only on R, and measured the output channels with an analyser. Chrome 152 / macOS, 2026-09-19.
| Output L | Output R | |
|---|---|---|
| Source | 440 | 880 |
| pan = -1 | 440 + 880 | silent |
| Left only (Splitter) | 440 | silent |
| Swapped | 880 | 440 |
I first assumed pan -1 would just switch the right side off. The spec formula says otherwise. For stereo input with pan ≤ 0, gainL = cos(x·π/2) and outL = inL + inR × gainL, where x = pan + 1. At pan = -1, gainL = 1, so the right channel is added to the left in full.
| You want | Use |
|---|---|
| The whole track in the left ear (one earbud in) | A. Panner pan: -1 |
| Only the source's left channel (drop the right) | B. Splitter |
| Original language left, interpreter right (two sources) | One Panner per source, -1 / +1 |
| Swap left and right | B. Splitter wired 0→1, 1→0 |
When there's no sound at all
Sometimes createMediaElementSource makes everything silent, with no error.
Cross-origin media without CORS headers: per the spec, cross-origin audio isn't exposed to scripts and comes through as silence. I haven't reproduced it and captured the console message yet
DRM content (EME): protected audio can't be pulled into Web Audio
AudioContext created before a click: autoplay policy leaves it
suspended. Callctx.resume()inside the clickYouTube iframe: the
<video>lives on someone else's page, so you can't reach it
So "Netflix in my left ear only" can't be done from a web page. That's an OS balance setting.
Side note: folding 5.1 into earphones drops the LFE
I put a 5.1 WAV with a different frequency per channel into the same demo. Center and surrounds were mixed into both sides at about -3dB (×0.707), and the LFE bass channel came out at -141dB, effectively discarded. That's the spec's speaker downmix formula. It's why film mixes also put important bass in the main channels.
Mono audio sits in the accessibility menu for a reason too. People who can't hear in one ear lose a whole instrument in a stereo mix. Don't design something that puts important information only on the left.
If you're building something like split-channel playback for a language app or an earphone L/R test, you can ask negabaro. The tables above are numbers we measured in our own demo.
Key Points
Earphones just play L/R. Left and right are decided by the file, the app or the OS
pan: -1 folds the right channel into the left (outL = inL + inR)
For only the source left channel, connect just output 0 of a ChannelSplitter
Cross-origin media without CORS, DRM and YouTube iframes are out of Web Audio's reach