Skip to main content

Resonance

Resonance

A modern, LMS-compatible music server written in Python

License: GPL-2.0 Python 3.11+ Svelte 5 Tailwind v4 Status: Alpha

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!


Table of Contents


Quick Start

Linux / macOS:

git clone https://github.com/endegelaende/resonance-server.git
cd resonance-server
python3 -m venv .venv
.venv/bin/python -m pip install mutagen aiosqlite fastapi uvicorn httpx
.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 mutagen aiosqlite fastapi uvicorn httpx
.venv\Scripts\python.exe -m resonance

The server starts on ports 3483 (Slimproto), 9000 (HTTP/API), and 9090 (CLI). 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.

Command-line options
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

Docker

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: host so 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_uuid into 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.

Environment variables
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.


Architecture

┌─────────────┐      ┌─────────────┐      ┌─────────────┐
│  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

Features

Protocols & Compatibility

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

Audio

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

Library & Playback

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

Frontends

Frontend Status
Web UI — Svelte 5 + Tailwind v4 (see below) Yes
iPeng (iOS) Verified
Squeezer (Android) Verified

Installation Details

The Quick Start above covers cloning and installing. Below are additional details for reference.

Alternative — Download ZIP instead of git clone: Go to https://github.com/endegelaende/resonance-server → green Code button → Download ZIP.

Tip: If python3 is 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

Python Dependencies

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

Optional Dependencies

pip install blurhash-python pillow   # BlurHash cover art placeholders

Transcoding Tools (optional)

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.

Windows

Binaries are included in third_party/bin/ — no extra installation needed.

Linux

# Standard tools (Debian/Ubuntu):
sudo apt install -y flac lame sox

macOS

# 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.

How to get the LMS-patched faad binary

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 system PATH.


Web UI

Resonance ships with a web interface built with Svelte 5, SvelteKit, and Tailwind CSS v4.

Running the Web UI

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.

API Integration

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

Project Structure

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

First Steps

Once the server is running:

  1. 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.

  2. 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).

  3. 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.


Project Structure

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

Running the Tests

# 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

Optional Dependency Markers

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

Contributing

Resonance is a hobby project and contributions are welcome! Here's how you can help:

  • Bug reports — Open an issue with steps to reproduce.
  • Hardware testing — If you own Squeezebox hardware other than Radio, your test results are especially valuable.
  • Pull requests — Fork the repo, create a branch, make your changes, and open a PR.

License

GPL-2.0 — same as the original Lyrion Music Server.

See THIRD_PARTY_NOTICES.md for licenses of all dependencies and shipped binaries.


Acknowledgments

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.

Metadata

Release files for resonance-server 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for resonance-server 0.1.0
File Size Uploaded
resonance_server-0.1.0.tar.gz 998.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for resonance-server 0.1.0
File Interpreter ABI Platform
resonance_server-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.7 MB

Release files / resonance_server-0.1.0.tar.gz

Download URL resonance_server-0.1.0.tar.gz
Size 998.6 kB
Tags Source
SHA-256 checksum
How to use checksums
0fe015ee1230626711f2247088902a14b1088d1fc9871854c02ec384a73c39f6
BLAKE2b-256 checksum
How to use checksums
ce2147ba6b213fbb6d37078ea44f4bc7f981bd2904b6923a25d597a89770eaca
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 6, 2026.

Transparency log

Release files / resonance_server-0.1.0-py3-none-any.whl

Download URL resonance_server-0.1.0-py3-none-any.whl
Size 687.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9b20639a5e5c9431c0df531679cf03dd96468f040ab75898aefcb83096100f0f
BLAKE2b-256 checksum
How to use checksums
05e2067f4c01cdb1448bfe9a403bed8c6a1327fc77f1ae028fa392a60b931a38
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 6, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page