Saxon
A high performance local music player with Navidrome support
Install / Use
/learn @kernelstub/SaxonREADME
- Saxon is a modern music player engineered with Tauri + Rust (native backend) and React + TypeScript (UI).
- It focuses on a fast, lightweight desktop experience with a responsive dark UI, virtualization for large libraries, and an audio pipeline that supports EQ, normalization, and crossfade.
- Supports both local libraries (folder scans) and Navidrome (Subsonic API) streaming.
| Area | What You Get | |------|--------------| | Audio | 10-band EQ, presets, crossfade, volume normalization | | Library | Folder scanning, search, favorites, recents, cover-art caching | | Performance | Backend scan cache + revision hash + virtualized list rendering | | Providers | Local library + Navidrome (Subsonic REST API) | | Platform | Tauri desktop app (Windows/Linux) |
<h1 id="features">Features</h1>Audio
| Feature | Description | |--------|-------------| | 10-band EQ | Parametric-style EQ bands with presets and custom tuning | | Crossfade | Smooth transitions with configurable fade duration | | Normalization | Optional dynamic range compression for consistent loudness | | WebAudio chain | MediaElement → EQ → Compressor → Master Gain |
Library & Playback
| Feature | Description | |--------|-------------| | Folder scanning | Recursive scan of local music folders (MP3/WAV/OGG/FLAC/M4A/AAC) | | Cover art | Embedded cover extraction (local) + cover URLs (Navidrome) | | Fast browsing | Virtualized track list for very large libraries | | Search | Title/artist/album filtering | | Favorites | Saved list of tracks for quick access | | Recents | Recently played tracking with ordering |
UI/UX
| Feature | Description | |--------|-------------| | Dark UI | Minimalist dark-themed interface | | Frameless window | Custom header + window controls | | Sidebar navigation | Switch between Library / Now Playing / Favorites / Recent |
<h1 id="providers">Providers</h1>| Provider | Browse | Stream | Cover Art | Notes |
|---------|--------|--------|----------|------|
| Local folders | ✅ | ✅ | ✅ (embedded tags) | Uses Tauri FS access + tag parsing |
| Navidrome (Subsonic) | ✅ | ✅ | ✅ (getCoverArt) | Uses Subsonic REST API (ping, getArtists, getAlbum, stream, getPlaylists) |
Navidrome library organization:
- Root: All Tracks, Artists, Playlists
- Artists: Artist → Album → Tracks
- Playlists: Playlist → Tracks
Saxon uses a native Rust backend for filesystem + metadata and exposes commands to the React UI through Tauri.
flowchart LR
UI[React UI] -->|"invoke()"| Tauri[Tauri Commands]
Tauri -->|scan_music_library| LocalScan[Local Folder Scan]
Tauri -->|get_cover_art| LocalCover[Embedded Cover Extract]
Tauri -->|navidrome_scan_library| NDScan[Navidrome / Subsonic Client]
NDScan -->|REST calls| Navidrome[(Navidrome Server)]
UI -->|HTMLAudioElement| Audio[Playback]
Audio -->|MediaElementSource| WebAudio[WebAudio EQ / Compressor]
<h1 id="download">Download</h1>
- Pre-compiled binaries available on the Releases page
- Windows:
.exeinstaller - Linux:
.deb,.rpm,.AppImage
Prerequisites
| Tool | Notes | |------|------| | Node.js | LTS recommended | | Rust | Stable toolchain | | Windows | Visual Studio C++ Build Tools | | Linux | WebKit2 GTK + build tools for Tauri |
Dependencies
Before running this project, make sure the following dependencies are installed:
Ubuntu / Debian
sudo apt update
sudo apt install -y \
libglib2.0-dev \
pkg-config \
libwebkit2gtk-4.1-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
libssl-dev \
build-essential
Fedora
sudo dnf install -y \
glib2-devel \
pkgconfig \
webkit2gtk3-devel \
libayatana-appindicator3-devel \
librsvg2-devel \
openssl-devel \
@development-tools
Arch Linux / Manjaro
sudo pacman -S --needed \
glib2 \
pkgconf \
webkit2gtk \
libayatana-appindicator \
librsvg \
openssl \
base-devel
macOS (using Homebrew)
brew install \
glib \
pkg-config \
webkit2gtk \
libayatana-appindicator3 \
librsvg \
openssl
Setup
git clone https://github.com/kernelstub/Saxon.git
cd Saxon
npm install
Run in dev:
npm run tauri dev
Build:
npm run tauri build
Build outputs:
| Platform | Output Folder |
|----------|---------------|
| Windows | src-tauri/target/release/bundle/nsis/ |
| Linux | src-tauri/target/release/bundle/deb/ and AppImage bundle output |
Saxon stores configuration in config.json inside the app config directory.
| Key | Type | Meaning |
|-----|------|---------|
| musicFolders | string[] | Local music folders to scan |
| favorites | string[] | Favorite track canonical IDs |
| recentTracks | string[] | Recently played track canonical IDs |
| eqEnabled | boolean | Enable EQ |
| eqPreset | string | EQ preset (flat, bass, treble, etc.) |
| eqValues | number[] | 10-band EQ values (0–100) |
| crossfade | number | Crossfade seconds |
| normalize | boolean | Enable normalization compressor |
| navidromeServers | object[] | Saved Navidrome servers (token+salt auth) |
| showWindowControls | boolean | Show minimize/close buttons in header |
| selectedTheme | string | Selected theme name from color.ini |
| discordRichPresence | boolean | Enable Discord Rich Presence |
Navidrome server object:
| Field | Meaning |
|------|---------|
| name | Display name in the library tree |
| baseUrl | Navidrome base URL (e.g. https://navidrome.example.com) |
| username | Subsonic username |
| token / salt | Subsonic token auth (derived; password not stored) |
| enabled | Toggle server on/off |
Theme (color.ini)
Saxon can be themed by editing color.ini in the same app config directory as config.json. The file is auto-created with defaults on first run.
Themes are INI sections:
[default]
background=#0f0f0f
foreground=#f2f2f2
[oled]
background=#000000
card=#000000
border=#111111
Each entry is key=value. Values are written directly into CSS variables, so they can be any valid CSS value (for example #RRGGBB, #RRGGBBAA, rgb(...), oklch(...), or 0.625rem for radius).
Supported keys:
| Key | What it controls |
|-----|------------------|
| background | App background |
| foreground | App text |
| card | Card surfaces |
| card-foreground | Card text |
| popover | Popover surfaces |
| popover-foreground | Popover text |
| primary | Primary accent |
| primary-foreground | Text on primary |
| secondary | Secondary surfaces |
| secondary-foreground | Text on secondary |
| muted | Muted surfaces |
| muted-foreground | Muted text |
| accent | Accent surfaces |
| accent-foreground | Text on accent |
| destructive | Destructive (danger) |
| destructive-foreground | Text on destructive |
| border | Borders |
| input | Inputs |
| ring | Focus rings |
| chart-1 | Chart color 1 |
| chart-2 | Chart color 2 |
| chart-3 | Chart color 3 |
| chart-4 | Chart color 4 |
| chart-5 | Chart color 5 |
| radius | Border radius |
| sidebar | Sidebar background |
| sidebar-foreground | Sidebar text |
| sidebar-primary | Sidebar primary accent |
| sidebar-primary-foreground | Text on sidebar primary |
| sidebar-accent | Sidebar accent |
| sidebar-accent-foreground | Text on sidebar accent |
| sidebar-border | Sidebar border |
| sidebar-ring | Sidebar focus ring |
| scrollbar-thumb | Scrollbar thumb |
| scrollbar-thumb-hover | Scrollbar thumb hover |
| range-track | Range/slider track |
| range-thumb | Range/slider thumb |
Useful Commands
| Command | What it does |
|--------|---------------|
| npm run dev | Starts Vite dev server |
| npm run build | Typecheck + build web assets |
| npm run tauri dev | Runs the desktop app in dev |
| npm run tauri build | Builds desktop bundles |
Tauri Commands (backend API)
| Command | Purpose |
|--------|---------|
| scan_music_library(path) | Scan local folder and return tracks/folders |
| get_cover_art(path) | Extract embedded cover art (local) |
| load_config() / save_config(config) | Persist configuration |
| navidrome_create_server(...) | Validate creds and create token+salt server entry |
| navidrome_test_connection(serverId) | Ping a configured server |
| navidrome_scan_library(serverId) | Fetch library tree/tracks from Navidrome |
| Layer | Tech | |------|------| | Frontend | React, TypeScript, Tailwind CSS, Radix UI, Lucide | | Backend | Rust + Tauri 2.x | | Build | Vite |
