Throwback player body { margin: 0; padding: 40px 16px; background: #050505; display: flex; justify-content: center; } /* ———- shell ———- */ .tbp { –red: #d31017; –red-lit: #ff2b2b; –red-deep: #6b0206; –ink: #050505; –steel-hi: #4a4a4a; –steel-lo: #171717; –text: #d8d8d8; –text-dim: #8b8b8b; width: 440px; max-width: 100%; font: 11px/1.4 Verdana, Tahoma, Geneva, sans-serif; color: var(–text); background: linear-gradient(#232323, #101010); border: 1px solid #000; border-radius: 3px; box-shadow: inset 0 1px 0 rgba(255,255,255,.14), 0 0 0 1px rgba(255,255,255,.05), 0 14px 34px rgba(0,0,0,.75); padding: 6px; box-sizing: border-box; -webkit-font-smoothing: antialiased; } .tbp *, .tbp *::before, .tbp *::after { box-sizing: border-box; } /* ———- title bar ———- */ .tbp-titlebar { display: flex; align-items: center; justify-content: space-between; padding: 0 2px 6px; } .tbp-wordmark { display: flex; align-items: baseline; gap: 5px; letter-spacing: .02em; } .tbp-wordmark b { font-size: 15px; font-weight: 700; color: #fff; text-shadow: 0 0 12px rgba(211,16,23,.6); } .tbp-wordmark span { font-size: 9px; color: var(–red-lit); } .tbp-winbtns { display: flex; gap: 3px; } .tbp-winbtns i { width: 13px; height: 12px; background: linear-gradient(#3d3d3d, #1a1a1a); border: 1px solid #000; border-radius: 2px; box-shadow: inset 0 1px 0 rgba(255,255,255,.15); } /* ———- top deck ———- */ .tbp-deck { display: flex; gap: 6px; } .tbp-art { width: 96px; height: 96px; flex: none; border: 1px solid #000; background: repeating-linear-gradient(135deg, #1b0203 0 8px, #2a0406 8px 16px); box-shadow: inset 0 0 0 1px rgba(255,255,255,.07); object-fit: cover; display: block; } .tbp-console { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 5px; } /* LCD */ .tbp-lcd { position: relative; background: repeating-linear-gradient(rgba(0,0,0,.35) 0 1px, transparent 1px 3px), linear-gradient(#1a0304, #0a0101); border: 1px solid #000; box-shadow: inset 0 0 14px rgba(211,16,23,.22), inset 0 1px 0 rgba(255,255,255,.06); padding: 5px 7px; display: flex; align-items: center; justify-content: space-between; gap: 8px; overflow: hidden; } .tbp-lcd-text { min-width: 0; } .tbp-track { font-weight: 700; font-size: 12px; color: #ff5a55; text-shadow: 0 0 8px rgba(255,60,55,.55); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .tbp-artist { font-size: 10px; color: #a8514f; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .tbp-state { font-size: 9px; color: var(–red-lit); letter-spacing: .08em; flex: none; align-self: flex-start; } /* EQ */ .tbp-eq { display: flex; align-items: flex-end; gap: 2px; height: 22px; flex: none; } .tbp-eq i { width: 3px; background: linear-gradient(#ff6a3d, #d31017 60%, #6b0206); height: 4px; transform-origin: bottom; } .tbp.is-playing .tbp-eq i { animation: tbp-bounce .9s ease-in-out infinite alternate; } .tbp-eq i:nth-child(1) { animation-delay: -.10s; } .tbp-eq i:nth-child(2) { animation-delay: -.45s; } .tbp-eq i:nth-child(3) { animation-delay: -.72s; } .tbp-eq i:nth-child(4) { animation-delay: -.28s; } .tbp-eq i:nth-child(5) { animation-delay: -.60s; } .tbp-eq i:nth-child(6) { animation-delay: -.05s; } @keyframes tbp-bounce { from { height: 3px; } to { height: 20px; } } /* scrubber */ .tbp-scrub { display: flex; align-items: center; gap: 7px; } .tbp-time { font: 10px/1 “Lucida Console”, Monaco, monospace; color: #ff5a55; flex: none; letter-spacing: .04em; } .tbp-rail { position: relative; flex: 1; height: 12px; display: flex; align-items: center; border: 0; padding: 0; background: none; cursor: pointer; } .tbp-rail::before { content: “”; position: absolute; left: 0; right: 0; height: 6px; background: linear-gradient(#000, #1c1c1c); border: 1px solid #000; box-shadow: inset 0 1px 2px rgba(0,0,0,.9); } .tbp-fill { position: absolute; left: 1px; height: 4px; width: 0; background: linear-gradient(var(–red-lit), var(–red-deep)); box-shadow: 0 0 7px rgba(255,43,43,.7); } .tbp-knob { position: absolute; left: 0; width: 11px; height: 11px; margin-left: -5px; border-radius: 50%; background: radial-gradient(circle at 34% 30%, #fff, #bdbdbd 45%, #4c4c4c); border: 1px solid #000; } /* ———- transport ———- */ .tbp-transport { display: flex; align-items: center; gap: 4px; padding: 6px 2px 7px; } .tbp-btn { width: 46px; height: 26px; display: inline-flex; align-items: center; justify-content: center; background: linear-gradient(var(–steel-hi), var(–steel-lo)); border: 1px solid #000; border-radius: 3px; box-shadow: inset 0 1px 0 rgba(255,255,255,.22), inset 0 -1px 0 rgba(0,0,0,.7); cursor: pointer; padding: 0; } .tbp-btn svg { fill: #cfcfcf; } .tbp-btn:hover svg { fill: #fff; } .tbp-btn:active { background: linear-gradient(#0d0d0d, #262626); box-shadow: inset 0 2px 4px rgba(0,0,0,.9); } .tbp-btn–play { width: 68px; } .tbp-btn–play svg { fill: var(–red-lit); filter: drop-shadow(0 0 5px rgba(255,43,43,.75)); } .tbp-spacer { flex: 1; } .tbp-link { font-size: 9px; letter-spacing: .06em; color: var(–text-dim); text-decoration: none; border: 1px solid #000; border-radius: 3px; background: linear-gradient(#333, #151515); box-shadow: inset 0 1px 0 rgba(255,255,255,.16); padding: 6px 9px; } .tbp-link:hover { color: #fff; } /* ———- stats ———- */ .tbp-stats { display: flex; justify-content: space-between; gap: 6px; padding: 5px 9px; background: linear-gradient(#e8262c, #a10a10 52%, #6b0206); border: 1px solid #000; box-shadow: inset 0 1px 0 rgba(255,255,255,.35), inset 0 -1px 0 rgba(0,0,0,.5); color: #fff; font-size: 10px; text-shadow: 0 1px 0 rgba(0,0,0,.45); } .tbp-stats b { font-weight: 700; } /* ———- barbed wire ———- */ .tbp-wire { display: block; width: 100%; height: auto; margin: 7px 0 3px; filter: drop-shadow(0 0 6px rgba(255,43,43,.45)); pointer-events: none; } /* ———- list window ———- */ .tbp-window { margin-top: 3px; } .tbp-tab { display: flex; align-items: center; justify-content: space-between; background: linear-gradient(#3a3a3a, #191919); border: 1px solid #000; border-bottom: 0; box-shadow: inset 0 1px 0 rgba(255,255,255,.18); padding: 5px 8px; font-size: 10px; color: #e2e2e2; } .tbp-tab i { width: 0; height: 0; border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 5px solid var(–red-lit); } .tbp-pane { border: 1px solid #000; background: linear-gradient(#141414, #0a0a0a); box-shadow: inset 0 1px 0 rgba(255,255,255,.05); padding: 4px; } .tbp-list { list-style: none; margin: 0; padding: 0; } .tbp-row { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; background: none; border: 0; border-bottom: 1px solid #1d1d1d; padding: 8px 6px; color: inherit; font: inherit; cursor: pointer; } .tbp-list li:last-child .tbp-row { border-bottom: 0; } .tbp-row:hover { background: rgba(211,16,23,.13); } .tbp-row[aria-current=”true”] { background: rgba(211,16,23,.22); } .tbp-row[aria-current=”true”] .tbp-name { color: #ff5a55; } .tbp-num { color: var(–text-dim); flex: none; width: 14px; } .tbp-name { font-weight: 700; font-size: 12px; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .tbp-plays { font-size: 9px; color: var(–text-dim); flex: none; } .tbp-embed { display: block; width: 100%; border: 0; } .tbp-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 6px 2px 1px; font-size: 9px; color: #5f5f5f; letter-spacing: .04em; } .tbp-foot a { color: #8b8b8b; text-decoration: none; } .tbp-foot a:hover { color: var(–red-lit); } .tbp :focus-visible { outline: 2px solid var(–red-lit); outline-offset: 1px; } @media (max-width: 420px) { .tbp-art { width: 74px; height: 74px; } .tbp-btn { width: 38px; } .tbp-btn–play { width: 54px; } .tbp-link { display: none; } } @media (prefers-reduced-motion: reduce) { .tbp.is-playing .tbp-eq i { animation: none; height: 12px; } }
XauraKat’s Mixmusic
Loading
STOPPED
0:00 0:00
0 total plays 0 adds 0 plays today
featured playlist
a place for friends powered by Spotify
/* ================= CONFIG ================= */ const CONFIG = { // Your playlist. Swap in your own ID. playlistId: ’19Ilkfzn4le7rZ2kjrTmOO’, profileName: “XauraKat’s Mix”, artistLine: “this mix was made just for you document.getElementById(id); const shell = el(‘tbp’); const playIcon = el(‘tbp-play-icon’); const listEl = el(‘tbp-list’); const playlistUri = ‘spotify:playlist:’ + CONFIG.playlistId; const playlistUrl = ‘https://open.spotify.com/playlist/’ + CONFIG.playlistId; el(‘tbp-profile’).textContent = CONFIG.profileName; el(‘tbp-caption’).textContent = CONFIG.caption; el(‘tbp-tab-label’).textContent = CONFIG.tabLabel; el(‘tbp-total-plays’).textContent = CONFIG.stats.totalPlays; el(‘tbp-adds’).textContent = CONFIG.stats.adds; el(‘tbp-today’).textContent = CONFIG.stats.playsToday; el(‘tbp-open’).href = playlistUrl; el(‘tbp-foot-link’).href = playlistUrl; if (CONFIG.artworkUrl) el(‘tbp-art’).src = CONFIG.artworkUrl; const tracks = CONFIG.tracks || []; const useList = tracks.length > 0; let index = 0; let controller = null; let duration = 0; let paused = true; const clock = ms => { const s = Math.max(0, Math.floor(ms / 1000)); return Math.floor(s / 60) + ‘:’ + String(s % 60).padStart(2, ‘0’); }; function screen(title, sub, state) { el(‘tbp-track’).textContent = title; el(‘tbp-artist’).textContent = sub; el(‘tbp-state’).textContent = state; } function progress(position) { const pct = duration ? Math.min(100, (position / duration) * 100) : 0; el(‘tbp-fill’).style.width = pct + ‘%’; el(‘tbp-knob’).style.left = pct + ‘%’; el(‘tbp-elapsed’).textContent = clock(position); el(‘tbp-total’).textContent = clock(duration); } function setPlaying(isPlaying) { paused = !isPlaying; shell.classList.toggle(‘is-playing’, isPlaying); el(‘tbp-state’).textContent = isPlaying ? ‘PLAYING’ : ‘PAUSED’; el(‘tbp-play’).setAttribute(‘aria-label’, isPlaying ? ‘Pause’ : ‘Play’); playIcon.innerHTML = isPlaying ? ” : ”; } // Build the numbered list if (useList) { listEl.hidden = false; el(‘tbp-embed-slot’).style.height = ’80px’; tracks.forEach((t, i) => { const li = document.createElement(‘li’); const row = document.createElement(‘button’); row.className = ‘tbp-row’; row.type = ‘button’; row.innerHTML = ‘‘ + (i + 1) + ‘.‘ + ‘‘ + ‘‘; row.querySelector(‘.tbp-name’).textContent = t.title; row.querySelector(‘.tbp-plays’).textContent = (t.plays || ”) && t.plays + ‘ plays’; row.addEventListener(‘click’, () => select(i, true)); li.appendChild(row); listEl.appendChild(li); }); } function select(i, autoplay) { if (!useList || !controller) return; index = (i + tracks.length) % tracks.length; const t = tracks[index]; listEl.querySelectorAll(‘.tbp-row’).forEach((r, n) => r.setAttribute(‘aria-current’, String(n === index))); screen(t.title, CONFIG.artistLine, ‘LOADING’); duration = 0; progress(0); if (t.uri) controller.loadUri(t.uri, false, 0); if (autoplay) setTimeout(() => controller.play(), 400); } screen(useList ? tracks[0].title : CONFIG.profileName, CONFIG.artistLine, ‘STOPPED’); window.onSpotifyIframeApiReady = API => { const first = useList && tracks[0].uri ? tracks[0].uri : playlistUri; API.createController( el(‘tbp-embed’), { uri: first, width: ‘100%’, height: useList ? 80 : CONFIG.embedHeight }, ctrl => { controller = ctrl; if (useList) select(0, false); ctrl.addListener(‘playback_update’, e => { duration = e.data.duration || duration; progress(e.data.position || 0); setPlaying(!e.data.isPaused); }); el(‘tbp-play’).addEventListener(‘click’, () => ctrl.togglePlay()); el(‘tbp-prev’).addEventListener(‘click’, () => { if (useList) select(index – 1, !paused); else ctrl.seek(0); }); el(‘tbp-next’).addEventListener(‘click’, () => { if (useList) select(index + 1, !paused); }); el(‘tbp-rail’).addEventListener(‘click’, ev => { if (!duration) return; const box = ev.currentTarget.getBoundingClientRect(); ctrl.seek(((ev.clientX – box.left) / box.width) * (duration / 1000)); }); if (CONFIG.autoplayOnFirstTouch) armAutoplay(ctrl); } ); }; // Start on the first interaction anywhere on the page. If that interaction // is a click on the player itself, stand down and let the button do its job. function armAutoplay(ctrl) { const events = [‘pointerdown’, ‘keydown’, ‘touchstart’]; let done = false; const go = ev => { if (done) return; done = true; events.forEach(t => document.removeEventListener(t, go, true)); if (ev.target && ev.target.closest && ev.target.closest(‘#tbp’)) return; ctrl.play(); }; events.forEach(t => document.addEventListener(t, go, { capture: true, passive: true })); } // If Spotify’s control script is blocked (ad blockers, sandboxed previews, // opening the file straight off the desktop), fall back to a plain embed so // the music still plays. Spotify’s own play button drives it in that case. setTimeout(() => { if (controller) return; const frame = document.createElement(‘iframe’); frame.className = ‘tbp-embed’; frame.src = ‘https://open.spotify.com/embed/playlist/’ + CONFIG.playlistId + ‘?theme=0’; frame.height = CONFIG.embedHeight; frame.allow = ‘autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture’; frame.loading = ‘lazy’; frame.title = ‘Spotify playlist’; el(‘tbp-embed-slot’).replaceChildren(frame); el(‘tbp-embed-slot’).style.height = ”; listEl.hidden = true; screen(CONFIG.profileName, CONFIG.artistLine, ‘READY’); }, 4000); })(); https://open.spotify.com/embed/iframe-api/v1