尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Browser-based auditory psychophysics & tinnitus phenotyping — a self-contained, offline-capable hearing/tinnitus test (Bayesian adaptive audiometry, speech-in-noise, tinnitus psychoacoustics). Uncalibrated/relative; not a diagnostic instrument.
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
A single-file, offline-capable web app with four visible tabs:
1 · Quick test
0. Set your level — a 1 kHz reference tone + slider; pick a level that's clearly audible but doesn't overshadow your tinnitus. Used for both sweeps.
1. Hearing range — medium-speed 250 Hz → 20 kHz sweep; tap "I can't hear it anymore" to record your high-frequency cutoff.
2. Tinnitus pitch match — a slow sweep starting at 500 Hz (documented tinnitus low bound).檢視完整 README ↗▶ Live demo · Scientific methods · MIT License
A single-file, offline-capable web app with four visible tabs:
A calibration-free speech-in-noise screen: three spoken digits over noise, with an adaptive 1-up/1-down SNR track → a speech-reception threshold. Uses the browser's built-in speech voice + Web Audio noise, so it's honest about being a relative, TTS-based screen rather than the validated clinical Digits-in-Noise corpus. Captures functional hearing (understanding speech in noise) that pure-tone testing can miss.
SNR math (verified against the DiN literature [1'][2']). SNR in dB is 20·log10(RMS_speech / RMS_noise); to change SNR by N dB with speech held fixed, the noise amplitude is scaled by 10^(−N/20) — implemented as noiseGain = 0.10·10^(−snr/20) (a validated "fix speech, vary noise" mixing method). Since the device speech voice's RMS is unmeasured, the result is a relative dB SNR (true SNR = value + an unknown constant). The track uses a larger 4 dB step until the first reversal, then 2 dB, and the SRT = mean of the reversal SNRs, discarding the first — standard adaptive practice. Simulation: SNR steps are exact (6.00 / 2.00 dB), and the SRT recovers a known threshold to ~0.8 dB.
Extra references: [1'] Smits et al. 2013, JASA (the DiN test); [2'] Smits 2022, JASA (1-up/1-down SEM); Van den Borre 2021 (DTT scoping review); Türüdü 2025 (online DiN, mixing-method effects ~1–2 dB, antiphasic vs diotic ~6 dB).
A directly visible exploratory suite—no need to finish the audiology panel before opening it:
All research-test results are stored in the raw-session JSON export. They are uncalibrated, within-session measures—not clinical cut-offs or diagnoses.
Results—including the full threshold table and tinnitus profile—are shown on screen and stay local by default. The user may explicitly export them, open a pre-filled email, or consent to the configured results service. Finishing a test never transmits observations automatically.
The four-part ribbon above the test tabs exposes a shared record used by every module:
Local history stays in that browser until the user exports it. No microphone is used and the page does not claim room acoustics, autonomic sensing or acoustic SPL.
These upgrades were guided by a review of recent audiology literature (via Consensus):
Key references: [1] Al-Maskari 2025; [2] Seluakumaran 2021; [4] Ceccato 2021; [5] Han 2020; [6] Gu 2025; [8] Lough 2022 (JASA); [9] Škerková 2026; [10] Daoud 2024; [11] Raj-Koziak 2019; [12] Manning 2019; [13] Cox 2021; [14] Wallaert 2024; [15] Vercammen 2025; [16] Quemar 2026; [17][18] Zhu 2024/2023. Full titles/links were provided in the build conversation.
Not diagnostic. Every part of this suite is an uncalibrated screening tool. For real concerns, see an audiologist (calibrated audiogram, validated tinnitus questionnaire) or an ENT (red flags: pulsatile, unilateral, sudden-onset, vertigo).
Runs in any modern browser on any device (iOS Safari, Android Chrome, desktop). No build step, no dependencies.
Results are never delivered automatically. The Email results button opens a pre-filled message in the user's mail app. A separate Send to configured results service… button displays a health-data consent prompt and only then tries the optional Netlify/Outlook delivery path. To enable that explicit-send path, deploy netlify/functions/send-result.js; it sends server-side via Microsoft Graph. Credentials live only in Netlify environment variables—never in the page or the repo.
Why not put credentials in the page? A static site's JavaScript is fully visible to anyone. OAuth tokens/secrets there would expose your whole mailbox. The serverless function is the safe place for them.
One-time setup (using your existing Entra app registration):
Mail.Send, then Grant admin consent. (App-only sending needs a confidential client — if your registration is public-only, add a client secret next.)MS_TENANT_ID, MS_CLIENT_ID, MS_CLIENT_SECRETMS_SENDER = [email protected] (the mailbox to send from)RESULT_TO = [email protected] (optional; defaults to MS_SENDER)ALLOWED_ORIGINS = your deployed URL, e.g. https://your-site.netlify.app (recommended)RESULT_TOKEN = optional shared token (see note below)Abuse hardening (built in). The endpoint is public, so the function includes: the recipient is fixed server-side (never taken from the request — so it can't be an open relay, only ever emails you), origin allow-listing (ALLOWED_ORIGINS, else same-site only), a request-size cap, per-IP best-effort rate limiting, a honeypot, and payload validation. RESULT_TOKEN adds a required header — but note a static site can't truly keep a secret, so it's friction, not real auth. Worst case without a token is someone spamming your inbox with junk results; the rate limit + origin check make that impractical.
Notes: the function has no npm dependencies (uses Node 18+ global fetch). Sending is app-only/unattended — no user needs to be signed in. It currently sends to you only; to also reply to the test-taker, add a toRecipients entry using an email they enter on the form (opt-in). If you use the Graph function, you can turn off the Netlify Forms email notification to avoid duplicates.
netlify/functions/send-result-delegated.js does the same job with a delegated refresh token (public client + PKCE) — no client secret. Setup:
Mail.Send.node tools/get-refresh-token.js <TENANT_ID> <CLIENT_ID>
It prints a code + URL — sign in as the mailbox to send from — then prints the refresh_token.MS_TENANT_ID, MS_CLIENT_ID, MS_REFRESH_TOKEN (+ optional RESULT_TO, ALLOWED_ORIGINS, RESULT_TOKEN), and point the site at this function (or rename it to send-result.js).Trade-off: refresh tokens rotate/expire (≈90 days of inactivity, and are revocable), and a stateless function can't persist a rotated token, so you may need to re-mint it occasionally. The app-only + secret path is more robust for fully unattended sending; the delegated path simply avoids storing a secret.
Following a technical review, the audiology panel now reports how much to trust each result rather than just a number:
These turn the panel from "a clever tone player" into an instrument that knows when it doesn't know — the reviewer's main recommendation.
Follow-up refinements:
| File | Purpose |
|---|---|
index.html | The entire app (HTML + CSS + JS, self-contained). Brand header + favicon are inline SVG. |
logo.svg | Standalone entertrainment logo (gradient badge + audio waveform) for other uses. |
netlify.toml | Publishes the folder as-is (+ serverless functions). |
netlify/functions/ | Optional Outlook-email functions — send-result.js (app-only) and send-result-delegated.js (no secret). |
tools/get-refresh-token.js | One-time helper to mint the delegated refresh token. |
README.md | This file. |
Drag & drop: go to https://app.netlify.com/drop and drop the hearing-test folder. You get a live URL instantly.
Optional result delivery: the app includes a Netlify Form named hearing-test, but it is only posted after the user presses Send to configured results service… and accepts the health-data consent prompt. In Netlify: Site → Forms → hearing-test → Add notification → Email → [email protected]. GitHub Pages has no result backend, so its button directs the user to Email results or JSON export instead.
Fallback everywhere: each result screen also has an "Email results" button that opens the tester's own mail app pre-filled and addressed to you — works even without the Netlify setup. (The audiogram image is a separate Download PNG button, since email links can't carry an attachment.)
OscillatorNodes for tones, a BufferSource of generated noise (band-/high-/low-pass filtered) for hiss/roar/masking, a StereoPannerNode for the L/R split, and a GainNode per voice.f(t) = f0·(f1/f0)^(t/T). Parameters live in the SWEEP object: hearing (250 Hz→20 kHz, 40 s) and tinnitus (500 Hz→12 kHz, 60 s).FREQS, DYN, THETA, PSY_SIGMA): playChirp() schedules warbled pulsed bursts at a fixed gainForDb(level). Each (ear, frequency) is a Bayesian adaptive threshold estimator (ZEST-inspired): initPrior() builds a prior over the threshold θ (grid THETA, informed by age + neighbouring frequencies + the other ear), updatePost() applies the logistic pHear(level, θ) after each response, and postStats() gives the posterior mean/SD. The next chirp is placed at the current posterior mean; a tone finishes when the posterior SD is small enough (or a trial cap is hit), reporting threshold + posterior SD + trial count. It is not a textbook ZEST (which is why "ZEST-inspired"): stimulus placement is at the posterior mean, not by expected-information-gain. Catch trials (P_CATCH, silent presentations) measure false-alarm rate; a press in the pre-stimulus silence is a false press and re-presented. Presentation order is shuffled across ear/frequency/level. gainForDb() maps relative dB to gain against your comfort ceiling.<canvas> (re-drawn on resize and on light/dark theme change), with broken lines across no-response points.classify()) is a transparent rule set combining sound-quality choice, pitch, questionnaire, audiogram configuration, and residual-inhibition/masking results.To change the recipient, edit the RECIPIENT constant (and the Netlify notification). To adjust ranges/speeds, edit SWEEP, FREQS, or DYN/RAMP.
This is a browser-based auditory psychophysics and tinnitus phenotyping tool intended for research, self-observation and hypothesis generation. It is not a medical device, not a diagnostic instrument, and not a substitute for calibrated audiometry or clinical assessment. Browser audio is uncalibrated — the delivered sound level depends on the device, DAC, OS volume, headphones and coupling — so all results are relative (useful within a session for trends and relationships), not clinical dB HL. Comfortable levels only; never raise levels to "test tolerance" or to reproduce a past exposure. Sudden, one-sided, pulsatile or asymmetric hearing change — or tinnitus with vertigo — needs professional assessment (audiologist / ENT), not a web page.
Released under the MIT License © 2026 entertrainment.