Library - top artist hero, carousels, recent tracks
Search - top result card, power filters, queue
Discover - learned recommendations, Sound Space, Song Radio
Analytics - listening trends, top artists, deep stats
AutoMix - radio-style endless queue with intensity tiers
Genre Galaxy - force-directed cosmos for exploring your listening taxonomy
Features
Library - your entire TIDAL library, synced locally and always fast
Full library sync (tracks, albums, artists, playlists) with real-time WebSocket progress, cursor-based incremental refresh, daily auto-sync, and metadata tracking
Server-side FTS search - SQLite FTS5 with prefix-weighted ranking; no preload-all, scales past 10k artists without freezing the UI; sort column applies to results
Home view: top artist hero card, recently played artist carousel, recently added album shelf, recent tracks
Trending shelf - unified Last.fm + TIDAL chart cards with country and genre scopes, lazy artwork backfill for missing covers, 6-hour shared cache so navigation between pages doesn't refetch
Artist pages (Spotify/Apple-style): blurred-artwork hero, full TIDAL discography (Albums / Singles & EPs), in-library flags, out-of-library cards linking to TIDAL preview, filter input, Tidal album play overlay
Album pages: track table, hover-reveal actions, equalizer bar on active row, "More by" shelf
Universal track-row hover cluster - same right-click and inline action menu on every track surface (queue, library, search, discover, playlists, artist/album pages, now-playing)
Duplicate detection page with ISRC matching + title/duration fallback (UI under active polish)
Bulk operations: add/remove favorites, manage playlists at scale
Decade strip filter, tile/list toggle, scroll position memory across back-navigation
Search & Command - plain text, power filters, and intent in one bar
"play tool" → plays top match immediately
"radio burial" → opens Song Radio seeded from Burial
"1994" → filters library to that year
Special searches:
/vibe → mood-based cluster search
/underrated → surfaces buried gems in your library
Ctrl+K / ⌘K - global command palette, reachable from anywhere (including inside Quiet Mode). Slash commands, quick-nav, and per-result action menus (Play now / Play next / Queue / Song radio / Go to artist) without leaving the keyboard. ArrowRight on the active row opens the actions menu; clicking the ⋯ icon toggles it.
Recent searches auto-save as clickable chips.
Search filter grids - clicking a single-category pill (Artists / Albums / Tracks / Playlists) expands to a multi-row grid instead of a cramped carousel; All keeps the horizontal overview carousels.
Lossless hi-fi streaming via TIDAL with PKCE login, encrypted token storage, automatic token refresh, and MPEG-DASH segmented streaming for high-bitrate sources
True DASH segment seek - dragging the scrubber anywhere on a fresh TIDAL track triggers a forced engine restart at the nearest DASH segment boundary instead of snapping back to the buffered region; in-buffer seeks still take the fast path
Buffered-bar scrubber - the now-playing progress bar renders the decoded region behind the playhead as a visual cue; pre-resolve / no-segment targets get an HTTP 409 ack with a corrective live snapshot so the UI snaps back cleanly
WASAPI exclusive-mode bit-perfect output (Windows) with quality live-apply, sample-rate-follow, idle device release, and shared-mode fallback
Audio device enumeration + live device switching (DeviceSwap rebuilds the output stream); sample rate follows source on track transition
Gapless playback: NearEnd event fires 15 s before track end, triggering pre-buffer engine swap for zero-gap transitions
BPM-aligned crossfade snap and per-track fade-in / fade-out; bit-perfect exclusive mode suppresses crossfade when needed
Automix: automatic queue continuation with Camelot + BPM + energy harmonic multipliers; harmonic match indicators on every queue row
Now-playing panel shows Camelot wheel key, BPM badge, and full queue
Queue redesign: drag-to-reorder, expanded layout (slim artwork + compact transport), Q hotkey to toggle, total-duration formatter, save-as-playlist, clear-with-undo (Z within 6 s)
Toast-based player error UX with retry, manual close, and 6 s auto-dismiss
Videos - search and play TIDAL music videos natively
Dedicated /videos page - search TIDAL's full video catalogue by artist, song, or session type
Full in-app HTML5 video player powered by hls.js: no redirects, no external browser tabs
Quality selector - Max (highest available) or Adaptive (hls.js ABR); persists across sessions
Autoplay - queues the next search result automatically; an "Up next" pill appears 15 s before the current video ends with the next title
Fullscreen with auto-hiding controls; native HLS fallback for Safari
Video mixes - TIDAL curated video playlists shown as a dedicated Video Mixes shelf on the home page (separate from Music Mixes); clicking a card navigates to /videos, loads the full playlist, and starts playback immediately
Video session panel - while /videos is active the desktop sidebar swaps from the audio now-playing panel to a compact video queue panel showing current artwork, up-next list, and an autoplay toggle; audio mini-player and now-playing sheet are hidden on mobile
Concurrent audio paused automatically when a video starts to prevent dual playback
Genre Galaxy - force-directed cosmos of your taxonomy ⚠️ in polish
⚠️ Live but needs polish - several interaction and rendering issues under active work.
Interactive force-directed canvas of your entire genre taxonomy - 285 genres across 14 families
Nodes sized and coloured by listen heat; edges drawn by genre co-occurrence
Drill into any genre: artist cluster view, full track list, per-genre audio metric summary
Mix this genre: loads tracks, shuffles queue, drops you at a random entry point
Seed Mix Builder: blend multiple genres, interleave their tracks, and play
Four view modes: Heat, Co-occurrence, Cohort, Evolution. Auto-drift pans the canvas
Discover / Sound Space - learned similarity canvas + Song Radio + intensity-tiered training
⚠️ Work in progress - functional but incomplete. UI and model quality still evolving.
Force-directed canvas of your library positioned by learned audio similarity
Hyperspace search: type a mood or reference and fly to the matching cluster
Nebula halos mark previously explored regions
Hover tooltip card with track preview, audio metric chips, and quick actions
Hybrid auto-seed + lock pill: pin a seed track to keep the canvas focused while you drift
Last.fm node resolution state machine - external recommendations resolve to TIDAL on demand with shimmer / resolved / unavailable states
Song Radio: plays outward from any track using learned neighbor embeddings; creativity slider controls exploration vs. exploitation; honours seed_tidal_id for non-library tracks
Feedback (like, dislike, queue, save) feeds back into the model
Prompt Explore: steer the engine with natural language - mood, reference artist, DJ style
Embedding pipeline trains on transitions, playlists, albums, genres, and listen sessions; incremental refresh + full retrain with live progress
Training intensity tiers - Max (96d / 64 neighbors / 8-track window), Medium (64d / 32 / 5), Low (48d / 24 / 3, audio-proxy stage skipped). Discovery engine controls live under Settings → Audio; the engine remembers
Real Stop - cancel checks live inside every long-running trainer stage, so the Stop button responds within ~1s instead of waiting for the current stage to finish
Safety preview - track count + active intensity → expected wall time + peak RAM + green/amber/red recommendation, blended 70/30 with your last successful run's actual duration
Live ETA - derived from the run's progress + start time, refreshed every WebSocket tick
How discovery works - when to retrain, what activates a model
The discovery engine learns a similarity space over your library: every track gets a vector, and each track's top neighbours are pre-computed and stored. Radio, Automix, "more like this", and the discover page all read those neighbours. Until a trained model is active, those surfaces fall back to a metadata-only heuristic (same artist / genre / decade), which is flat - same handful of tracks every time.
When to retrain
First time after syncing your library - there's no model yet.
After a big sync - new tracks have no neighbours until you retrain.
After a few weeks of listening - the model improves with real plays. New transitions teach it which tracks belong together.
You don't need to retrain often. Once a week or so when you've added music or listened a lot. Daily is overkill.
Incremental refresh vs Full retrain
Incremental refresh reuses the cached audio-proxy features from the last run and only rebuilds the behavioural + similarity stages. Faster - typically 30–50% of a Full Retrain wall-clock. Use this for routine refreshes.
Full retrain wipes the audio cache and recomputes everything from scratch. Use this if you've changed intensity tier, suspect the cache is stale, or it's been a long time since the last clean rebuild.
On the very first run there's nothing cached, so both buttons do identical work.
Intensity tiers
Max (96-dim, 64 neighbours, 8-track context) - best radio quality, slowest. Recommended for libraries under ~10k or overnight runs.
Medium (64-dim, 32 neighbours, 5-track context) - the default. Indistinguishable from Max for most listening; ~50% of the wall-clock.
Low (48-dim, 24 neighbours, 3-track context) - skips the audio-proxy stage entirely. Cold tracks lose their metadata anchor, but the engine stays usable on modest hardware.
Why a model might not activate
A run can complete with full coverage but still leave Active model on Fallback only. The activation gate scales with how much you've actually listened:
0 plays - needs ≥ 50% coverage. Cold-start mode.
1–49 plays - needs ≥ 70% coverage. Recall@10 isn't reliable on a tiny held-out set, so the gate looks at coverage only.
50+ plays - needs ≥ 85% coverage AND ≥ 15% recall@10. Full strict gate.
If you complete a run and the model doesn't activate, you'll usually see Coverage well above the relevant threshold but Active model still says Fallback. That means recall didn't clear - keep listening, retrain again in a week, and the gate will pass naturally.
Radio orchestration - the thing that picks the next track when Automix or Song Radio is running
Canonical TasteVector powering both Automix and Song Radio - single scoring model so the two surfaces agree on what "similar" means
Genre coherence scoring via weighted Jaccard against the seed's genre set
Engine slot filled from precomputed track_similarity pairs to keep latency low
Reason plumbing through the queue with structured suffix - every queued track carries a human-readable "why is this here?" string
Per-edge diagnostics - every neighbor row carries confidence, support_count, candidate_in_degree (+ percentile), primary_reason, plus play_count_seed/candidate. Per-reason held-out hit-rate persisted to discovery_diagnostics so metadata-bonus weights can be calibrated against real predictive value
Tier 2 re-ranker (flag-gated) - source-score normalization (rank + percentile-clipped hybrid), soft confidence penalty, hub penalty using in-degree percentile, constraint-based diversity rerank (artist / album spacing, genre saturation, recent-track skip, penalty relaxation pass), soft source-quota bonus. Each behavior independently toggleable; one-flip kill-switch reverts to the legacy interleave path with no deploy
radio_diagnostics table - every queue records target-vs-actual source mix, penalty counts, average confidence + hub percentile, and which flags were active
Pending Last.fm rows - non-library Last.fm radio results queue immediately as pending placeholders and resolve to TIDAL playables in a background pool at play time; aggressive GC keeps the table clean
Diagnostic harness available as a permanent debug tool for evaluating candidate funnel quality
Sportify anonymous Spotify proxy - browse and play any Spotify playlist natively without a Spotify subscription; Spotify→TIDAL track resolution with local metadata caching (tracks, albums, artists, playlists) and a negative-cache to avoid repeated lookup failures; DB migrations 031-032
Spotify playlist view (/spotify-playlist/[id]) - full track list with lazy TIDAL artwork resolution, playback via resolved TIDAL IDs, and inline save-to-library actions
Rule-based smart playlists with AND/OR logic: genre, artist, date range, quality tier, play count, BPM, key, Camelot, energy, danceability, instrumental-only, sample-data presence
DSP audio analysis runs passively during playback with a user-facing toggle: BPM, key, Camelot, LUFS, energy, danceability, beat strength, spectral centroid, stereo width
Duplicate detection via ISRC matching with title/duration fallback
MusicBrainz enrichment (ISRC-first + title fallback, rate-limited); portable MusicBrainz snapshot for offline transfer between installs
Last.fm genre pipeline: closed taxonomy, orphan-node fix, hierarchy-aware merge; artist.getsimilar fallback when track-level recall is empty
Automix page (recently reimagined): full surface for tuning the harmonic mixing engine
Mobile remote - installable iOS/Android PWA at /remote for controlling NOORwave from your phone
Installable PWA - open http://<your-machine>:3334/remote from a phone on the LAN, "Add to Home Screen", and it launches standalone (no browser chrome). 512px icon, manifest, dedicated service worker with N=2 cache retention so a server rebuild doesn't 404 lazy chunks for clients still running the previous bundle
Bounded-scroll shell - the player surface stays viewport-sized; sub-pages scroll inside a contained region so iOS rubber-band can't latch a stuck scroll. Persistent /remote/+layout.svelte keeps the action sheet, blurred backdrop, MediaSession bridge, silent-audio keepalive, and wake-lock alive across navigations so jumping between pages is instant
Transport - full playback, seek and volume sliders (guarded against ticker overwrites mid-drag), like, shuffle, repeat, automix, queue, sleep timer
Sub-pages - /remote/library, /remote/artists/[id], /remote/albums/[id], /remote/playlists/[id], plus TIDAL previews at /remote/tidal/artists/[id] and /remote/tidal/albums/[id]. Library page has Artists / Albums / Tracks segmented control (Tracks defaults to liked-only, sorted by date added)
Mini search sheet - Library / TIDAL / Playlists tabs with debounced server-side search, race-safe result merging, and inline play / queue / radio actions
Long-press action sheet - same buildTrackMenu as the desktop right-click menu, presented as an iOS-style sheet. Swipe down to dismiss with composed-transform animation; tap-through is suppressed during the dismiss tween
Haptics - navigator.vibrate fires on every tap, long-press, and transport action when the device supports it
iOS PWA reliability pass - touch-action: manipulation and parallel pointerup/click on every tap surface to suppress 300ms tap delay and ghost clicks under backdrop-filter stacking contexts; passive pointer listeners so single-finger scroll handoff doesn't fight gesture arbitration
Sleep timer with stale-fire guard - 2-minute grace window on rehydration; if the saved fireAt is older than that (laptop slept overnight) the timer is discarded rather than firing pause as soon as you unlock the phone
Five GLSL shader wallpapers: Aurora, Chrome, Grid, Nebula, Topo - sidebar and now-playing panel float as glass tiles over them, with a wallpaper palette system (Nebula / Verdant / desat variants)
Quiet Mode - fullscreen "just listen" overlay launched from a button on the now-playing artwork. Large artwork + transport, blurred backdrop, body-scroll lock, embedded ⌘K search pill. Esc cascade is deterministic across the three overlays (action menu → palette → quiet mode).
6-digit PIN auth: auto-submits on the sixth digit, numeric keyboard on mobile; local browser auto-connects; legacy tokens auto-migrate on startup
LAN access: run on one machine, open from any browser on the network; every raw fetch() carries the auth header
WebSocket-driven: playback state, sync progress, queue, training progress push instantly without polling
Global keyboard shortcuts: Space play/pause · ← → seek · ↑ ↓ volume · L like · S shuffle · R repeat · Q toggle queue · ⌘K / Ctrl+K command palette · Z undo clear-queue (within 6 s) · ? shortcuts help popover
Tauri desktop app: system tray menu (network toggle, restart, exit), global media key shortcuts, native window management, GitHub-releases auto-updater
Audio device enumeration and live switching; sample rate follows source on track transition
Dedicated TIDAL catalogue search page (/search) and TIDAL artist/album profile pages (/tidal/artists/[id], /tidal/albums/[id]) for browsing outside your library
Tech Stack
Layer
Technology
Backend
Rust 2024 edition, Axum 0.8, Rayon
Database
SQLite 3 (rusqlite), FTS5, WAL mode
Frontend
SvelteKit 2 + Svelte 5 runes, TypeScript, Vite
Desktop shell
Tauri 2
Audio decode
Symphonia 0.5
Audio output
CPAL 0.17 (cross-platform)
Real-time
Tokio broadcast channel → WebSocket
Feed parsing
RSS 2.0 + Atom syndication
Getting Started
Download (recommended)
Download the latest portable build from GitHub Releases - no install required:
Platform
File
Arch
Windows (installer)
NOORwave-vX.X.X-windows-x64-setup.exe
x64
Windows (portable)
NOORwave-vX.X.X-windows-x64.zip
x64
macOS
NOORwave-vX.X.X-macos-arm64.tar.gz
Apple Silicon
macOS
NOORwave-vX.X.X-macos-x64.tar.gz
Intel
Linux
NOORwave-vX.X.X-linux-x64.tar.gz
x64
Installer vs portable (Windows): the installer auto-updates in-place on launch and stores data under %LOCALAPPDATA%\NOORwave. The portable zip is fully relocatable and only ever notifies you about updates - you swap the build yourself. Both run the same backend.
Every build contains the same three things - keep them together:
NOORwave(.exe) ← desktop shell + system tray
noor-server(.exe) ← backend, API, audio engine
www/ ← bundled UI (do not move or delete)
Created next to the executables on first run: noor.db (library + settings), noor-server.log.
Windows (installer)
Run NOORwave-vX.X.X-windows-x64-setup.exe
Installs to %LOCALAPPDATA%\Programs\NOORwave (per-user, no admin prompt)
Library, settings, PIN, and queue live in %LOCALAPPDATA%\NOORwave and are preserved across every update
Future releases auto-install in-place on launch (see Updating)
The installer is currently unsigned by a CA. Windows SmartScreen may show "Windows protected your PC" on first run - click More info -> Run anyway. The Tauri updater payload itself is signed by the project's minisign key, so in-place updates verify the signature before installing even though SmartScreen does not.
Windows (portable)
Unzip to any folder
Double-click NOORwave.exe
Window opens when the server is ready (~2 s)
The folder is fully relocatable - rename or move it to another drive and it keeps working.
Portable builds are unsigned. On some Windows 11 installs, Smart App Control may block noor-server.exe; if that happens, the app window can open but 127.0.0.1:3334 refuses the connection because the local server never started. Advanced workaround: download the source code and run .\scripts\build-windows11-release.cmd. This creates dist\NOORwave-win11\NOORwave.exe; the result is still unsigned, so strict Smart App Control setups may still block it.
macOS
Extract the .tar.gz
Clear the Gatekeeper quarantine flag (required on first launch):
xattr -cr NOORwave noor-server
Launch:
./NOORwave
Gatekeeper blocks unsigned binaries by default. The xattr -cr command clears the quarantine flag - it is safe and equivalent to right-clicking → Open in Finder.
Linux
Extract the .tar.gz
Mark the binaries executable:
chmod +x NOORwave noor-server
Install runtime libraries if missing (Ubuntu / Debian):
Runs the backend and frontend separately. The frontend hot-reloads; the Tauri shell is not involved.
# Terminal 1 - backend
cargo run --release -p noor-server
# Terminal 2 - frontend
cd frontend
pnpm install
pnpm run dev
Open http://localhost:5173. The frontend connects to the backend on port 3334 automatically.
First Run
Open the app - the browser on the server machine auto-connects without a PIN
Remote/LAN devices: enter the 6-digit PIN shown in Settings → Access Token
Complete TIDAL PKCE sign-in in Settings
Trigger a library sync - progress streams live via WebSocket
Start playing
Environment Variables
Variable
Default
Description
NOOR_ADDR
0.0.0.0:3334
Override server bind address
NOOR_DB
<exe dir>/noor.db (workspace root in dev)
Override database path
RUST_LOG
noor_server=info
Log level
TIDAL_PKCE_CLIENT_ID
(built-in)
Override TIDAL PKCE OAuth2 client ID
TIDAL_PKCE_CLIENT_SECRET
(built-in)
Override TIDAL PKCE OAuth2 client secret
Updating
Windows has two channels: the per-user installer (silent in-app update) and the portable zip (notify-only). macOS and Linux are portable-only.
Installer (Windows) - silent in-app update
On every launch the installed app reads latest.json from the latest GitHub Release. When a newer signed setup.exe is published, the tray menu surfaces:
v0.1.52 available - click to install
Clicking it stops the local sidecar, downloads the signed setup, runs it in passive mode, and restarts the app at the new version. The NOORwave/ data folder under %LOCALAPPDATA% is preserved - your library, playlists, settings, PIN, training state, and queue all carry over. Settings -> About surfaces the same install mode, current version, and pending update inside the app.
Portable - notify-only
On every launch the Tauri shell quietly polls the GitHub Releases API. If a newer tag is available, the system tray menu gains an extra item:
↑ vX.Y.Z available - click to download
Clicking it opens the GitHub release page in your browser. No silent install on portable - swap the build yourself (see below).
Manual portable swap
Works on every platform:
Quit NOORwave
Download NOORwave-vX.Y.Z-<platform>.<ext> from Releases
Extract over the existing folder, replacing NOORwave(.exe), noor-server(.exe), and www/
Relaunch
Your library, playlists, settings, PIN, training state, and queue all live in noor.db next to the binaries - the swap doesn't touch them.
Updated by the swap
Preserved across the swap
NOORwave(.exe)
noor.db
noor-server(.exe)
noor-server.log
www/
TIDAL session, PIN, queue, training state
Platform notes
macOS - Gatekeeper quarantines every fresh download. Re-run xattr -cr NOORwave noor-server after extracting a new build.
Linux - runtime libraries (libwebkit2gtk-4.1-0, libasound2, libayatana-appindicator3-1) only need to be installed once. Subsequent updates don't require apt-get again.
Windows - the folder is fully relocatable; you can extract to a different drive and your old install keeps working until you delete it.
Known Bugs
Bug
Notes
Discover / Sound Space - functional but incomplete
UI and model quality still evolving
Song Radio - working but needs tuning
Recommendation quality varies
Genre Galaxy - live but rough edges
Several interaction and rendering issues under active work
ACRCloud sample recognition
Placeholder - not reliably functional
Playlist save failing under certain conditions
Intermittent edge case, reproducing
Current Limitations
The honest list of what NOOR doesn't do. Some of these are by design, some are not implemented yet, none are reproducible defects (those go in Known Bugs above).
Streaming sources
Requires an active TIDAL subscription. HiFi or HiFi Plus is needed for lossless playback; the standard tier streams at AAC quality.
Spotify integration is anonymous metadata only. You can browse and play any Spotify playlist, but every track is resolved to and streamed from TIDAL - there is no native Spotify playback and no Spotify Premium required.
YouTube Music and SoundCloud are not implemented. Schema columns and API client stubs exist; resolution logic and routes do not.
Beatport and Bandcamp catalogues are not implemented.
Audio output
WASAPI exclusive-mode bit-perfect output is Windows only. macOS and Linux fall back to the system mixer (CPAL shared-mode); the OS performs sample-rate conversion.
Sample-rate-follows-source is Windows-only for the same reason.
Distribution
macOS builds are unsigned - Gatekeeper quarantine must be cleared on every download (xattr -cr).
The Tauri auto-updater installs in-place on Windows installer builds (signed setup.exe from the latest release). Portable builds notify only - they don't self-install; you swap them manually. macOS and Linux are portable-only (see Updating).
No mobile app. The LAN UI works in a phone browser but isn't optimised for it.
Lyrics - no lyrics view in Quiet Mode or anywhere else.
Offline mode - no cached metadata browsing or local-file playback.
Playlist export (M3U / JSON) - not implemented.
Playlist collaboration / public read-only sharing links - not implemented.
Network
LAN access requires both devices on the same network. No tunneling, no remote-internet access by design.
Roadmap
What's already shipped ✓ (full list)
Recent release highlights
DiscoverSpace blend hardening and artwork consistency - external discovery seeds now resolve through known library anchors, blend clearing and remapping reset stale selection state, duplicate anchors no longer inflate coverage, Add discoveries uses the same pending TIDAL resolver path as Play discoveries, Make blend radio asks for a deeper route, and shared artwork rendering now keeps TIDAL CDN sizing and fallbacks consistent across command, row, and search surfaces (v0.1.56)
DiscoverSpace blend discovery - build two to four seed blends from library, TIDAL, or pending discoveries; the map now marks seed, external candidate, and library guide roles, explains blend scoring, reports blend health, and adds Add discoveries, Play discoveries, and Make blend radio actions that reuse the existing queue and resolver path (v0.1.55)
Spotify mood playlist recovery - retired Spotify editorial IDs for Party Hits and Happy Hits were replaced with current Spotify-owned playlists, and long Spotify playlist resolution now writes TIDAL match results in batches so playable rows appear while the resolver is still working (v0.1.54)
Mood playlist latency fix - /moods no longer prefetches full Spotify playlists just to render mood cards; TIDAL mood pages and playlist track pages now use in-memory caches so repeated Happy / Focus playlist opens return from cache instead of re-hitting TIDAL or competing with Sportify proxy calls (v0.1.53)
TIDAL resolver hydration trim - Spotify-to-TIDAL resolution skips unnecessary candidate hydration work when cached or clear-enough matches are already available, reducing resolver tail latency and upstream TIDAL pressure (v0.1.53)
Playback queue starts and shuffle seeding - TIDAL playlist, mix, library, and genre starts now seed playback order predictably, preserving direct-start behavior while avoiding broken queue starts after shuffle or mood entry points (v0.1.53)
Windows installer with signed auto-updater - per-user NSIS installer alongside the existing portable zip; installed builds read latest.json from the latest GitHub Release on launch, surface a tray hint, and silently download + install the signed setup.exe in passive mode before restarting at the new version; portable builds keep their notify-only tray hint. Settings -> About now surfaces install mode, current version, and pending update. Installs to %LOCALAPPDATA%\Programs\NOORwave; library + settings live under %LOCALAPPDATA%\NOORwave and are preserved across updates. MusicBrainz / Last.fm portable snapshots reused across installed and portable layouts (v0.1.52)
[~] Spotify public stats via anonymous partner-GraphQL(best-effort) - partner-GraphQL fetcher with anonymous token mint, GraphQL-hash auto-refresh, request-coalescing cache, schema migrations, and artist-page stats rendering. No official Spotify credentials required, but Spotify can reject anonymous token minting (e.g., TOTP cipher rotation that turns /api/token into 400 Bad Request) - when that happens stats stay null and the panel degrades gracefully. TIDAL track resolution does not depend on this path (v0.1.52)
Untapped Spotify and TIDAL discovery surfaces - new top-level /charts and /moods pages; new Sportify detail pages at /spotify-album/[id], /spotify-artist/[id], /spotify-track/[id]; TIDAL discover shelves extracted into a shared TidalDiscoverShelves component so search, charts, and moods all share one card treatment (v0.1.52)
Discovery polish, caching, proxy resilience - unified card hover pattern (transparent border lift, art-wrap with hover zoom, PlayOverlay only where clicks actually play) across /charts, /moods, home mood/chart rails; 6-hour shared cache + cold-load skeleton on the home moods rail; curated Spotify mood rails on /moods drill-downs; Sportify outage recovery with single auto-retry, friendly error, and explicit Retry button on the playlist view; fixed a motion-token easing footgun that was silently dropping hover transitions on 12 card files (var(--motion-base) ease is invalid CSS - browsers drop the whole rule) (v0.1.52)
Sportify -> TIDAL first resolution - the Sportify resolver routes Spotify tracks through TIDAL search before falling back, with hardened anonymous-token and GraphQL-hash refresh for the public-stats client (v0.1.52)
Now-playing badge-row stability - state badge locked to a stable min-width and the chip row set to nowrap so the wider 'Scrubbing' pill no longer reflows the stream chip onto a second line; stream detail string also drops the redundant quality tier label since bit depth / sample rate / Excl already convey it (v0.1.52)
True DASH segment seek (option C) - past-buffer scrubber targets on TIDAL tracks now trigger a forced engine restart at the nearest DASH segment boundary; absolute-track sample accounting in the runtime; evaluate_seek_decision moved into the playback module with both bounds ([offset, buffered]); POST /api/playback/position accepts allow_segment_seek and returns 202 / 409 / 500 honestly (v0.1.51)
Buffered-bar scrubber + route-side seek ack - now-playing progress bar renders the decoded region behind the playhead; redirectable buffered_source mirror on the runtime handle survives Switch and crossfade promotion; route-side 409 with a live snapshot for pre-resolve targets so the UI snaps back instead of fighting WS resyncs (v0.1.51)
Playback runtime hardening - error / panic / cancel observability rework: every long-running stage observes a stop flag; orphan decoder threads tracked via a background-join helper so Stop returns within ~250ms; transition failures emit typed Error+Stopped event pairs; one-shot growth-warn telemetry on unbounded buffer drift (v0.1.51)
Artist / album page fixes - corrected discography ordering and restored missing sections on artist pages (v0.1.51)
Mobile remote PWA - installable /remote surface (512px icon, manifest, service worker with N=2 cache retention). Bounded-scroll shell with persistent /remote/+layout.svelte so action sheet, blurred backdrop, MediaSession bridge, silent-loop keepalive, and wake lock survive sub-page navigation (v0.1.50)
Remote sub-pages - artists, albums, playlists, and TIDAL artist/album previews live inside /remote/* so navigation from the phone never falls into the heavy desktop chrome. Library page adds an Artists / Albums / Tracks segmented control (Tracks defaults to liked-only, sorted by date added) (v0.1.50)
Long-press action sheet + mini search - iOS-style sheet with swipe-to-dismiss and ghost-click suppression; Library / TIDAL / Playlists tabs in the search sheet with race-safe result merging (v0.1.50)
Sleep timer with stale-fire guard - 2-minute grace window on rehydration so a saved fireAt from an overnight-slept device is discarded instead of firing pause the moment you unlock (v0.1.50)
iOS PWA reliability pass - touch-action: manipulation and parallel pointerup/click on tap surfaces to suppress 300ms tap delay and ghost clicks; passive pointer listeners so single-finger scroll handoff isn't fighting gesture arbitration; back button no longer needs multiple taps (v0.1.50)
Seek/volume slider hardening - guarded against ticker overwrites mid-drag so the thumb doesn't snap back while you're dragging (v0.1.50)
Discovery album linking - injecting TIDAL discovery tracks now resolves albums by tidal_id with ON CONFLICT DO NOTHING, no fuzzy stubs (v0.1.50)
Svelte 5.55.7 - XSS fix bump (v0.1.50)
Settings cleanup - Discovery engine training, intensity, safety preview, progress, and active-model controls now live under Audio instead of a separate top-level Discovery tab (v0.1.44)
Incremental TIDAL library sync - favorite cursors let routine syncs stop once they hit known items, while full sync markers remain intact (v0.1.44)
WASAPI exclusive renderer reliability - format recovery, device-busy retry caps, active-source swapping, idle release, and gapless rules tightened across the exclusive path (v0.1.44)
Passive audio-analysis performance - already-analyzed tracks are skipped before sampling and analysis work is capped during normal playback (v0.1.44)
External discovery resolution - sidecar TIDAL matches can resolve candidate rows without importing unrelated tracks into the main library (v0.1.44)
Server route split - the large route surface is now split into focused analytics, audio-analysis, chart, discovery, duplicates, enrichment, genre, search, Sportify, TIDAL home, and TIDAL sync modules (v0.1.44)
TIDAL auth and streaming hardening - PKCE login, encrypted token storage, returned quality details, lossless DASH streaming, and cleaner external sign-in failures (v0.1.40-v0.1.43)
UI zoom and shell polish - browser-style zoom, Tauri WebView zoom support, shell navigation extraction, player bar extraction, route registry, and layout contract coverage (v0.1.30-v0.1.44)
DSP and design-system refresh - dynamic-programming beat tracking, spectral-flux onset detection, perceptual tempo logic, fluid typography tokens, and the liquid-glass visual system (v0.1.27-v0.1.29)
Multi-platform portable releases - Windows x64, Linux x64, macOS ARM64, and macOS x64 builds ship from GitHub Releases with checksum publishing (v0.1.19-v0.1.44)
Foundation
Discovery engine with embedding-based learning
Similar Radio with creativity and context controls
Home page with RSS-driven new releases, daily picks, articles, and news
Spotify auth and genre enrichment
Audio feature extraction (BPM, key, energy, danceability via DSP)
Genre Galaxy visualization with heat, co-occurrence, cohort, and evolution views
Genre Mix: randomised entry point, seed blend builder
Discovery Sound Space with hyperspace search and nebula halos
Automix harmonic mixing (Camelot + BPM + energy multipliers)
Like button with local + TIDAL sync; optimistic hearts
Queue redesign: drag-to-reorder, expanded layout (slim artwork + compact transport), Q hotkey, total-duration formatter, save-as-playlist, clear-with-undo (Z within 6 s)
Toast-based player error UX with retry, manual close, 6 s auto-dismiss
Surface radio reasons + shuffle mode labels + action microcopy on every queue row
Stale is_playing cleared on server restart
External TIDAL queue actions (play next / append) for non-library tracks (v0.1.17)
Unified playability states across all song cards (search, discover, trending) (v0.1.17)
Song radio loading toast with proper dismiss on all paths; ephemeral TIDAL queue advance on next_track (v0.1.17)
Automix suspend for 60 s after manual queue clear (v0.1.17)
TIDAL Video Playback - dedicated /videos page: search TIDAL's video catalogue, full in-app HLS player (hls.js), quality selector, autoplay with up-next pill, video mixes shelf, fullscreen, concurrent audio paused on play (v0.1.18)
Video session integration - home page splits Music Mixes and Video Mixes into separate shelves; clicking a video mix card navigates to /videos and starts playback automatically; desktop sidebar swaps to a compact video queue panel with up-next list and autoplay toggle during active video sessions; mobile audio chrome hidden during video playback (v0.1.19)
Multi-platform releases - Linux x64, macOS ARM64, macOS x64 portable builds published to GitHub Releases alongside Windows (v0.1.19)
Song Radio cold-start fallback - when all candidate sources return empty (newly-synced tracks not yet in the learning model), radio now surfaces other tracks by the same artist instead of failing silently (v0.1.18)
TIDAL Your Mixes token auto-refresh - the shelf no longer loops on 401 errors; refreshes inline like all other TIDAL endpoints (v0.1.18)
RSS feed fixes - removed defunct Resident Advisor feed; updated Pitchfork and Mixmag to current URLs; fixed Bandcamp Daily parse failure caused by UTF-8 BOM (v0.1.18)
Play All on TIDAL albums and playlists now plays beyond track 1 (v0.1.26)
TIDAL Retry-After honored + concurrent-request cap to avoid 429 lockouts (v0.1.26)
Per-album TIDAL sync timeout + retry - sync no longer stalls on slow album lookups (v0.1.26)
NOOR uses TIDAL's unofficial API via a PKCE OAuth2 flow. This project is not affiliated with, endorsed by, or associated with TIDAL Music AS or MQA Ltd. Use is at your own discretion and risk. Credentials are stored locally, AES-GCM encrypted in the SQLite database. NOOR is intended for personal use only.
A convention so the bug list, roadmap, and limitations don't drift. Future-me, this is for you.
Section semantics
Features - present-tense capabilities only. If you can't run it today, it doesn't go here.
Known Bugs - reproducible defects on the current version only. If it's intentional or unimplemented, it lives in Current Limitations instead.
Current Limitations - by-design constraints AND unimplemented integrations. The "won't fix soon" pile.
Roadmap → Up next - actively planned with intent to land in the next few releases. Speculative items don't go here; they belong in Current Limitations under "Functionality not yet wired".
Roadmap → What's already shipped - every entry tagged with the (vX.Y.Z) it shipped in.
For each closed bug since the last tag: remove the row from Known Bugs, add a bullet to What's already shipped with (vX.Y.Z).
For each shipped roadmap item: remove from Up next, add to What's already shipped with (vX.Y.Z).
Adjust Current Limitations as constraints change - when ACRCloud is wired, move it from "Functionality not yet wired" into a Features bullet; when Linux gets a bit-perfect output path, qualify the WASAPI line; etc.
Keep the Windows 11 Smart App Control note in the release boilerplate. If Windows signing is added later, update both the release workflow body and the Windows section above.
After git tag + git push --tags: gh release edit vX.Y.Z to prepend a "What's new" section to the auto-generated release body. CI only emits portable-build boilerplate.
Verification before declaring a section accurate
Bug status - git log v<prev>..HEAD --oneline | grep -i <keyword>. If a closing commit exists, the bug is closed.
Roadmap status - same query. If shipped, move it.
Features - confirm the route or code path actually exists. Don't trust memory.
Versions - every (vX.Y.Z) tag in What's already shipped must match an entry in git tag -l.