A polished, self-hosted music player for Subsonic and OpenSubsonic libraries — in your browser or the (experimental) desktop app.
Stream from Navidrome, Gonic, Airsonic, and other compatible servers through a responsive interface built for desktop, mobile, and Windows.
Features · Desktop App · Screenshots · Quick Start · Docker · Contributing
|
|
| Now-playing sidebar and queue | Full-screen player |
Nebula is also distributed as a native desktop app built with Electron, on Windows and macOS. It includes everything in the web player plus:
- A custom frameless title bar with native window controls and a system tray
- Automatic updates delivered from GitHub Releases, with an in-app Restart & Install banner and a tray/menu-bar notification when a new version is ready
- Windows taskbar integration: playback progress, thumbnail transport buttons (previous, play/pause, next), and global media keys
- A native always-on-top mini-player window
- Secure credential storage through the OS credential vault (Windows DPAPI)
- The Nebula logo as the app, taskbar, and menu-bar icon
Download the latest installer from the
Nebula Releases page
(Nebula-2.4.6-setup.exe, NSIS installer) or sideload the unsigned
Nebula-2.4.6-setup.appx package with Windows Developer Mode enabled. Once
installed, Nebula checks GitHub Releases for updates and notifies you when a
new version is available.
The macOS edition is distributed as a .dmg installer and adds the native
features of the platform:
- Native macOS traffic lights with a hidden-inset title bar and a drag-to-move window chrome
- A native application menu (About, Settings
⌘,, Edit shortcuts, Playback controls) and a Dock menu - A menu-bar status item whose icon adapts to dark/light appearance, with quick controls (Show Nebula, Play/Pause, Next, Previous, Mini Player, Quit)
- Now Playing in Control Center and on the lock screen, plus keyboard media keys via the system Media Session
- Notification Center banners when an update is downloaded
- A floating panel mini-player that stays above other apps and is hidden from Cmd-Tab
macOS builds ship unsigned. On first launch, right-click the app and choose Open (or allow it in System Settings → Privacy & Security) to bypass Gatekeeper. Automatic updates require code signing; the update check, download, and in-app banner work unsigned, but "Restart & Install" may not complete until the app is signed with a Developer ID.
- Shared app-wide audio engine with queue, repeat, seek, volume, and Media Session controls
- Per-track playback speed and pitch controls with pitch-correction support
- Resilient Subsonic streaming with bounded URL caching and stalled-stream recovery
- Automatic transcoding rules for browser-sensitive formats such as ALAC and M4A
- Internet radio playback for direct streams and HLS playlists
- Web Audio visualizers including Bars, Wave, Circle, Mirror, and Spectrum
- Expandable full-screen player, desktop sidebar player, floating mini-player, mobile player bar, and a native desktop mini-player window
- Structured and synchronized lyrics with fallback lyric providers
- AutoEq headphone calibration profile search and application
- Configurable keyboard shortcuts and an immersive Zen mode
- Browse artists, albums, songs, playlists, favorites, and genres
- Spotlight-style search across artists, albums, and tracks
- Featured albums, random mixes, recent releases, and most-played statistics
- Persistent sorting and filtering by genre, year, and library metadata
- Demo mode for exploring the interface without connecting a server
- Subsonic API 1.16.1 with fallback negotiation through API 1.14.0
- OpenSubsonic extension discovery and structured lyrics v2 support
- ID3-first album and starred endpoints with legacy server fallbacks
- Password token/salt authentication and optional OpenSubsonic API-key authentication
- Responsive light and dark themes with system-preference detection
- IndexedDB caching for API responses, settings, credentials, and local play statistics
- Docker, Vercel, and static-hosting deployment options for the web player
- Native Windows desktop app (Electron) with automatic updates, tray, taskbar controls, media keys, and OS credential vault; macOS edition with traffic lights, app/Dock menus, a menu-bar status item, Now Playing and media keys, and a panel mini-player
- Optional authenticated localhost bridge for the Nebula Music Stream Deck plugin
Nebula can expose the currently open browser player to the Nebula Music plugin for Stream Deck and Stream Deck+. Install the plugin from its latest release. In Settings → Stream Deck, enable the bridge, use the same local port as the plugin, and enter the six-digit code shown by Stream Deck.
The bridge connects only to IPv4 loopback, is disabled by default, and stores its pairing token in this browser's IndexedDB. It sends playback metadata, playlist summaries, and compressed cover-art pixels; it never sends Subsonic credentials, the complete queue, or authenticated artwork URLs. Use Revoke pairing to remove the pairing from Stream Deck and delete the saved browser token after confirmation. The bridge also exposes Nebula's playback-rate, pitch, and pitch-correction controls to compatible Stream Deck+ plugin versions.
Nebula Music supports servers implementing the Subsonic API or compatible OpenSubsonic extensions, including:
The music server must be reachable from the browser running Nebula. HTTPS and correct CORS configuration are strongly recommended.
Download and run the latest installer from the
Nebula Releases page.
Windows ships an NSIS installer (Nebula-2.4.6-setup.exe); macOS ships a
.dmg installer (Nebula-2.4.6-arm64.dmg). No setup beyond the installer is
required — Nebula updates itself from GitHub Releases. To run the desktop app
from source during development:
npm install
npm run start:electron # builds the renderer + main process and launches Electron- Node.js 20.19+ or 22.12+; Node.js 24 LTS is recommended
- npm
- A reachable Subsonic-compatible server, unless using demo mode
git clone https://github.com/lilremark/Nebula-Music.git
cd Nebula-Music
npm install
npm run devOpen http://localhost:3000. No environment variables are required.
Choose one of the supported authentication methods:
- Password: Enter the server URL, username, and password. Nebula stores the generated token and salt instead of the raw password.
- API key: Enter the server URL and OpenSubsonic API key. Nebula omits the username and legacy token parameters as required by the extension.
npm run typecheck
npm run build
npm run previewThe production bundle is written to dist/.
The published image is available from Docker Hub as
lilremark/nebula-music.
It serves Nebula through an unprivileged NGINX 1.30.3 container. The Compose
setup uses a read-only filesystem, drops Linux capabilities, and includes a
health check.
Pull and run the latest release:
docker pull lilremark/nebula-music:latest
docker run -d \
--name nebula-music \
--restart unless-stopped \
--read-only \
--tmpfs /tmp:rw,noexec,nosuid,size=16m \
--security-opt no-new-privileges \
--cap-drop ALL \
-p 8080:8080 \
lilremark/nebula-music:latestOpen http://localhost:8080.
To stop the container:
docker stop nebula-music
docker rm nebula-musicUsing the included Compose configuration:
docker compose -f docker/docker-compose.yml up -d
docker compose -f docker/docker-compose.yml downCompose uses latest by default. Set NEBULA_VERSION=2.3.0 to pin the current
release. See docker/README.md for local-build commands and
additional deployment details.
Nebula is a client-side application. Server details and preferences are entered in the UI and stored in the browser.
| Storage | Contents |
|---|---|
| IndexedDB | Settings, cached API responses, authentication data, and per-server play statistics |
localStorage |
Lightweight play-history snapshots and the last-seen application version |
To reset the application completely, clear the site data for the Nebula origin in your browser.
| Action | Default |
|---|---|
| Play or pause | Space |
| Previous track | ArrowLeft |
| Next track | ArrowRight |
| Toggle repeat | L |
| Cycle visualizer | V |
| Toggle Zen mode | Z |
Shortcuts can be changed in Settings.
Nebula-Music/
├── components/ Reusable UI, navigation, radio, and player components
├── constants/ Equalizer presets and shared constants
├── context/ Global store and theme state
├── docker/ Docker, Compose, and Nginx configuration
├── hooks/ Adaptive color, artist image, and waveform hooks
├── public/ Static browser assets and audio worklets
├── services/ Subsonic API, AutoEq, and IndexedDB services
├── screenshots/ README product screenshots
├── views/ Home, browse, library, radio, search, and settings views
├── App.tsx Application shell and view routing
├── index.tsx React bootstrap
└── index.css Global styles and theme variables
flowchart LR
UI["React views and components"] --> Store["Context store"]
Store --> API["Subsonic service"]
Store --> Audio["HTMLAudioElement and Web Audio API"]
API --> Server["Subsonic/OpenSubsonic server"]
API <--> Cache["IndexedDB cache"]
Audio --> Session["Media Session API"]
Audio --> Visualizer["AnalyserNode visualizers"]
The global store coordinates library requests, cached responses, playback
state, and UI updates. A shared HTMLAudioElement handles playback while a
lazily created Web Audio graph powers analysis, visualizers, pitch processing,
and equalization.
| Command | Description |
|---|---|
npm run dev |
Start the Vite development server on port 3000 |
npm run typecheck |
Run TypeScript validation without emitting files |
npm run build |
Create the production bundle |
npm run preview |
Preview the production build locally |
- Import this repository into Vercel.
- Select the Vite framework preset.
- Use
npm run buildas the build command. - Use
distas the output directory.
Build the project with npm run build, then deploy the generated dist/
directory to Netlify, Cloudflare Pages, Amazon S3, or another static host.
Because Nebula connects directly from the browser, the deployed origin must be permitted by the music server's CORS policy.
Nebula cannot connect to my server
- Verify that the URL includes
https://orhttp://. - Confirm the server is reachable from the same browser and network.
- Check the server's CORS configuration.
- Avoid mixed content: an HTTPS deployment cannot call an HTTP music server.
- Confirm the selected password or API-key authentication mode is supported by the server.
Audio plays but seeking does not work
- Confirm the server supports byte-range requests and returns appropriate
Content-LengthandAccept-Rangesheaders. - Enable server-side transcoding for formats the browser cannot seek reliably.
Settings or themes do not persist
- Ensure the browser is not blocking IndexedDB or local storage.
- Clear the site's stored data after changing between demo and live-server credentials.
- Added a dedicated Windows title bar with the Nebula wordmark and window controls above the app, matching the macOS layout.
- Moved the window controls out of the top bar into the new title strip in the main window and the full-screen player, and tightened their hover highlights so they fit within the strip.
- The navigation drawer now clears the new Windows title bar instead of overlapping it.
- Fixed Stream Deck pairing on the Windows desktop app: the bridge handshake now again sends a valid loopback origin the plugin accepts, so the plugin connects and pairs reliably.
- Fixed playback hanging after the app was minimized or hidden for a long time (or after the PC woke from sleep) by keeping the playback pipeline alive while the window is in the background.
- Reduced idle CPU and smoothed animations: the now-playing panel, mini-player, cover flow, and visualizer no longer re-render every frame while paused or hidden.
- Stopped constant Windows taskbar churn — thumbnail buttons and the taskbar progress bar now update only when they actually change.
- Re-syncs tray, media-key, and mini-player state after waking from sleep.
- Fixed the auto-update check failing with
net::ERR_HTTP2_SERVER_REFUSED_STREAMby forcing update requests over HTTP/1.1, which GitHub serves reliably.
- Reworked the always-on-top mini-player: it now shows the current album art, progress, and transport controls, plus an "Up Next" list of the next five queued tracks with their cover art.
- Clicking an "Up Next" row jumps playback to that track in the queue.
- Fixed a Windows taskbar issue where a second, overlapping Nebula icon appeared by registering the app's AppUserModelID so all windows share one taskbar entry.
- Fixed desktop playback stalling after a few tracks — media now loads directly from your server instead of the proxy, so streams and cover art no longer exhaust their connection pool.
- Opening a related album under "More by" now returns to the top of the album page, showing the album art, info, and tracklist (web, Windows, and macOS).
- Waveform previews fall back gracefully on servers that do not send CORS headers, matching the web build.
- Added a native macOS arm64 edition with a dedicated title strip, traffic lights, app menu, Dock menu, menu-bar controls, media keys, and Notification Center updates.
- Added live Now Playing metadata and sanitized cover art to the macOS Playback menu.
- Added reproducible Windows and macOS release artifacts with platform-specific GitHub update feeds.
- Kept automatic Windows updates and added safe update checks with manual GitHub downloads for unsigned macOS builds.
- Fixed automatic updates — the updater now reads the published
latest.yml(stable channel) and uses prerelease detection for beta. - Replaced the tray indicator with the Nebula logo.
- Made the Home Most Played / For You section a collapsible dropdown so it no longer scrolls when stacked under Quick Picks.
- Nebula is now a native Windows desktop app with a custom frameless title bar, window controls, and a system tray.
- Added automatic updates from GitHub Releases with an in-app Restart & Install banner and a tray notification.
- Added Windows taskbar integration: playback progress, thumbnail transport buttons (previous, play/pause, next), and global media keys.
- Added a native always-on-top mini-player window and secure credential storage via the OS credential vault (Windows DPAPI).
- Reworked the sign-in screen into a split view with a looping cover-flow animation.
- Redesigned the Settings updates panel as a centered hero with phase-aware controls.
- Made full-screen player tabs and the sidebar close button clickable; pinned Zen-mode controls and refined the title marquee.
- Improved visualizer accuracy (pre-DSP sampling), aligned the waveform ticker, and smoothed mini-player progress.
- Home layout stacks Quick Picks and Most Played/For You responsively.
- Added a signed NSIS installer plus an unsigned appx package and set the Nebula logo as the app/taskbar icon.
- Added an opt-in, authenticated localhost bridge for the Nebula Music Stream Deck plugin.
- Added Stream Deck and Stream Deck+ control for now-playing artwork and metadata, playback, seeking and scrubbing, volume and mute, previous and next tracks, fixed playlists, and live playlist browsing.
- Added Stream Deck+ playback-speed, pitch, and pitch-correction controls.
- Added single-use pairing codes, token authentication, loopback-only connections, runtime protocol validation, and sanitized artwork transfer without Subsonic credentials or authenticated URLs.
- Reduced bridge update traffic and removed background command delays for responsive hardware control while Stream Deck is minimized.
- Improved full album and playlist queue replacement before playback.
- Refined Docker security, health checks, Compose deployment, and published
multi-platform
2.2.0andlatestimages. - Updated the Tailwind CSS and PostCSS build chain to patched releases with a clean npm audit.
- Updated all npm dependencies, including React 19, Vite 8, Tailwind CSS 4, TypeScript 6, Motion 12, and Lucide React 1.
- Added OpenSubsonic extension discovery and API-key authentication.
- Added structured lyrics v2 and ID3-first album/starred endpoints with legacy fallbacks.
- Added Subsonic protocol fallback negotiation from API 1.16.1 through 1.14.0.
- Centralized Subsonic response and error handling.
- Updated Docker builds to Node.js 24 LTS and unprivileged NGINX 1.30, with a read-only runtime, dropped capabilities, and container health checks.
- Published multi-platform
2.1.3andlatestimages to Docker Hub atlilremark/nebula-music. - Added refreshed product screenshots and updated project documentation.
- Added AutoEq headphone calibration.
- Added production Docker and Nginx deployment files.
- Improved long-session Subsonic playback recovery.
- Refined visualizer controls and Settings layout.
See the commit history for the complete development history.
Issues and pull requests are welcome. Read CONTRIBUTING.md for development requirements, validation commands, and pull-request guidance.
Security vulnerabilities must not be reported publicly. Email remark@remark.rip and follow SECURITY.md.
Distributed under the MIT License.


