▶ Try the live demo — a modern, dependency-free HTML5 radio player for any stream (Icecast, Shoutcast, Zeno.FM, RadioJar, Azuracast and more): the album art of the current song becomes a blurred full-page backdrop, with glass-style circular controls on top. Now playing metadata, album art, song lyrics, recently played history, YouTube clip mode and installable PWA — no Bootstrap, no jQuery, no API key. Just HTML, CSS and vanilla JavaScript.
🇧🇷 Player de rádio online para o seu site — grátis e sem chave de API. Mostra a música tocando agora com capa do álbum, letra da música, histórico das últimas tocadas e até o clipe do YouTube sincronizado com a rádio. Compatível com Icecast, Shoutcast, Zeno.FM e Azuracast. É só editar duas linhas e hospedar em qualquer servidor (ou de graça na Vercel/Netlify/GitHub Pages) — ou subir com Docker em um comando, configurando tudo por variável de ambiente.
Now playing
- Live song metadata via the twj.es API, with automatic fallback to an alternative endpoint in case of error
- Smart metadata parsing — ICY streams that send
"Artist - Title"as one string are split correctly, so the artist name is never duplicated on screen - Album art delivered straight by the API (
albumArt), with search.php + iTunes as fallbacks — no wrong covers from fuzzy matches - Blurred full-page backdrop generated from the current cover (1500x1500)
- Lyrics via lyrics.ovh with LRCLIB fallback — no API key required, with request caching
Recently played
- The 4 most recent songs, with covers from the search API (iTunes as fallback, filtered to music only)
- The song currently playing is filtered out of the list (no duplicates)
- 🎬 Clickable history: songs with a known music video show a play badge — click the card to watch the clip of a song that already played (one-off playback: the radio pauses while it plays and resumes when it ends or you pause it; it does not turn clip mode on)
- With clip mode ON, a history clip plays to the end and then the player returns to the video programming (the current song's clip) by itself
🎬 Clip mode (music video of the current song)
- When the metadata API returns a
youtubeIdin the now-playing payload, a clip button automatically appears (feature-detected) - Turned on, the music video takes the place of the album art, synchronized with the radio position (
start = elapsed) - Every song change swaps the embed; songs without a clip fall back to the radio automatically
- Pausing the video resumes the radio, playing it again pauses the radio — no external library (YouTube IFrame postMessage)
Player
- Smooth volume fade in/out on play/pause (no audio "pop")
- Loading spinner while the stream buffers
- Automatic reconnection with backoff when the network drops
- Volume as a circular button with a slider popover (desktop; on mobile the hardware buttons rule)
- Media Session integration (lock screen / notification controls with artwork)
- Keyboard shortcuts (see below)
App
- Responsive design — mobile-first single column, side-by-side layout on desktop
- Progressive Web App (PWA) with an "Install app" button when the browser allows it
- Accent color and surfaces themeable via CSS variables
- Single-file configuration in config.js — no need to touch the player source
- 🐳 Docker image ready for self-hosting, fully configurable by environment variables (see below)
Open config.js and edit the lines below:
window.RADIO_CONFIG = {
// RADIO NAME
RADIO_NAME: 'Your Radio Name',
// Change Stream URL Here. Supports ICECAST, ZENO, SHOUTCAST, RADIOJAR and any other stream service.
URL_STREAMING: 'https://stream.zeno.fm/yn65fsaurfhvv',
};The metadata API URLs are derived from URL_STREAMING automatically. If config.js is missing, the player falls back to the defaults in js/script.js.
Open the img folder and add your logo named cover.png (it is used as the fallback cover, favicon and PWA icon).
All the design tokens live at the top of css/style.css as CSS variables — change the accent color, surfaces and radius in one place:
:root {
--accent: #00e1e7; /* accent color (slider, live dot glow, focus rings) */
--bg: #0b0e13; /* page background */
--surface: rgba(255, 255, 255, 0.06); /* glass surfaces */
--border: rgba(255, 255, 255, 0.12); /* glass borders */
--radius: 20px; /* card corner radius */
}Just put the files in your server, run the Docker image, or use Free Hosting:
Self-hosting? Run it in one line — no need to clone the repo or edit any file:
docker run -d -p 8080:80 \
-e RADIO_NAME="My Radio" \
-e URL_STREAMING="https://stream.zeno.fm/yn65fsaurfhvv" \
--name radioplayer ghcr.io/jailsonsb2/radioplayer:latestThe player is at http://localhost:8080. Or with Docker Compose — see docker-compose.yml:
docker compose up -dEnvironment variables
| Variable | Default | Description |
|---|---|---|
RADIO_NAME |
Jailson Web Rádio |
Radio name (header, page title, PWA) |
URL_STREAMING |
Zeno.FM demo stream | Stream URL (Icecast, Shoutcast, Zeno.FM, Azuracast...) |
API_URL |
derived from the stream | Only if you run your own metadata API |
FALLBACK_API_URL |
derived from the stream | Fallback metadata endpoint |
ACCENT_COLOR |
#00e1e7 |
Accent color (--accent) |
BG_COLOR |
#0b0e13 |
Page background (--bg) |
THEME_COLOR |
#0b0e13 |
PWA theme color |
PWA_NAME |
same as RADIO_NAME |
App name on install |
PWA_SHORT_NAME |
same as PWA_NAME |
Short name (home screen icon) |
The container generates config.js and manifest.json from these on startup, so changing a variable and restarting is enough — no rebuild required.
Your own logo — mount it over the default cover:
-v ./my-logo.png:/usr/share/nginx/html/img/cover.png:roBuilding locally instead of pulling the image:
docker build -t radioplayer .Notes
- Built for
linux/amd64andlinux/arm64— Raspberry Pi and ARM NAS included. Image size ≈ 76 MB (nginx alpine + ~1.7 MB of player files). - Ships a
HEALTHCHECK, so Portainer / Unraid /docker psshow the real container health. - Everything is served over plain HTTP on port 80 inside the container. Put it behind a reverse proxy with HTTPS if you expose it to the internet — browsers block the PWA install prompt and the Media Session API on insecure origins.
- The full album art, lyrics and now playing metadata still come from the public APIs listed in Data Sources — the container serves the player, not the metadata.
When the browser signals that installation is available, an "Install app" button appears (top-right on desktop, bottom of the screen on mobile).
Note: the service worker fetches the HTML and config.js from the network first, so a configuration change reaches returning visitors on the next load. After a bigger update (CSS/JS), still bump the cache version in service-worker.js (CACHE_NAME) to force everything to refresh at once.
Edit the manifest.json file:
- Locate the
"name"field and replace it with the name of your radio. - If desired, customize
"background_color"and"theme_color"to match your branding.
{
"name": "Your Radio Name",
"short_name": "Radio Player",
"start_url": "/index.html",
"display": "standalone",
"background_color": "#0b0e13",
"theme_color": "#0b0e13",
"icons": [
{
"src": "img/cover.png",
"sizes": "192x192",
"type": "image/png"
}
]
}- Icecast / Shoutcast
- Zeno Radio
- RadioJar
- Azuracast
- Centova Cast
- Everest Cast
- MediaCP
- Sonic Panel
- twj.es — now playing metadata, song history and album art
- Apple Music / iTunes — cover art fallback
- lyrics.ovh + LRCLIB — lyrics
M- mute/unmutePandspace- play/pausearrow upandarrow down- increase/decrease volume0 to 9- volume percent
More free radio players from the same author — pick the style that fits your station:
| Project | Style |
|---|---|
| Radioplayer_api | Multi-station player with 3 switchable layouts (Retrô Glass, Clássico, Aurora Deck) |
| bottom_radioplayer | Bottom-bar component for websites — the audio never stops while visitors navigate |
| Radioplayer_html5 | Multi-station HTML5 player with API now playing |
| RadioPlayer-ZenoRadio | Full-page player for Zeno.FM streams (SSE metadata) |
| metadados | The free now playing API used by these players (ICY metadata + iTunes + YouTube clips) |
If you have any feedback, please reach out to me at contato@jailson.es
This project is licensed under the GNU AGPL-3.0 (see LICENSE): you are free to use, modify and redistribute it — including commercially — provided derivative works remain open source and keep the original copyright notices, even when offered only as a hosted/network service.
Closed-source / commercial licensing: to embed this code in a proprietary product without AGPL obligations, a separate commercial license is available — contact contato@jailson.es.
Copyright (C) 2024-2026 Jailson Bezerra (@jailsonsb2)
