https://cdn.tailwindcss.com
tailwind.config = {
corePlugins: {
preflight: false,
}
}
#faith-radio-wrapper {
font-family: ‘Inter’, sans-serif;
width: 100%;
display: flex;
justify-content: center;
padding: 20px 0;
box-sizing: border-box;
}
.glass-panel {
background: linear-gradient(135deg, #450a0a 0%, #b91c1c 100%);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 10px 40px -10px rgba(0, 0, 0, 0.5);
}
.bar {
width: 6px;
background: #fbbf24;
border-radius: 999px;
animation: bounce 1.2s infinite ease-in-out;
opacity: 0.8;
}
.bar.paused {
animation-play-state: paused;
height: 4px !important;
opacity: 0.3;
transition: all 0.3s ease;
}
@keyframes bounce {
0%, 100% { height: 10px; }
50% { height: 32px; }
}
.fr-range {
-webkit-appearance: none;
width: 100%;
height: 20px;
background: transparent;
cursor: pointer;
margin: 0;
}
.fr-range:focus {
outline: none;
}
.fr-range::-webkit-slider-thumb {
-webkit-appearance: none;
height: 16px;
width: 16px;
border-radius: 50%;
background: #fbbf24;
cursor: pointer;
margin-top: -6px;
box-shadow: 0 2px 6px rgba(0,0,0,0.3);
}
.fr-range::-webkit-slider-runnable-track {
width: 100%;
height: 4px;
cursor: pointer;
background: rgba(255,255,255,0.2);
border-radius: 2px;
}
.station-list::-webkit-scrollbar {
width: 6px;
}
.station-list::-webkit-scrollbar-track {
background: rgba(0,0,0,0.1);
}
.station-list::-webkit-scrollbar-thumb {
background: rgba(255,255,255,0.2);
border-radius: 10px;
}
.station-item.active {
background: rgba(255, 255, 255, 0.15);
border-left: 4px solid #fbbf24;
}
Now Playing
Select a Station
Faith & Music
Your Stations
document.addEventListener(“DOMContentLoaded”, function() {
// EXACT “SECURE 16” LIST – Rejoice at Top, EWTN Removed
const stations = [
{
name: “Rejoice Radio”,
genre: “Christian Talk (SG)”,
src: “
https://rejoiceradio.streamguys1.com/live”
},
{
name: “Ancient Faith Talk”,
genre: “Orthodox Talk”,
src: “
https://ancientfaith.streamguys1.com/talk”
},
{
name: “Ancient Faith Music”,
genre: “Sacred Music”,
src: “
https://ancientfaith.streamguys1.com/music”
},
{
name: “Abiding Radio”,
genre: “Instrumental Hymns”,
src: “
https://streams.abidingradio.com:7800/1”
},
{
name: “Abiding Radio Sacred”,
genre: “Vocal Hymns”,
src: “
https://streams.abidingradio.com:7800/2”
},
{
name: “Abiding Radio Kids”,
genre: “Children’s Songs”,
src: “
https://streams.abidingradio.com:7800/3”
},
{
name: “ChristianRock.Net”,
genre: “Christian Rock”,
src: “
https://listen.christianrock.net/stream/11/”
},
{
name: “Christian Classic”,
genre: “Classic Rock”,
src: “
https://listen.christianrock.net/stream/14/”
},
{
name: “Christian Power Praise”,
genre: “Modern Worship”,
src: “
https://listen.christianrock.net/stream/13/”
},
{
name: “Christian Hits”,
genre: “Pop & Contemporary”,
src: “
https://listen.christianrock.net/stream/10/”
},
{
name: “Christian Hard Rock”,
genre: “Hard Rock and Metal”,
src: “
https://listen.christianrock.net/stream/15/”
},
{
name: “Christian Country”,
genre: “Country Gospel”,
src: “
https://listen.christianrock.net/stream/16/”
},
{
name: “Rejoice Timeless”,
genre: “Timeless Truths”,
src: “
https://rejoice.primcast.com/proxy/timeless/stream”
},
{
name: “Rejoice Pulpit”,
genre: “Sermons & Preaching”,
src: “
https://rejoice.primcast.com/proxy/rejoice_pulpit/RBNSermon”
}
];
let currentStationIndex = 0;
let isPlaying = false;
const audio = document.getElementById(‘audio-player’);
const playBtn = document.getElementById(‘play-btn’);
const playIcon = document.getElementById(‘play-icon’);
const pauseIcon = document.getElementById(‘pause-icon’);
const prevBtn = document.getElementById(‘prev-btn’);
const nextBtn = document.getElementById(‘next-btn’);
const volumeSlider = document.getElementById(‘volume-slider’);
const stationNameEl = document.getElementById(‘station-name’);
const stationGenreEl = document.getElementById(‘station-genre’);
const statusText = document.getElementById(‘status-text’);
const liveDot = document.getElementById(‘live-dot’);
const stationContainer = document.getElementById(‘station-container’);
const volumeDisplay = document.getElementById(‘volume-display’);
const visualizerContainer = document.getElementById(‘visualizer’);
function init() {
renderStations();
setupVisualizer();
loadStation(0, false);
audio.addEventListener(‘error’, function() {
statusText.textContent = “Offline/Error”;
statusText.className = “text-red-300 font-bold font-sans”;
isPlaying = false;
updatePlayButton();
setVisualizerState(false);
liveDot.classList.add(‘hidden’);
});
audio.addEventListener(‘waiting’, () => { statusText.textContent = “Buffering…”; });
audio.addEventListener(‘playing’, () => {
statusText.textContent = “Live”;
statusText.className = “text-yellow-400 font-bold font-sans”;
liveDot.classList.remove(‘hidden’);
});
audio.addEventListener(‘canplay’, () => {
if (isPlaying) {
audio.play().catch(e => console.log(“Play failed: “, e));
}
});
}
function setupVisualizer() {
visualizerContainer.innerHTML = ”;
for(let i=0; i {
playing ? bar.classList.remove(‘paused’) : bar.classList.add(‘paused’);
});
}
function renderStations() {
stationContainer.innerHTML = ”;
stations.forEach((station, index) => {
const el = document.createElement(‘div’);
el.className = `station-item p-3 mb-1 rounded-lg cursor-pointer hover:bg-white/10 transition flex items-center justify-between group ${index === currentStationIndex ? ‘active’ : ”}`;
el.innerHTML = `
${station.name}
${station.genre}
`;
el.onclick = () => {
if (index !== currentStationIndex) {
loadStation(index, true);
} else {
togglePlay();
}
};
stationContainer.appendChild(el);
});
}
function loadStation(index, autoPlay) {
audio.pause();
audio.removeAttribute(‘src’);
audio.load();
currentStationIndex = index;
const station = stations[index];
stationNameEl.textContent = station.name;
stationGenreEl.textContent = station.genre;
renderStations();
audio.src = station.src + (station.src.includes(‘?’) ? ‘&’ : ‘?’) + ‘cb=’ + Date.now();
statusText.textContent = autoPlay ? “Connecting…” : “Ready”;
statusText.className = “text-white/60 font-sans”;
liveDot.classList.add(‘hidden’);
if (autoPlay) {
isPlaying = true;
updatePlayButton();
setVisualizerState(true);
audio.play().catch(e => console.log(“Initial play blocked:”, e));
}
}
async function playAudio() {
try {
statusText.textContent = “Connecting…”;
isPlaying = true;
updatePlayButton();
setVisualizerState(true);
if (!audio.src || audio.src === window.location.href) {
audio.src = stations[currentStationIndex].src;
}
await audio.play();
} catch (error) {
if (error.name !== “AbortError”) {
statusText.textContent = “Format Error”;
isPlaying = false;
updatePlayButton();
setVisualizerState(false);
}
}
}
function pauseAudio() {
audio.pause();
isPlaying = false;
updatePlayButton();
setVisualizerState(false);
statusText.textContent = “Paused”;
statusText.className = “text-white/60 font-sans”;
liveDot.classList.add(‘hidden’);
}
function togglePlay() {
isPlaying ? pauseAudio() : playAudio();
}
function updatePlayButton() {
if (isPlaying) {
playIcon.classList.add(‘hidden’);
pauseIcon.classList.remove(‘hidden’);
} else {
playIcon.classList.remove(‘hidden’);
pauseIcon.classList.add(‘hidden’);
}
}
playBtn.addEventListener(‘click’, togglePlay);
prevBtn.addEventListener(‘click’, () => {
let idx = currentStationIndex – 1;
if (idx {
let idx = currentStationIndex + 1;
if (idx >= stations.length) idx = 0;
loadStation(idx, true);
});
volumeSlider.addEventListener(‘input’, (e) => {
const val = parseFloat(e.target.value);
audio.volume = val;
volumeDisplay.textContent = `Vol: ${Math.round(val * 100)}%`;
});
init();
});