Guides
How to build an HTML audio player for your station
Updated
This guide builds a one-button player for your own website: it plays your station, shows what's on air, and greys out when you're off air. It's plain HTML and JavaScript, with no build step and no libraries, so it drops into any site that lets you add a script.
You'll need your station's slug, the last part of your station's address. For evenings.fm/sample-radio, the slug is sample-radio.
Step 01: Create the page
Create a file called index.html and paste in:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Station player</title>
<style>
#player { cursor: pointer; }
#player:disabled { opacity: 0.5; cursor: default; }
</style>
</head>
<body>
<button id="player" disabled>Loading…</button>
<audio id="audio" hidden></audio>
<script>
// JavaScript goes here
</script>
</body>
</html>#playeris the button listeners press. It shows the name of what's on air, and it's disabled while you're off air.#audioplays the stream. It stays hidden; the button controls it.
Step 02: Add the JavaScript
Replace the comment in the <script> with the code below, and change garage to your own slug. We've left garage in so the example plays a real station (Garage Radio) the moment you open it.
const stationSlug = "garage"; // replace with your station's slug
const apiEndpoint = `https://api.evenings.co/v1/streams/${stationSlug}/public`;
const POLL_INTERVAL = 10000; // 10 seconds, matching the API's cache
const button = document.getElementById("player");
const audio = document.getElementById("audio");
let station = { online: false, name: "", streamUrl: "" };
let isPlaying = false;
// Ask the API whether the station is on air and what's playing.
async function fetchStationData() {
try {
const res = await fetch(apiEndpoint, { cache: "no-store" });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const json = await res.json();
station = {
online: json.online,
name: json.name || json.host || stationSlug,
streamUrl: json.streamUrl,
};
} catch {
station = { ...station, online: false };
}
render();
}
function render() {
if (isPlaying) {
button.textContent = `Pause ${station.name}`;
button.disabled = false;
} else if (station.online) {
button.textContent = `Play ${station.name}`;
button.disabled = false;
} else {
button.textContent = "Offline";
button.disabled = true;
}
}
// Connect to the stream fresh each time, so playback starts at the live moment.
function play() {
audio.src = station.streamUrl;
audio.play()
.then(() => {
isPlaying = true;
render();
})
.catch(stop);
}
// Pausing a live stream would leave the listener behind when they resume,
// so stop disconnects completely.
function stop() {
audio.pause();
audio.removeAttribute("src");
audio.load();
isPlaying = false;
render();
}
button.addEventListener("click", () => {
if (isPlaying) stop();
else if (station.online) play();
});
// If you sign off (or the connection drops) mid-listen, reset the button.
audio.addEventListener("ended", stop);
audio.addEventListener("error", () => {
if (isPlaying) stop();
});
fetchStationData();
setInterval(fetchStationData, POLL_INTERVAL);What each part does:
fetchStationData()calls the public Streams API, which needs no API key.nameis whatever is on air right now: your scheduled show, the current Always On track, or the details you set with Edit Details when you go live without a show. The player falls back tohost, then to the slug, ifnameis empty.- Polling every 10 seconds. The API caches each response for up to 10 seconds, so checking more often only returns the same answer.
cache: "no-store"stops the browser from reusing its own copy, and we clear our cache when you go live or sign off, so the button changes within about 10 seconds of either. play()andstop()connect and disconnect the stream. Polling never touches the<audio>element, so a status check can't interrupt someone who's listening.- The
endedanderrorlisteners put the button back to Offline or Play if the stream stops while someone is listening, instead of leaving it stuck on Pause.
Step 03: Try it
- Open
index.htmlin any browser. You don't need a web server for this one. - While the station is on air, the button reads Play followed by the name of what's on air. Press it to listen.
- Press it again to stop. When the station goes off air, the button switches to Offline within about 10 seconds.
To put the player on your site, copy the <button>, the <audio> element, the <style> rules and the <script> into your page. Each open page makes six API requests a minute, well inside the API's limit of 100 requests a minute per IP address.
Next steps
If you'd rather design your player visually, the Framer guide builds the same player as a Framer component. If listeners on your site hear you a second or two late, reducing player latency shows how to bring them closer to live. And if the player won't connect to your station, email contact@evenings.email with your slug and we'll look into it with you.