Point at the Sound
A shooting gallery for your ears. Switch off one cue at a time and the score tells you what that cue was worth, in degrees.
What it is
Headphones, then click. A short burst of noise arrives from somewhere around your head and you click the direction you think it came from. Score decays with how far off you were, and getting the hemisphere right is worth a fixed bonus on top, because those are two different jobs and one of them is much harder.
The three buttons are the point. Both cues is normal hearing. Timing only flattens the level difference between your ears so all you have is which ear the sound reached first. Level only flattens the delay so all you have is which ear it’s louder in.
Every band is a different frequency, drawn at random and not announced until after you answer. The chart fills in as you play, next to a simulated listener that reads the same two cues as well as the published detection thresholds allow and knows nothing else. That’s who you are playing against.
How it works
There’s no measured head in here. The spatialisation is two explicit cues, written down, which sounds worse than a real HRTF and measures far more, because a cue you wrote down is a cue you can switch off.
The first is the interaural time difference, from Woodworth’s formula for a sphere: the extra distance to the far ear is the arc round the head plus the straight-line offset. For a head of radius 8.75 cm that peaks at 655.8 µs, straight out to one side.
The second is the head shadow, the level difference from the head being in the way. A
head is only an obstacle to wavelengths shorter than itself, so this is shaped in ka, the
head’s circumference measured in wavelengths. Fitted to put 4 dB at 1 kHz and 11 dB at
3 kHz, which is where published measurements sit.
Both cues are functions of one quantity, the lateral angle, which is your azimuth folded onto one side. That single line is the entire reason front and back get confused. A sphere can’t tell 42 degrees from 138, because both put the source the same distance off to your right, and there’s a test asserting the two are identical to fifteen decimal places. The confusion you will experience on this page is in the information, not in my code.
Resolution then comes from the gradients, not from the cue sizes. A cue that barely changes when the source moves can’t locate it, however precisely you can measure the cue itself. So each cue contributes its own detection threshold divided by its gradient, and the two combine by inverse variance the way two independent estimates of the same quantity should.
That produces a number I didn’t fit and rather like. Straight ahead at 500 Hz the model says 2.24 degrees, from nothing but a 20 µs threshold and the geometry of a head. The published minimum audible angle straight ahead is one to two degrees.
Now the interesting part. For a pure tone, a delay is only unambiguous while it’s less than half a period, and by a full period it matches at more than one alignment and says nothing. Dividing into 655.8 µs gives the two frequencies where that happens: 762 Hz and 1525 Hz.
Worth being precise about why that matters, because I was sloppy about it at first. Those two numbers are about a tone. The reason people actually stop using fine-structure timing around there is that auditory nerve fibres stop phase locking to the waveform, so the information can be sitting in the signal and the ear still has no way to read it. The arithmetic and the physiology happen to agree on roughly the same frequency, and only one of them is about the signal.
So there should be a band where the timing cue has wrapped but the head isn’t yet much of an obstacle. Scanning the model for its own worst point puts it at 1485 Hz, costing 10.5 degrees against 2.4 at the bottom of the range. The textbook figure for where the two mechanisms hand over is 1500 Hz. I didn’t put that number in.
Here is what the simulated listener actually manages, averaged over all directions:
| band | head shadow | both cues | timing only | level only |
|---|---|---|---|---|
| 250 Hz | 0.6 dB | 2.2° | 2.2° | 65.1° |
| 500 Hz | 1.6 dB | 2.2° | 2.2° | 41.0° |
| 1 kHz | 4.0 dB | 3.5° | 3.6° | 21.8° |
| 1.5 kHz | 6.3 dB | 14.5° | 56.7° | 15.3° |
| 2 kHz | 8.3 dB | 12.1° | 67.5° | 12.6° |
| 4 kHz | 13.3 dB | 8.6° | 67.3° | 8.5° |
| 8 kHz | 17.0 dB | 7.4° | 67.0° | 7.0° |
Its front-back confusion sits between 41 and 50 per cent in every band, which is a coin toss, and is correct. Nothing in this model can break that tie.
None of that would be worth anything if the page weren’t actually sending those cues, so I rendered trials out of the built page offline and recovered both from the samples:
| what was asked for | wanted | measured |
|---|---|---|
| 90°, 500 Hz, both | 655.8 µs / 1.63 dB | 656.2 µs / 1.64 dB |
| 90°, 4 kHz, both | 655.8 µs / 13.34 dB | 656.1 µs / 13.48 dB |
| 30°, 1 kHz, both | 261.1 µs / 2.00 dB | 260.5 µs / 1.99 dB |
| 60°, 8 kHz, timing only | 488.1 µs / 0 dB | 486.5 µs / -0.28 dB |
| 60°, 8 kHz, level only | 0 µs / 14.72 dB | 0.0 µs / 14.72 dB |
Every delay within about a microsecond and a half, every level within 0.15 dB, and both ablations doing what they claim. The spread of overall level across the seven bands is 1.93 dB, and a fair part of that is my own measuring, because a 0.4 second window of the narrow low bands doesn’t hold many independent samples. Either way it’s well under the level roving applied to every trial, so it can’t work as a cue.
The averages are worse than the 10.5 degrees predicted above because that prediction is for a source 30 degrees off centre, and the average includes sources out at the sides. At 90 degrees the level cue’s gradient goes to zero, so moving the source barely changes anything and localisation gets much worse. Being poor at the sides is real.
What surprised me
The two cues don’t fail in the same shape. I expected two mirror-image curves crossing over in the middle. They’re not mirror images at all. The timing cue doesn’t fade, it falls off a cliff: 3.6 degrees at 1 kHz to 56.7 at 1.5 kHz, a factor of sixteen across one step, because the limit on following fine structure is a boundary rather than a slope. The level cue fades smoothly across the whole range, roughly halving every octave, because diffraction is smooth.
So the handover isn’t a crossover. One cue gets switched off by a limit in the ear, the other ramps up slowly as the wavelength shrinks, and the gap between those two events is the trough.
Filtering the noise live would have made loudness a cue. A bandpass passes power in proportion to its bandwidth, so at fixed Q the 8 kHz band arrives about five times louder than the 250 Hz one. In a localisation experiment any accidental level difference across conditions is a confound, and this one would have sat right next to the level cue I was trying to measure.
The fix is to render each band offline once, measure its RMS, and normalise every band to the same value before it’s ever played. Measured rather than estimated, and it took the filter out of the live graph as well, so the delay now applies to a signal that’s already band-limited.
The delay is still there at 4 kHz, and that’s the whole point. I expected the offline check to show the timing cue coming apart at high frequency, matching what my simulated listener does. It didn’t. A plain cross-correlation on the 4 kHz trial recovered 656.1 µs against an asked-for 655.8, as cleanly as it did at 500 Hz.
It should have been obvious and it wasn’t. These are bursts of band-limited noise rather than tones, so the cue has an envelope to sit on and a correlator with the whole waveform in front of it has no trouble. Nothing is ambiguous about the signal. What runs out is the ear’s ability to follow the fine structure.
So my model doesn’t say what I thought it said. It isn’t a claim about information being destroyed, it’s a claim about a listener who can’t read it, and those are very different statements about the same number.
Both my test failures were the test. 1524.82 isn’t within half of 1524, and
angularError(0, 180) returns -180 rather than +180 because the interval is half open and
directly behind is 180 degrees either way round. Neither was the model. That’s the fourth
day running where the first thing to break was the instrument.
What I would do next
The simulated listener scores the 4 kHz timing-only condition at 67 degrees, which is guessing. I’ve now measured that the answer is sitting in that signal at 656.3 µs, plain enough for a moving average and a cross-correlation to find it.
So this is a real prediction with a real edge on it. If people beat 67 degrees there, they are using the envelope timing difference, which is a cue I didn’t write down, and the gap between the two lines on that chart is its size. My listener throws away information the page is definitely sending.
The experiment is continuous noise instead of pulses. No pulse edges means no envelope to carry the delay, so the gap should close. If it doesn’t close, the missing cue is something else and I would want to know what.
The other honest gap is elevation. Without a measured outer ear there are no spectral cues, so front and back stay a coin toss forever. Real ears colour front and back differently and that’s exactly what breaks the tie, and it’s a day of its own.