Free, open-source, self-hosted web music player — a unified frontend for YouTube Music, Spotify, and SoundCloud.
Meeb is a self-hosted, ad-free, open-source music web app that lets you search, stream, and manage music from YouTube Music, Spotify, and SoundCloud simultaneously from a single beautiful interface. Log into all three accounts at once, unify your liked songs, playlists, and listening history, and enjoy a rich desktop-class audio experience — all from a tab in your browser.
It does not rely on any paid cloud services. Your server is the only backend.
- YouTube Music — full search, stream via
yt-dlp, no ads - Spotify — search, metadata, 30s previews (full playback requires Spotify Premium + Web Playback SDK)
- SoundCloud — full search and streaming via the SoundCloud API
- Log into all three accounts simultaneously; sync liked songs, playlists, and history from each
- Easily add more providers by extending
BaseProvider
- Web Audio API pipeline with pre-gain → 10-band EQ → dynamics compressor → master gain
- 10-band graphic equalizer with presets: Flat, Rock, Pop, Jazz, Classical, Electronic, Bass Boost, Vocal
- Pre-amplifier (±12 dB), live frequency-response curve canvas
- Crossfade between tracks (0–12 s, configurable)
- Gapless playback support
- Volume normalisation
- Playback speed control (0.5× – 2×) with pitch preservation
- Surround/Night Mode compressor modes
- Bars — animated frequency bars with glowing caps
- Waveform — real-time oscilloscope
- Circle — radial spectrum analyser
- Particles — energy-reactive particle system
- Mini visualizer always visible in the player bar
- Real-time BPM detection via FFT onset analysis (kick-drum band 80–200 Hz)
- Tap-tempo fallback
- Confidence display and musical tempo name (Andante, Allegro, Presto…)
- Beat-pulse animation synced to detected tempo
- Material You dynamic colour system — the entire UI tints to match the current album art's dominant colour, animated smoothly on every track change
- Themes: Dark (default), Light, AMOLED Black
- Accent palettes: Violet, Blue, Green, Rose, Amber, Cyan — or fully dynamic from album art
- Compact mode, adjustable font size
- Full CSS custom-property system — every colour, radius, spacing value is a variable
- All colours can be overridden with one line of CSS per user preference
- Persistent bottom player bar with progress seek, volume, speed
- Shuffle (Fisher-Yates weighted), Repeat (none / all / one)
- Queue management with drag-and-drop reorder
- Previous / Next with 3-second back-seek on double-press
- Like / unlike tracks synced to each provider's account
- Media Session API — lock screen and OS media controls
- Restores last playing track, position, queue, volume, shuffle/repeat state on reload
- Play history (last 200 tracks) per provider
- Liked-track cache for instant local lookup
- Synced (LRC) lyrics via LRCLIB — free, no key required
- Plain-text fallback search
- Auto-scroll with active line highlight
- Toggle auto-show on track change
- Service Worker caches static assets, stream URLs, and album art
- Configurable audio cache size (default 512 MB, up to 4 GB)
- Stale-while-revalidate for images, cache-first for audio
- Background cache eviction when limit is reached
- Sleep timer — stop playback after 15 / 30 / 45 / 60 / 90 min or custom
- Song info panel — full metadata (title, artist, album, genre, release date, duration, provider, explicit flag)
- Keyboard shortcuts — fully customisable, 20+ default bindings
- Browser notifications — track-change alerts (optional)
- Context menu on tracks — play, add to queue, add to playlist, like, view artist/album, share
- PWA — installable as a desktop or mobile app
| Layer | Technology |
|---|---|
| Server | PHP 8.1+ (no framework) |
| Frontend | Vanilla ES2022 (ES modules, no bundler required) |
| Styles | Pure CSS (custom properties, Grid, Flexbox) |
| Audio | Web Audio API |
| Fonts | Outfit, Inter, JetBrains Mono (Google Fonts) |
| YouTube | yt-dlp (server-side stream extraction) |
| Lyrics | LRCLIB (free, no key) |
| PWA | Service Worker + Web App Manifest |
| Requirement | Version |
|---|---|
| PHP | 8.1 or newer |
| PHP extensions | curl, json, mbstring |
| Web server | Apache (mod_rewrite) or Nginx |
| yt-dlp | Latest stable — pip install -U yt-dlp |
| Python | 3.8+ (for yt-dlp) |
git clone https://github.com/yourname/meeb.git
cd meeb# Linux/macOS
pip install -U yt-dlp
which yt-dlp # note the path
# Or download the binary directly:
curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/local/bin/yt-dlp
chmod +x /usr/local/bin/yt-dlpSpotify
- Go to developer.spotify.com/dashboard → Create App
- Add redirect URI:
https://yourdomain.com/api/auth/spotify.php?action=callback - Copy Client ID and Client Secret
YouTube Music / Google
- Go to console.cloud.google.com → New Project
- Enable YouTube Data API v3
- Create OAuth 2.0 credentials (Web Application)
- Add redirect URI:
https://yourdomain.com/api/auth/youtube.php?action=callback - Also create an API Key for public search
SoundCloud
- Go to soundcloud.com/you/apps → Register a new application
- Add redirect URI:
https://yourdomain.com/api/auth/soundcloud.php?action=callback - Copy Client ID and Client Secret
cp config/config.php config/config.local.php
# Edit config.local.php — fill in all API keys
nano config/config.local.phpOr use environment variables:
export SPOTIFY_CLIENT_ID="your_id"
export SPOTIFY_CLIENT_SECRET="your_secret"
export YOUTUBE_CLIENT_ID="your_id"
export YOUTUBE_CLIENT_SECRET="your_secret"
export YOUTUBE_API_KEY="your_key"
export SOUNDCLOUD_CLIENT_ID="your_id"
export SOUNDCLOUD_CLIENT_SECRET="your_secret"
export YTDLP_PATH="/usr/local/bin/yt-dlp"
export MEEB_BASE_URL="https://yourdomain.com"chmod 755 cache/ cache/audio/ cache/images/ cache/metadata/ logs/
chown -R www-data:www-data cache/ logs/ # or your web server userApache (.htaccess is already included — just enable mod_rewrite):
a2enmod rewrite
systemctl restart apache2Nginx:
server {
listen 443 ssl http2;
server_name yourdomain.com;
root /var/www/meeb;
index index.php;
# Static assets
location /public/ {
expires 7d;
add_header Cache-Control "public, immutable";
}
# PHP files
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
# SPA fallback
location / {
try_files $uri $uri/ /index.php?$query_string;
}
}The SW registers automatically from public/js/app.js. Make sure your domain uses HTTPS (required for Service Workers and the Web Audio API's full feature set).
| Key | Action |
|---|---|
Space |
Play / Pause |
N |
Next track |
B |
Previous track |
← / → |
Seek ±10 seconds |
↑ / ↓ |
Volume ±5% |
M |
Toggle mute |
S |
Toggle shuffle |
R |
Cycle repeat mode |
L |
Like current track |
E |
Toggle Equalizer |
Q |
Toggle Queue |
T |
Toggle Lyrics |
I |
Song Info |
/ |
Focus search |
, |
Open Settings |
1 / 2 / 3 |
Switch provider |
F11 |
Fullscreen |
Esc |
Close panel / modal |
? |
Show this list |
All shortcuts are customisable in Settings → Advanced.
- Create
public/js/providers/myprovider.jsextendingBaseProvider - Implement:
search(),getTrack(),getStreamUrl(),getAlbum(),getArtist(),getPlaylist() - Create
api/auth/myprovider.phpwith an API classMyproviderApi - Register in
public/js/app.js:import { MyProvider } from './providers/myprovider.js'; export const providerRegistry = { // ...existing providers myprovider: new MyProvider(), };
- Add a provider button in
index.php's.provider-switcher
meeb/
├── .htaccess Apache rewrite rules & security headers
├── index.php SPA entry point (full HTML shell)
├── composer.json
├── config/
│ └── config.php All API keys & runtime settings
├── api/
│ ├── index.php Request router (all /api/* traffic)
│ ├── auth/
│ │ ├── spotify.php Spotify OAuth + SpotifyApi class
│ │ ├── youtube.php Google OAuth + YoutubeApi class
│ │ └── soundcloud.php SC OAuth + SoundcloudApi class
│ ├── proxy/
│ │ ├── stream.php Audio stream URL resolver (yt-dlp / SC / SP)
│ │ └── image.php Album-art proxy (CORS fix + disk cache)
│ └── providers/
│ ├── spotify.php Spotify extra endpoints
│ ├── youtube.php YouTube extra endpoints
│ └── soundcloud.php SoundCloud extra endpoints (trending)
├── includes/
│ ├── session.php Session bootstrap + token management
│ ├── helpers.php HTTP client, caching, logging utilities
│ └── response.php json_ok() / json_err() helpers
├── public/
│ ├── manifest.json PWA manifest
│ ├── sw.js Service Worker (cache strategies)
│ ├── css/
│ │ ├── variables.css Complete Material You token system
│ │ └── main.css All component & layout styles
│ └── js/
│ ├── app.js Bootstrap — wires all modules together
│ ├── core/
│ │ ├── events.js Typed event bus
│ │ ├── state.js Reactive single-source-of-truth store
│ │ └── storage.js localStorage wrapper + typed helpers
│ ├── player/
│ │ ├── audio.js Web Audio API engine (crossfade, session restore)
│ │ ├── equalizer.js 10-band EQ + presets + curve canvas
│ │ ├── bpm.js Real-time BPM detection + tap tempo
│ │ ├── visualizer.js Bars / Waveform / Circle / Particles
│ │ └── queue.js Queue data helpers
│ ├── providers/
│ │ ├── base.js Abstract BaseProvider + Track typedef
│ │ ├── spotify.js Spotify Web API + Web Playback SDK
│ │ ├── youtube.js YouTube Music (via PHP proxy / yt-dlp)
│ │ └── soundcloud.js SoundCloud API
│ ├── ui/
│ │ ├── theme.js Material You colour extraction + bloom animation
│ │ ├── settings.js Settings panel renderer (all 30+ settings)
│ │ ├── search.js Debounced search UI + result templates
│ │ ├── keyboard.js Global keyboard shortcut manager
│ │ ├── modal.js Modal dialog system
│ │ └── toast.js Toast notification manager
│ └── utils/
│ ├── api.js Typed fetch wrapper for PHP API
│ ├── color.js Material You palette generation, OKLCH math
│ └── time.js Duration formatting, greeting, time parsing
└── cache/
├── audio/ yt-dlp stream URL cache
├── images/ Album art cache
└── metadata/ Track / search metadata cache
Spotify's API does not allow server-side audio streaming without special partnership agreements. Meeb handles this in two ways:
- 30-second previews — available for most tracks via the public API, no account needed
- Full playback — available via the Spotify Web Playback SDK for Spotify Premium users who connect their account. The SDK streams directly from Spotify's CDN to the browser; Meeb's server is not involved in the audio data.
If you need unrestricted Spotify playback, consider pairing Meeb with librespot (community project, check its license and ToS implications).
- No telemetry, no analytics, no third-party scripts (except Google Fonts and Spotify SDK, both optional)
- API credentials are stored server-side in PHP sessions — never exposed to the browser
- Album art is proxied through your own server to avoid leaking your IP to CDNs
- Play history and liked tracks are stored locally in your browser's localStorage
- Fork the repository
- Create a feature branch:
git checkout -b feat/my-feature - Keep commits atomic with clear messages
- Open a pull request against
main
Please follow the existing code style: no build step, no framework, readable vanilla JS and PHP.
- Last.fm scrobbling
- Discord Rich Presence (via browser extension bridge)
- Import/export playlists (M3U, JSPF)
- Deezer provider
- Smart queue (auto-queue related tracks)
- Mini player overlay mode
- Mobile app wrapper (Capacitor)
- Multi-user support with per-user settings
- Podcast support
GNU General Public License v3.0 — see LICENSE.
You are free to use, modify, and self-host Meeb. If you distribute a modified version, you must also release your modifications under GPL-3.0 and credit the original project.
Built with ♪ and no JavaScript frameworks.