Changelog
Release notes for every version of BeatDet.
v0.7.12Latest2026-04-14
Fixed
- –Drop not working / overlay stuck: the global drag overlay used a window-level `drop`
v0.7.112026-04-14
Fixed
- –Drop-to-replace overlay stuck on screen: the global drag overlay's inner hint box was
- –Key ambiguity label garbled: the warning "Low confidence — key may be ambiguous" was
Added
- –Zoom keyboard shortcuts: `X` zooms the waveform in (step +0.5×), `Z` zooms out
- –Volume keyboard shortcuts: `]` raises volume by 0.1, `[` lowers it, clamped to 0-1.
v0.7.102026-04-14
Added
- –Global drag overlay: dragging any file over the browser window now darkens the entire
- –Settings keyboard shortcut: pressing `S` from any page navigates to the Settings page;
Fixed
- –Waveform drag-to-seek cursor: the playhead cursor bar now tracks the pointer in real
v0.7.92026-04-13
Fixed
- –Keyboard shortcuts flyout margin: gap between the `?` button and the flyout panel
v0.7.82026-04-13
Fixed
- –Ctrl+R passthrough: the global `keydown` handler now ignores events where `ctrlKey`,
- –Drag-enter/leave flicker: file drag highlight on the uploader no longer flickers when
- –Drop-to-replace hint: "Drop audio to replace" text is now shown on the loaded-file
v0.7.72026-04-13
Fixed
- –Re-analyse after session restore: "Re-analyse" button was missing when a session
Added
- –CloseButton component: reusable dismiss/close button exported from `Button.tsx` with
v0.7.62026-04-13
Added
- –Page tagline: subtitle "Beat & key detection for DJs and producers" added below the
- –Consistent pointer cursor: global `cursor: pointer` CSS rule added for all enabled
Changed
- –Footer action buttons: "New file" and "Re-analyse" changed from `ghost` to `secondary`
- –Detection Notes toast dismiss: improved padding and `hover:shadow-md` for a clearer
v0.7.52026-04-12
Changed
- –What's New banner: moved from inside the results block to the top of the page, above
- –Page title: heading changed from "Beat Detection" to "BeatDet"; subtitle updated to
- –Playback controls spacing: secondary controls row (speed buttons, zoom) now uses
Fixed
- –Detection Notes toast position: wrapped in `ReactDOM.createPortal` to escape the
Added
- –Drop-to-replace on loaded file: the file info panel now accepts drag-and-drop events;
v0.7.42026-04-12
Added
- –HPSS key detection: Harmonic-Percussive Source Separation (HPSS) is now applied to
v0.7.32026-04-12
Fixed
- –Key detection frequency range: raised `fMin` from 65 Hz to 150 Hz (~D3) in the
Added
- –GiantSteps benchmark: `giantsteps-benchmark.mjs` script for measuring key detection
v0.7.22026-04-11
Changed
- –Key detection profiles: switched from Krumhansl-Kessler (1982) perceptual profiles to Bellman-Budge (2005) corpus-derived profiles. The Bellman-Budge profiles assign much larger weights to diatonic scale tones relative to non-diatonic ones, providing stronger tonic/dominant separation on real audio. Verified on Für Elise (A Minor, 8A) — previously misdetected as E Major with KK profiles.
Added
- –Known-key test tracks: Für Elise (A Minor, 8A) passes; Canon In D 8-bit synths and Eine Kleine Nachtmusik added as documented known limitations (square-wave harmonic pollution and sonata-form modulation respectively).
v0.7.12026-04-11
Fixed
- –Key confidence metric: confidence now shows the raw Pearson correlation coefficient (0–1) instead of a misleading normalised value that was always 100% for the winner. This gives users a meaningful absolute measure of tonal clarity (e.g. 0.86 for a clear key, 0.57 for an ambiguous one).
Added
- –Real-audio key detection test: Canon In D added as known-key verification (now skipped as a known limitation due to 8-bit synth harmonic artefacts; see v0.7.2).
v0.7.02026-04-11
Added
- –Key detection: Bellman-Budge chroma-profile correlation identifies the musical key of uploaded audio. Displays the detected key (e.g. "C Major"), Camelot Wheel code for DJ-friendly harmonic mixing, relative key, normalised confidence bar, and top-5 alternative candidates ranked by correlation. Ambiguity flag shown when confidence is too low.
- –KeyDisplay component: prominent key card with large key name, mode badge, Camelot code, confidence bar, relative key, and candidate list with confidence bars.
- –Key detection tests: 18 unit tests covering chroma vector computation, Pearson correlation, Camelot codes, relative key calculation, candidate ranking, and ambiguity detection on flat input.
- –Show key detection setting: toggleable via `showKey` in Display settings (default on). Settings store migrated to schema v5.
v0.6.42026-04-02
Fixed
- –Lint: resolved all ESLint errors (5 errors → 0). Fixed `set-state-in-effect` warnings in `BpmDisplay` (derived state for hints, suppress for tap reset) and `WhatsNewBanner` (suppress for localStorage read). Removed unused `Hint` type import, stale `eslint-disable` directive in `layout.tsx`, and suppressed `no-require-imports` in `jest.config.js`.
- –Dead CSS: removed unused `.export-options-grid` class from `globals.css`.
- –Misleading comment: corrected "active:scale" comment to match the actual `opacity` rule.
- –Unescaped entity: fixed `'` in `WhatsNewBanner` JSX text for `react/no-unescaped-entities`.
Changed
- –README: updated project structure tree (added `changelog/page.tsx`, `ErrorBoundary.tsx`, `ThemeInitialiser.tsx`, `WhatsNewBanner.tsx`, `hintUtils.ts`, all four test files), expanded the Testing section from two to four suites, updated component descriptions (tap tempo, keyboard shortcuts help, MP3/ZIP export), and added missing Display settings (Classic UI, Waveform height).
- –package.json: synced `version` field to `0.6.4` (was stale at `0.3.4`).
v0.6.32026-04-02
Added
- –Tap tempo: a "Tap" button on the BPM card that measures inter-tap intervals and displays a tapped BPM estimate. Resets automatically after 3 s of inactivity. A "Use" button applies the tap BPM as the display multiplier.
- –Playback speed control: four compact speed buttons (0.5×, 0.75×, 1×, 1.5×) in the waveform player let users slow down audio for beat verification.
- –Loop region: a loop toggle button (also accessible via L) enables a draggable, resizable blue region on the waveform. Playback loops continuously within the region. Toggling off clears the region.
- –Waveform region → export: when a loop region is active and the ExportPanel is in Custom range mode, a "Use waveform region" button pre-fills the start/end inputs from the region bounds.
- –Keyboard shortcuts help: a keyboard icon in the NavBar (and ? global shortcut) opens a shortcuts reference panel listing Space, R, L, and ?.
- –R shortcut: restarts playback from the beginning (mirrors the restart button).
Changed
- –WaveformPlayer controls: restructured into a primary transport row (time, restart, play, loop, mute, volume) and a secondary row (speed buttons, zoom, beats badge) to prevent overflow on 360 px screens.
- –Mobile layout: primary controls now use `flex-wrap`, the time display no longer has a fixed width, and the volume slider is slightly narrower to prevent horizontal overflow at 360 px.
Fixed
- –Accessibility: `select` elements now receive the same `focus-visible` outline as other interactive elements; `button:active` has a consistent `opacity` reduction for non-colour-based feedback.
v0.6.22026-04-01
Changed
- –Docs: trimmed `CLAUDE.md` to remove outdated implementation notes and reduce noise for AI assistants.
v0.6.12026-04-01
Added
- –Settings nav toggle: clicking the Settings icon when already on the Settings page now returns to the home page, matching the existing Changelog icon behaviour. Avoids having to move the mouse to navigate back.
- –Settings Saved indicator: a brief "Saved" toast fades in at the bottom-right of the screen after any setting is changed, confirming the auto-save.
Changed
- –Milestone renumbering: corrected milestone plan to v0.6.x = UI/UX Polish (current focus), v0.7.x = Key Detection, v0.8.x = Library Release. Updated TODO.md, CLAUDE.md, and CHANGELOG.md accordingly.
v0.6.02026-03-31
Added
- –Modern UI: elevated panels with subtle box shadows, accent-dot section headings, BPM hero card gradient background, upload zone glow on hover/drag, button hover lift, navbar shadow, progress bar glow, and fade-in animation on results. All purely CSS-driven via `ui-*` hook classes in `globals.css`.
- –Classic UI toggle: new `classicUi` boolean in `DisplaySettings` (default `false`); when enabled, `data-ui="classic"` is set on `<html>` and all modern enhancements are suppressed. Toggle available in Settings > Appearance > "Use classic UI".
- –Settings store schema v4: migrates v3 -> v4 automatically, adding `classicUi: false` to existing display settings.
- –FOUC prevention for UI mode: inline head script now also reads `classicUi` from localStorage and applies `data-ui="classic"` before first paint.
Fixed
- –Static export: icon route files (`apple-icon.tsx`, `icon1.tsx`, `icon2.tsx`) and `manifest.ts` now export `dynamic = 'force-static'` to fix build failure with Next.js 16 `output: "export"`.
v0.5.12026-03-31
Fixed
- –Jest Node.js 25 warning: added `testEnvironmentOptions: { globalsCleanup: 'off' }` to `jest.config.js` to prevent the `--localstorage-file` warning from Node.js 25's built-in `localStorage` during Jest environment teardown.
v0.5.02026-03-30
Added
- –PWA support: BeatDet is now installable as a Progressive Web App on desktop and mobile.
Changed
- –Milestone plan: revised to v0.6.x = UI/UX Polish; v0.7.x = Key Detection; v0.8.x = Library Release (`beatdet-core`).
v0.4.112026-03-29
Changed
- –Code review / housekeeping: no behaviour changes.
v0.4.102026-03-29
Fixed
- –Cancel race condition leaving UI stuck: If the user pressed Cancel just as analysis completed, `status` was set back to `'idle'` but `fileInfo` remained set with no clear button visible. Two fixes applied: (1) `AudioUploader`'s clear button is now shown whenever `fileInfo` is present, regardless of `status`; (2) added a `controller.signal.aborted` guard in `analyseFile` after `analyseAudio` returns so a late cancel cannot be overridden by `setStatus('complete')`.
- –Tempo candidates not clickable: Candidate tempo chips were rendered as `<div>` elements with no interaction. They are now `<button>` elements that select the candidate as the displayed BPM (by computing `candidate.bpm / baseBpm` as the multiplier). The currently-selected candidate is highlighted in accent colour, matching the ÷2/×2 button behaviour. Clicking a 3/4-feel track's 3× candidate now works as expected.
v0.4.92026-03-29
Fixed
- –Progress bar jump-back (properly fixed): The shimmer is now an absolutely-positioned overlay on the progress track rather than the fill bar itself. The fill bar is always sized to the real percentage, so when the shimmer disappears there is no visual jump from 100% to a small value.
- –Progress bar stuck at 5% (properly fixed): Removed the `onProgress(0.05)` call before `decodeAudioData` — the indeterminate shimmer now covers the entire decode phase. Replaced `await Promise.resolve()` micro-task yields with `requestAnimationFrame`-based yields (`yieldToBrowser`), guaranteeing the browser paints each intermediate progress value before the next pipeline stage runs.
v0.4.82026-03-29
Fixed
- –Theme FOUC (still flashing light on dark mode): Replaced `next/script beforeInteractive` approach (v0.4.6) with a plain render-blocking `<script>` placed directly inside `<head>` in the root layout. The `next/script` approach does not reliably inject before the first paint in static-export or dev-server mode. A plain `<script>` in `<head>` is parse-order guaranteed to execute before any pixel is painted. Added a `@media (prefers-color-scheme: dark)` CSS rule in `globals.css` as a zero-JS fallback for the most common system-dark case, so the correct palette is applied even before the script runs. Removed the now-unnecessary `ThemeInitialiser` component.
- –Progress bar jump-back animation: Suppressed CSS transition when the bar switches from indeterminate (100% width shimmer) to the first real progress value, preventing visible backward animation.
- –Sparse progress bar updates: Added `await Promise.resolve()` yields in `analyseAudio` after the 20%, 30%, and 60% milestones so React can flush state between synchronous pipeline stages.
v0.4.72026-03-29
Fixed
- –Progress bar jump-back animation: Suppressed CSS transition on the single frame when switching from indeterminate (shimmer at full width) to the first real progress value.
- –Sparse progress bar updates: Added micro-yields (`await Promise.resolve()`) in `analyseAudio` so React flushes intermediate progress state between pipeline stages.
v0.4.62026-03-29
Fixed
- –Theme FOUC (flash of light on dark mode): `ThemeInitialiser` now uses `next/script` with `strategy="beforeInteractive"`, which injects the inline theme-detection script into the document `<head>` before any page content is painted. Previously the script was a plain `<script>` inside `<body>`, which ran too late and caused a brief flash of the light (Solarised Light) palette before the dark theme was applied.
v0.4.52026-03-29
Changed
- –Detection note toast size: increased from `text-xs` to `text-sm` throughout; icon sizes grown proportionally; max width widened from `max-w-xs` to `max-w-sm`.
- –Em-dash removal: replaced all em-dashes (\u2014) with colons, semicolons, or restructured prose across all source files, comments, and user-facing strings (`hintUtils.ts`, `BpmDisplay.tsx`, `BeatList.tsx`, `WhatsNewBanner.tsx`, `NavBar.tsx`, `layout.tsx`, `page.tsx`, `settings/page.tsx`, `changelog/page.tsx`, and all test files). Null value indicators in `BpmDisplay` changed from `\u2014` to `-`.
- –CLAUDE.md: trimmed `Current State` section to key architectural facts only (removed exhaustive feature list that duplicated `README.md`); updated checklist step 6 to discourage adding feature descriptions; removed version number (source of truth is `VERSION` file).
v0.4.42026-03-29
Fixed
- –Waveform scrollbar: the scrollbar is now correctly hidden. WaveSurfer renders into a shadow DOM, so the previous CSS approach (targeting `::webkit-scrollbar` on the outer container) had no effect. The fix uses WaveSurfer's built-in `hideScrollbar: true` option, which applies the style inside the shadow DOM, combined with JavaScript `mouseenter`/`mouseleave` listeners that toggle the hidden state so the scrollbar reappears on hover.
v0.4.32026-03-29
Fixed
- –Zoom controls: removed the decorative `×` character that appeared after the zoom number input — it served no function and looked like a broken button.
- –Waveform scrollbar visibility: fixed the hover-to-show scrollbar logic. The previous implementation used `background: transparent`, which still reserved layout space. Replaced with `height: 0` on `::-webkit-scrollbar` so the bar is truly invisible until hover.
- –Default waveform height: raised default from 80 px (small) to 120 px (medium) in both `DEFAULT_DISPLAY` and the `??` fallback in `WaveformPlayer`. Settings store bumped to schema `version: 3` with a migration that preserves existing user preferences while upgrading unset values to 120 px.
Changed
- –TODO milestone: library-release milestone re-labelled `v0.7.0` (was `v1.0.0`).
v0.4.22026-03-29
Added
- –Waveform height setting: new `waveformHeight` display preference (80 / 120 / 160 / 200 px) configurable on the Settings page. Applies live via WaveSurfer `setOptions` without reloading the audio. Defaults to 80 px (previous behaviour).
- –Zoom number input: a numeric text field sits next to the waveform zoom slider for precise, repeatable values. Accepts any value in [1, 8] and snaps to the nearest 0.5 step.
- –Wider zoom slider: zoom range input widened from 80 px to 144 px (Tailwind `w-36`) for finer control.
Fixed
- –Chart panel gap: increased inter-chart grid gap from `gap-5` (20 px) to `gap-6` (24 px) so the borders between "Onset Strength & Beat Markers" and "BPM Distribution Histogram" are no longer visually touching.
- –Waveform scrollbar on hover: the WaveSurfer horizontal scrollbar (visible when zoomed in) is now hidden by default and appears only on pointer hover over the waveform area. Implemented via `.waveform-scroll-area` CSS class in `globals.css`.
v0.4.12026-03-29
Added
- –Tempo confidence hints now include contextual Wikipedia "More info" links:
- –`buildHints` and `isCloseRatio` extracted to `src/lib/hintUtils.ts` for independent testability.
- –31 unit tests for `hintUtils` covering all hint conditions, URL values, suppression behaviour, and edge cases.
- –Waveform zoom slider (`ZoomIn` icon + range input) wired to `waveformZoom` display setting; default zoom also configurable on the Settings page.
Fixed
- –"Trouble with Tribals 135bpm.mp3" real-audio test marked as `skip` — detection locks onto ~188 BPM at low confidence (≈0.39); the outcome is environment-sensitive and not a regression in the algorithm.
v0.4.02026-03-29
Added
- –Tempo confidence hints: fixed bottom-right toast notification surfaces detection edge cases to the user after each analysis. Hints cover:
- –`correctionRatio` field added to `BpmEstimate` type and returned from `estimateBpm`. Ratio hints are suppressed when the algorithm already auto-corrected the same ratio, preventing contradictory advice.
Changed
- –Auto-scroll to results removed. Results appear below the upload zone and modern display resolutions do not require the scroll.
v0.3.92026-03-29
Added
- –README: accuracy comparison note — tested more accurate more often than Tunebat and AudioAlter across 26 randomly sampled Kevin MacLeod tracks (60-204 BPM).
- –README: explicit call-out that all processing is in-browser with no upload required, and works well on mobile.
Fixed
- –WaveformPlayer: beat-marker overlay now scoped to the waveform canvas height only (`WAVEFORM_HEIGHT` constant); previously `h-full` extended into the WaveSurfer horizontal scrollbar zone when zoomed in, obscuring it.
- –Page: session restore no longer triggers auto-scroll to results on refresh. Auto-scroll now only fires when a new analysis completes (previous status was `'analysing'`), not when a stored session is rehydrated (`idle → complete`).
v0.3.82026-03-29
Added
- –Changelog nav icon now navigates back to the home page when already on the changelog page (toggles between changelog and home).
Fixed
- –CHANGELOG.md: back-filled v0.3.4, v0.3.5, and v0.3.6 entries that were previously folded into v0.3.7.
v0.3.72026-03-29
Added
- –×3 upward harmonic correction in `estimateBpm`: detects tracks where the onset detector locks onto every-third-beat downbeats (e.g. slow waltzes); threshold 70%.
- –10 new real-audio benchmark tracks (batch 3) in `realAudio.test.ts`, covering 54-186 BPM across funk, classical, electronica, and world genres.
Changed
- –Default `bpmMin` lowered from 60 → 55 in settings store (allows detection of slower tempos without user adjusting settings).
- –Test suite `bpmMin` floor lowered from 55 → 50, enabling the 54 BPM Nightdreams track to pass.
Fixed
- –`AudioContext` creation moved from module scope into `beforeAll()` in `audioExport.test.ts` to prevent "no output device available" errors on cold Jest runs.
v0.3.62026-03-29
Fixed
- –Canon In D For 8 Bit Synths BPM corrected from 133 → 132 in filename and test suite (per incompetech source PDF).
v0.3.52026-03-29
Changed
- –`testfiles/` excluded from Git via `.gitignore`; binary test assets kept locally only.
- –Git history rewritten with `git filter-repo` to remove previously committed MP3 blobs; pack size reduced from multi-MB to ~207 KiB.
v0.3.42026-03-29
Added
- –`closeBufferContext()` export in `audioExport.ts` for clean test teardown.
Fixed
- –Jest open-handles warning: `audioExport.test.ts` now uses a single shared `AudioContext` closed in `afterAll`, instead of creating a new instance per helper call.
v0.3.32026-03-28
Fixed
- –`scrollIntoView` option `behaviour` (silently ignored) corrected to `behavior`.
- –Removed unused `useRef` import from `page.tsx`.
- –README version header, package.json version, and CHANGELOG 200 MB reference all synced.
- –TODO.md: ticked all shipped v0.3.0 export and changelog items.
v0.3.22026-03-28
Added
- –Analysis cancellation: Cancel button shown during loading/analysing; `AbortController` passed through to `analyseAudio`; uploading a new file automatically aborts any in-flight analysis.
- –Virtual scrolling in BeatList: table rows rendered via `@tanstack/react-virtual`; only visible rows are mounted with overscan=8; handles 500+ beat tracks without DOM bloat.
Fixed
- –`scrollIntoView` option key was `behaviour` (ignored by browsers); corrected to `behavior`.
- –Removed unused `useRef` import from `page.tsx`.
v0.3.12026-03-28
Changed
- –File size guard lowered from 200 MB to 100 MB (Cloudflare Pages upload limit).
- –`audioExport.ts`: replaced `OfflineAudioContext` buffer factory with a lazy `AudioContext` singleton (`getBufferContext()`) for improved testability.
Added
- –`audioExport.test.ts`: 23 unit tests covering `encodeWav`, `normalisePeak`, `sliceBuffer`, `concatenateBuffers`, `bundleZip`, and `MAX_FILE_BYTES`.
v0.3.02026-03-27
Added
- –MP3 export: browser-side MP3 encoding via `lamejs`; choose WAV or MP3 and bitrate (128 / 192 / 256 / 320 kbps) in the export panel and settings.
- –ZIP download for cut-at-beats mode: all slices bundled into a single `.zip` file via `fflate`, replacing sequential browser downloads.
- –CSV / JSON beat list export: download beat timestamps (and optionally confidence) as `.csv` or `.json` directly from the beat timeline.
- –Waveform zoom: zoom slider in the waveform player; wired to the `waveformZoom` setting.
- –In-app changelog: `/changelog` route listing all release notes; version badge in the nav bar links to it.
- –"What's New" banner: shown once to returning users after an upgrade; dismissed per-version in `localStorage`.
- –Error boundary: wraps result components to prevent a render error crashing the entire page.
- –File size guard: friendly error shown before decoding if the selected file exceeds 100 MB (Cloudflare Pages limit).
- –Settings migration: Zustand persist version field incremented; stale settings are merged safely with defaults.
Changed
- –Export panel now surfaces format (WAV / MP3) and bitrate controls inline.
- –Cut-at-beats now downloads a single ZIP instead of sequentially triggering individual downloads.
v0.2.02026-03-27
Added
- –Version injection: version read from `VERSION` file at build time via `next.config.ts`; no more hardcoded strings.
- –Favicon: `src/app/icon.svg` — Solarised blue waveform icon, auto-registered by Next.js App Router.
- –Open Graph / Twitter Card metadata in `layout.tsx`.
- –BPM ÷2 / ×2 buttons: display-only quick-correct for common octave errors; resets on new analysis.
- –Click-to-seek: clicking any beat row in the timeline jumps the waveform to that time.
- –Space bar toggles waveform play/pause globally (excluded when focus is on a form control).
- –Re-analyse button: re-run detection on the last uploaded file with current settings, no re-upload needed.
- –Export error UI: export failures now shown in-panel, not just `console.error`.
- –Cut-at-beats file count badge: shows "Will download N files" before the user clicks Download.
- –Auto-scroll: results scroll into view after analysis completes.
- –Indeterminate progress: full-width shimmer bar during file-load phase; percentage shown only during analysis.
- –Waveform theme reactivity: waveform colours update live when the theme is toggled.
- –Session restore banner: explains waveform is unavailable and prompts re-upload.
- –Accessibility: `role="alert"` on error banner; `aria-live` region announces analysis start / BPM result; `aria-label` on all waveform controls and nav buttons; screen-reader `<p>` summaries on charts.
- –Mobile: BPM stats grid stacks on narrow viewports; beat table scrolls horizontally.
- –Single-chart layout: histogram takes full width when onset curve is hidden.
Fixed
- –Duration display no longer rounds `59.5 s` up to `0:60`.
- –Upload zone and error messages now mention AAC (it was accepted but unlabelled).
Removed
- –`next-themes` dependency (app uses its own `useTheme` + `ThemeInitialiser`).
Changed
- –`lang="en-AU"` on `<html>` element.
- –AAC added to accepted format labels throughout the UI.
v0.1.72026-03-26
Changed
- –Various beat detection tuning (half-tempo gate improvements).
v0.1.62026-03-25
Added
- –Initial public release.
- –Spectral-flux beat detection engine.
- –BPM estimation with multi-lag IOI accumulation and harmonic correction.
- –WaveSurfer.js waveform player with beat markers.
- –Onset strength chart and BPM histogram (Chart.js).
- –Beat timeline table with confidence bars.
- –Four export modes: full, isolate-beats, cut-at-beats, custom-range.
- –Session persistence via `sessionStorage`.
- –Settings page: all detection, display, and export parameters.
- –Light / Dark / System theme with Solarised colour palette.
- –Zustand settings store with `localStorage` persistence.