A browser tool that maps your relative hearing sensitivity from 10 Hz to 25 kHz using pure
sine tones and repeated blind threshold tests. Everything runs client-side — no server, no
network, no account; your results live only in your browser's localStorage.
Warning
Not a medical or diagnostic device. It measures your whole playback chain (DAC + transducer + ears), not your ears in isolation, and has no access to true sound pressure — results are a relative sensitivity map, not clinical dB HL. For best results use wired headphones in a quiet room. If you have hearing concerns, see an audiologist.
- Sweeps 10 Hz – 25 kHz (capped to your hardware's Nyquist limit) with pure sine tones.
- Uses an ascending method of limits with adaptive bracketing and repeated, blind trials to estimate a threshold per frequency, with a standard deviation per point.
- Tests both ears together (binaural) and then each ear separately (monaural), so you can see interaural asymmetry.
- Produces a relative threshold curve with confidence bands, censored markers, and an interactive chart — hover any frequency to read every channel's value at once.
- Saves sessions locally and lets you overlay and compare past runs.
- Exports results as JSON / CSV / PNG.
Requires Bun (a recent Node.js + npm also works).
bun install
bun run dev # http://localhost:5173
bun run build # typecheck + production build into dist/
bun run preview # serve the production build locallyWith npm: npm install, npm run dev, npm run build, npm run preview.
The production build in dist/ is fully static and can be hosted on any static file host.
A modern browser with the Web Audio API (Chrome, Firefox, Safari, Edge). The app requests a
96 kHz AudioContext and falls back gracefully, capping the top test frequency just under the
device's Nyquist frequency when 25 kHz isn't reachable. Audio starts only after a click, per
browser autoplay policy.
- Intro / environment — disclaimer, a setup checklist, sample-rate detection, an optional setup note (so comparisons can flag mismatched hardware), and a resolution choice (coarse / normal / fine = points per octave).
- Calibration — set a comfortable 1 kHz reference level, confirm the sensitive-band ceiling (ISO 226 attenuation applied so those bands aren't played painfully loud), and a reaction-time probe used to correct measured thresholds.
- Binaural scan — every frequency, both ears, in a shuffled and blind order. Press
Spacethe moment you hear a tone. - Monaural scan — every frequency on each ear, again shuffled and blind.
- Results — the relative threshold curve (binaural / left / right) with ±1 SD bands and censored markers; interaural asymmetry summary; reliability counters; JSON/CSV/PNG export.
- History — overlay and compare past sessions (one binaural line per session), or drill into a single session's three channels. Warns when comparing across different hardware/setup notes.
In-progress scans are checkpointed to localStorage after every trial, so you can pause,
reload, or resume later without losing position.
- Pure tones from a Web Audio
OscillatorNode, with anti-click attack/release envelopes. - True L/R isolation via a
ChannelMergerNode(no inter-channel bleed), not a stereo panner. - ISO 226:2023 equal-loudness weighting (attenuation-only, at a nominal phon level) so perceptually sensitive bands are presented at sane levels — used for the EQ shape only, never as absolute SPL.
- Ceiling escalation — a frequency you don't hear is retried louder on the next pass; if it's still unheard it's recorded as censored (▲), and if it is caught it gets tested more thoroughly.
- Blinding & randomization — a materialized, shuffled test queue driven by a seeded PRNG, plus silent catch trials to measure your false-alarm rate and overall reliability.
- Reaction-time correction and censored-data handling (▲ not heard at ceiling / ▼ heard at floor) so the curve reflects real thresholds rather than button-timing or range limits.
src/
audio/ AudioContext + sample-rate cap, anti-click sine generator with merger-based
L/R routing, dBFS↔gain helpers, ISO 226 equal-loudness curve.
engine/ Deterministic log-spaced frequency generator, seedable PRNG, calibration,
single ascending run, censored-aware aggregation/stats, scan orchestration
(materialized queue, adaptive bracketing, catch trials, checkpoint/resume).
storage/ Versioned localStorage schema, quota / private-mode failure handling,
JSON/CSV/PNG export.
ui/ Wizard steps, the interactive results chart, history/compare, tone playground.
Tech stack: React 19 + TypeScript 5 (strict) on Vite 6, with no runtime dependencies beyond React.
The equal-loudness coefficients come from ISO 226:2023 and are used for the EQ shape only at a nominal phon level — never as absolute sound pressure. The standard's PDF is copyright-protected and is not included in this repository.
MIT © Niko Storni
