A modern, LMS-compatible music server written in Python
An independent reimplementation of the
Lyrion Music Server
(formerly Logitech Media Server / SlimServer), built from scratch in Python with asyncio.
Controls Squeezebox hardware, Squeezelite, and works with iPeng, Squeezer, and other LMS-compatible apps.
Disclaimer — Resonance is a hobby project, not affiliated with or endorsed by the Lyrion / LMS project. It is under active development, not finished, and will contain bugs. When protocol behavior is unclear, the LMS source code is the reference. LLMs are used extensively as a coding partner throughout development. The developer only owns a single Squeezebox Radio — other hardware (Touch, Boom, Transporter, Classic, Controller) has not been tested. Feedback and bug reports are very welcome!
pip install resonance-server
resonance
That's it. The server starts on ports 3483 (Slimproto), 9000 (HTTP/API), and 9090 (CLI). The package includes the Web UI and all core plugins — no extra build steps needed.
Open http://localhost:9000 in your browser to access the Web UI.
Players on the same subnet may discover the server automatically via UDP broadcast.
If not, point your player to the server IP manually (e.g. squeezelite -s <server-ip>).
→ See First Steps for what to do next.
Options:
-v, --verbose Enable debug logging
-p, --port PORT Slimproto port (default: 3483)
--host HOST Bind address (default: 0.0.0.0)
--web-port PORT HTTP port (default: 9000)
--cli-port PORT Telnet CLI port (default: 9090, 0 to disable)
--version Show version
If you prefer to run from a git checkout (e.g. for development):
Linux / macOS:
git clone https://github.com/endegelaende/resonance-server.git
cd resonance-server
python3 -m venv .venv
.venv/bin/python -m pip install -e .
.venv/bin/python -m resonance
Windows (PowerShell or cmd.exe):
git clone https://github.com/endegelaende/resonance-server.git
cd resonance-server
python -m venv .venv
.venv\Scripts\python.exe -m pip install -e .
.venv\Scripts\python.exe -m resonance
Note: When running from source, build the Web UI separately:
cd web-ui && npm install && npm run build— see Web UI for details.
Resonance ships with a multi-stage Dockerfile and a docker-compose.yml for easy deployment.
The image includes all audio transcoding tools (faad, flac, lame, sox, ffmpeg) and the
pre-built Svelte Web-UI — no extra setup required.
Note: The Compose file uses
network_mode: hostso that Squeezebox UDP discovery broadcasts on port 3483 reach the container. Docker bridge mode does not forward LAN broadcasts, which prevents players from finding the server automatically.
Quick start with Docker Compose:
# 1. Clone the repository
git clone https://github.com/endegelaende/resonance-server.git
cd resonance-server
# 2. Configure your environment
cp .env.example .env
# Edit .env — at minimum set MUSIC_DIR to your music library path
# 3. Build and start
docker compose up -d
# 4. Open the Web UI
# http://localhost:9000
# 5. View logs
docker compose logs -f resonance
Standalone Docker run (host network):
docker build -t resonance-server .
docker run -d \
--name resonance-server \
--network host \
--restart unless-stopped \
-v /path/to/music:/music:ro \
-v resonance-data:/app/data \
-v resonance-cache:/app/cache \
resonance-server
If you have been running Resonance natively before, mount your existing
cache/server_uuidinto the container (-v ./cache/server_uuid:/app/cache/server_uuid:ro) so the server keeps the same identity. Squeezebox Radio blacklists UUIDs of servers it previously failed to connect to, so a consistent UUID is essential.
| Variable | Default | Description |
|---|---|---|
MUSIC_DIR |
./music |
Host path to your music library (mounted read-only) |
LOG_LEVEL |
INFO |
Log level: DEBUG, INFO, WARNING, ERROR, CRITICAL |
RESONANCE_DISPLAY |
0 |
Enable bitmap display rendering for SB2/3/Classic/Boom (set to 1 after hardware verification) |
See .env.example for the full reference.
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Web-UI / │ │ │ │ Squeezebox │
│ iPeng / │◄────►│ Resonance │◄────►│ Radio/Touch │──► ))
│ Squeezer │ HTTP │ Server │Slim- │ Squeezelite │
│ │ │ (Python) │proto │ │
└─────────────┘ └──────┬──────┘ └─────────────┘
│
┌──────┴──────┐
│ SQLite │
│ Music DB │
└─────────────┘
Resonance speaks the same protocols as LMS. The server gives commands, players execute.
| Port | Protocol | Purpose |
|---|---|---|
| 3483 | Slimproto (TCP) | Binary player control |
| 9000 | HTTP | Streaming + JSON-RPC + Web UI |
| 9090 | Telnet CLI | Text-based command interface |
| Feature | Status |
|---|---|
| Slimproto (binary player control) | Yes |
| JSON-RPC API (LMS-compatible) | Yes |
| Cometd/Bayeux (real-time push) | Yes |
| Telnet CLI (Port 9090) | Yes |
| UDP Discovery (auto-detect) | Yes |
| Jive Menu System (Radio, Touch, Boom, Controller) | Yes |
| Feature | Status |
|---|---|
| HTTP Streaming (MP3, FLAC, OGG, WAV) | Yes |
| On-the-fly Transcoding (M4A, M4B, AAC, ALAC) | Yes |
| Internet Radio (radio-browser.info via plugin) | Yes |
| Podcasts (RSS + PodcastIndex via plugin) | Yes |
| Remote URL Proxy (HTTPS → HTTP for hardware) | Yes |
| Gapless Playback | Yes |
| Crossfade (configurable overlap) | Yes |
| ReplayGain (track & album mode) | Yes |
| Seeking (byte-accurate & time-based) | Yes |
| DSD/DoP (DSF/DFF, native + transcode) | Yes |
| Feature | Status |
|---|---|
| Music Library (scanner, SQLite, full-text search) | Yes |
| Cover Art (extraction, caching, BlurHash placeholders) | Yes |
| Playlist / Queue (shuffle, repeat, insert, move) | Yes |
| Favorites (hierarchical folders, LMS-compatible) | Yes |
| Alarm Scheduling (per-player) | Yes |
| Device Capabilities (volume curves, hardware flags) | Yes |
| Plugin System (commands, menus, content providers) | Yes |
| Server-Driven UI (plugins build web pages in Python) | Yes |
| Security Headers (CSP, X-Frame-Options, etc.) | Yes |
| Community Plugin Repository (one-click install) | Yes |
| Frontend | Status |
|---|---|
| Web UI — Svelte 5 + Tailwind v4 (see below) | Yes |
| iPeng (iOS) | Verified |
| Squeezer (Android) | Verified |
The recommended way to install Resonance is via PyPI:
pip install resonance-server
This installs the server with all required dependencies, bundled core plugins, and the pre-built Web UI. No Node.js, no build steps — just install and run.
Tip: If
python3is not found or too old, install Python 3.11+ via your package manager:
- Debian/Ubuntu:
sudo apt install python3 python3-venv python3-pip- Fedora:
sudo dnf install python3- macOS:
brew install python@3
All dependencies are installed automatically via pip install resonance-server:
| Package | Purpose |
|---|---|
mutagen |
Read audio file metadata (tags, duration, cover art) |
aiosqlite |
Async SQLite for the music library database |
fastapi |
Web framework for JSON-RPC, REST API, streaming |
uvicorn |
ASGI server that runs FastAPI |
httpx |
Fully featured HTTP client for Python 3 |
pip install resonance-server[blurhash] # BlurHash cover art placeholders (Pillow + blurhash-python)
Whether a format is streamed directly (passthrough) or transcoded depends on
both the audio format and the player/device type. The rules are defined
in resonance/config/devices.toml (device tiers) and
resonance/config/legacy.conf (transcoding pipelines).
Common cases that need no extra tools: MP3, FLAC, OGG, and WAV are passthrough for most players (Squeezelite, SB2+, Radio, Touch, Boom).
Cases that require external tools:
| Tool | When needed |
|---|---|
| faad | M4A, M4B, ALAC, AAC-in-MP4 — decodes audio from MP4 containers |
| lame | Used together with faad — encodes the decoded stream to MP3 |
| flac | FLAC → PCM conversion (devices requesting raw PCM), server-side crossfade |
| sox | Opus support, OGG → PCM fallback, server-side crossfade |
Example: MP4-container formats (M4A, M4B, ALAC) always need transcoding because no Squeezebox hardware or Squeezelite can reliably stream MP4 over HTTP. WMA works on SB2+ but needs transcoding on SLIMP3. Opus always needs
sox.
Binaries are included in third_party/bin/ — no extra installation needed.
# Standard tools (Debian/Ubuntu):
sudo apt install -y flac lame sox
# Using Homebrew:
brew install flac lame sox
For faad you need the LMS-patched version from
ralph-irving/faad2, which adds
seeking support (-j/-e flags) and ALAC decoding.
Option A — Extract from LMS package (easiest):
Download the LMS package from lms-community.github.io,
extract it, and copy the faad binary for your architecture:
# Example for x86_64:
cp /path/to/lms/Bin/x86_64-linux/faad third_party/bin/faad
chmod +x third_party/bin/faad
Available architectures: x86_64-linux, aarch64-linux, arm-linux,
armhf-linux, i386-linux, powerpc-linux, sparc-linux, darwin,
i386-freebsd-64int, i86pc-solaris-thread-multi-64int.
Option B — Build from source:
git clone https://github.com/ralph-irving/faad2.git
cd ~/faad2
autoreconf -i
./configure
make
sudo make install
sudo ldconfig
Verify:
ldconfig -p | grep libfaad
which faad
faad -h
Note: If you place the binaries in
third_party/bin/, Resonance finds them automatically. Otherwise, make sure they are on your systemPATH.
Resonance ships with a web interface built with Svelte 5, SvelteKit, and Tailwind CSS v4.
cd web-ui
npm install # one-time
npm run dev # dev server → http://localhost:5173
npm run build # production build → web-ui/build/
The dev server proxies API requests to the Python backend on port 9000. Make sure the backend is running first.
The frontend communicates with Resonance via:
| Protocol | Endpoint | Purpose |
|---|---|---|
| JSON-RPC | /jsonrpc.js |
LMS-compatible API (player control, library queries) |
| REST | /api/* |
Modern endpoints (folders, scan, artwork, plugins) |
| Cometd | /cometd |
Real-time updates (currently uses polling) |
| SSE | /api/plugins/{id}/events |
Server-Sent Events for plugin UI live updates |
web-ui/
├── src/
│ ├── app.css # Global styles + Tailwind v4 theme
│ ├── app.html # HTML shell
│ ├── lib/
│ │ ├── api.ts # API client (JSON-RPC + REST)
│ │ ├── components/
│ │ │ ├── AddFolderModal.svelte # Add music folder dialog
│ │ │ ├── AlarmSettings.svelte # Per-player alarm management
│ │ │ ├── BlurHashPlaceholder.svelte# Blurred artwork placeholder
│ │ │ ├── CoverArt.svelte # Album art with BlurHash + glow
│ │ │ ├── FavoritesView.svelte # Favorites browse + manage
│ │ │ ├── NowPlaying.svelte # Playback controls + progress
│ │ │ ├── PlayerSelector.svelte # Multi-player dropdown
│ │ │ ├── PlaylistsView.svelte # Saved playlists manage
│ │ │ ├── PluginsView.svelte # Plugin management UI
│ │ │ ├── PodcastView.svelte # Podcast browse + subscribe
│ │ │ ├── QualityBadge.svelte # Lossless / Hi-Res indicators
│ │ │ ├── Queue.svelte # Playlist sidebar
│ │ │ ├── RadioView.svelte # Internet radio browse + search
│ │ │ ├── ResizeHandle.svelte # Drag-to-resize panels
│ │ │ ├── SearchBar.svelte # Search with debounce + Ctrl+K
│ │ │ ├── SettingsPanel.svelte # Player settings
│ │ │ ├── Sidebar.svelte # Navigation sidebar
│ │ │ ├── ToastContainer.svelte # Toast notification renderer
│ │ │ └── TrackList.svelte # Track list with play/add actions
│ │ ├── plugin-ui/ # Server-Driven UI (SDUI) system
│ │ │ ├── registry.ts # Widget type → Svelte component map
│ │ │ ├── PluginRenderer.svelte # Recursive component renderer
│ │ │ ├── PluginPageView.svelte # Page container (SSE + polling)
│ │ │ ├── actions.svelte.ts # Generic action dispatcher
│ │ │ └── widgets/ # 20 widget components
│ │ │ ├── ActionButton.svelte, Alert.svelte, Card.svelte, ...
│ │ │ └── Toggle.svelte, Textarea.svelte, Modal.svelte, ...
│ │ └── stores/
│ │ ├── color.svelte.ts # Dynamic accent colors (Vibrant)
│ │ ├── player.svelte.ts # Player state + polling
│ │ ├── toast.svelte.ts # Toast notification store
│ │ └── ui.svelte.ts # Navigation + layout state
│ └── routes/
│ ├── +layout.svelte
│ └── +page.svelte
├── static/ # Fonts, favicon, brand assets
├── package.json
├── svelte.config.js
├── tsconfig.json
└── vite.config.ts
Once the server is running:
Add your music — Open http://localhost:9000 in your browser (or http://localhost:5173
if running the dev server). Click Add Folder, enter the path to your music directory,
and the library scan starts automatically.
Connect a player — Start Squeezelite
or power on your Squeezebox hardware. Players on the same subnet may discover the server
automatically via UDP broadcast. If discovery doesn't work, specify the server IP explicitly
(e.g. squeezelite -s <server-ip> or enter the IP in your hardware player's network settings).
Play music — Browse your library in the Web UI, select a track or album, and hit play. LMS-compatible apps like iPeng (iOS) or Squeezer (Android) should work as well, but this has not been fully verified yet.
resonance-server/
├── resonance/ # Main Python package
│ ├── __main__.py # Entry point (python -m resonance)
│ ├── server.py # Main server, starts all components
│ ├── content_provider.py # ContentProvider ABC + Registry
│ ├── plugin.py # PluginContext (DI for plugins)
│ ├── plugin_manager.py # Plugin discovery, loading, lifecycle
│ ├── config/ # Configuration
│ │ ├── devices.toml # Device tiers (Modern/Legacy)
│ │ └── legacy.conf # Transcoding rules (LMS-style)
│ ├── core/ # Business logic
│ │ ├── library.py # MusicLibrary facade
│ │ ├── library_db.py # SQLite database layer
│ │ ├── scanner.py # Audio file scanner (mutagen)
│ │ ├── playlist.py # Playlist management
│ │ ├── artwork.py # Cover art extraction + BlurHash
│ │ └── events.py # Event bus (pub/sub)
│ ├── player/ # Player management
│ │ ├── client.py # PlayerClient (status, commands)
│ │ ├── capabilities.py # Device capabilities + volume curves
│ │ └── registry.py # PlayerRegistry (all players)
│ ├── protocol/ # Network protocols
│ │ ├── slimproto.py # Slimproto server (Port 3483)
│ │ ├── cli.py # Telnet CLI (Port 9090)
│ │ ├── discovery.py # UDP discovery
│ │ └── commands.py # Binary command builder
│ ├── streaming/ # Audio streaming
│ │ ├── server.py # Streaming server + URL proxy
│ │ ├── transcoder.py # Transcoding pipeline
│ │ ├── crossfade.py # Server-side crossfade (SoX)
│ │ ├── seek_coordinator.py # Latest-wins seek coordination
│ │ └── policy.py # Format decision logic
│ └── web/ # HTTP layer
│ ├── server.py # FastAPI app (Port 9000)
│ ├── jsonrpc.py # JSON-RPC handler
│ ├── cometd.py # Bayeux long-polling
│ ├── handlers/ # Command handlers
│ │ ├── status.py # Player status
│ │ ├── playback.py # Play/Pause/Stop
│ │ ├── playlist.py # Queue commands
│ │ ├── seeking.py # Seek (non-blocking)
│ │ └── library.py # Library queries
│ └── routes/ # FastAPI routes
│ ├── api.py # REST API
│ ├── streaming.py # /stream.mp3 (local + remote proxy)
│ ├── artwork.py # Cover art endpoints
│ └── cometd.py # /cometd
├── plugins/ # Plugin directory (auto-discovered)
│ ├── example/ # Hello World template
│ ├── favorites/ # Favorites (LMS-compatible)
│ ├── nowplaying/ # Now Playing tutorial plugin
│ ├── radio/ # Internet Radio (radio-browser.info)
│ └── podcast/ # Podcast (RSS + PodcastIndex)
├── web-ui/ # Svelte 5 frontend
├── tests/ # pytest suite (2853 tests)
├── scripts/ # Dev & test scripts
├── third_party/ # External binaries
│ ├── bin/ # faad, flac, lame, sox (Windows)
│ └── squeezelite/ # Squeezelite binary
├── docs/ # Documentation
│ ├── ARCHITECTURE.md # System architecture
│ ├── CHANGELOG.md # Change log
│ ├── PLUGINS.md # Plugin system overview
│ ├── PLUGIN_API.md # Plugin API reference (incl. SDUI §19)
│ ├── PLUGIN_TUTORIAL.md # Plugin tutorial (step by step)
│ └── PLUGIN_CASESTUDY.md # Case study: raopbridge plugin deep dive
├── Dockerfile # Multi-stage Docker build
├── docker-compose.yml # Reference deployment config
├── .dockerignore # Docker build context exclusions
├── .env.example # Environment variable reference
├── pyproject.toml # Python project config
└── LICENSE # GPL-2.0
# Install dev dependencies (one-time)
pip install -e ".[dev]"
# Install ALL optional dependencies (Pillow, blurhash, dev tools)
pip install -e ".[all]"
# Run the full test suite
pytest
# Run with coverage report
pytest --cov
# Run a specific test module
pytest tests/test_radio_plugin.py -v
Some tests require optional dependencies (Pillow, external audio tools like
flac, lame, sox). These tests are tagged with pytest markers and will
be auto-skipped when the dependency is missing — no failures, just skips.
| Marker | Requires | Install |
|---|---|---|
@pytest.mark.requires_pil |
Pillow (PIL) | pip install -e ".[blurhash]" |
@pytest.mark.requires_tools |
flac, lame, sox on PATH |
See Transcoding Tools |
# Run only tests that need no optional dependencies
pytest -m "not requires_pil and not requires_tools"
# Run only PIL-dependent tests
pytest -m requires_pil
# Run everything (missing deps auto-skip with clear message)
pytest
Resonance is a hobby project and contributions are welcome! Here's how you can help:
GPL-2.0 — same as the original Lyrion Music Server.
See THIRD_PARTY_NOTICES.md for licenses of all dependencies and shipped binaries.
A huge thank-you to the Squeezebox community — you keep this wonderful platform alive.
If you have feedback, ideas, or run into bugs — please open an issue or start a discussion. Community input is what makes this project better.