webdevpuneet.com

Music Player Card — Free HTML CSS JS Snippet

Music Player Card · Cards · Plain HTML, CSS & JS · Live preview

CategoryCards

Copy & paste

Source Code

<div class="scene">
  <div class="player">
    <div class="album" id="album">
      <div class="vinyl" id="vinyl"></div>
    </div>
    <div class="meta">
      <div class="track-info">
        <div class="track-name">Midnight City</div>
        <div class="artist">M83</div>
      </div>
      <button class="like-btn" id="like" onclick="toggleLike()">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg>
      </button>
    </div>
    <div class="progress-wrap">
      <span class="time" id="cur">1:24</span>
      <div class="progress-track" id="track" onclick="seek(event)">
        <div class="progress-fill" id="fill" style="width:35%">
          <div class="thumb"></div>
        </div>
      </div>
      <span class="time">3:53</span>
    </div>
    <div class="controls">
      <button class="ctrl-btn" title="Shuffle">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 3 21 3 21 8"/><line x1="4" y1="20" x2="21" y2="3"/><polyline points="21 16 21 21 16 21"/><line x1="4" y1="4" x2="9" y2="9"/></svg>
      </button>
      <button class="ctrl-btn prev" onclick="prev()">
        <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><polygon points="19 20 9 12 19 4 19 20"/><line x1="5" y1="19" x2="5" y2="5"/></svg>
      </button>
      <button class="play-btn" id="play" onclick="togglePlay()">
        <svg id="play-icon" width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><polygon points="5 3 19 12 5 21 5 3"/></svg>
      </button>
      <button class="ctrl-btn next" onclick="next()">
        <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><polygon points="5 4 15 12 5 20 5 4"/><line x1="19" y1="5" x2="19" y2="19"/></svg>
      </button>
      <button class="ctrl-btn" title="Repeat">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/><polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/></svg>
      </button>
    </div>
    <div class="volume-row">
      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/></svg>
      <input type="range" class="vol-slider" value="70" oninput="setVol(this.value)" />
      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/></svg>
    </div>
    <audio id="audio" preload="metadata"></audio>
  </div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Real playback via a native <audio> element — no Web Audio API, no external player library
Sample tracks included (royalty-free SoundHelix demo MP3s) so the preview plays real audio out of the box
Vinyl CSS spin animation: @keyframes rotate(360deg) 4s linear infinite
animation-play-state toggled via .spinning class, driven by the audio element's own play/pause/ended events
Play/pause SVG icon swap via innerHTML replacement — no dual-element show/hide
Auto-advance on the native "ended" event — continuous playback into the next track, not just a UI reset
Next/prev track with modulo wraparound: (idx + 1) % tracks.length
Progress bar and elapsed time driven by real timeupdate/loadedmetadata events, not a setInterval timer
Click-to-seek sets audio.currentTime from click position; volume slider sets real audio.volume
Export as HTML file, React JSX, or React + Tailwind CSS
Mobile (375px), Tablet (768px), Desktop device preview buttons
Live split-pane editor — preview updates as you type

About this UI Snippet

Music Player Card — Real Audio Playback, Vinyl Spin Animation & Play/Pause State

Screenshot of the Music Player Card snippet rendered live

A music player UI card demonstrates several important interactive patterns: a toggle between two states (play/pause), cycling through an array of items (tracks), animated visual feedback (vinyl spin), and a progress bar. These patterns appear in audio players, video players, carousels, and any queue-based interface. This snippet wires a real <audio> element to sample tracks, so play, pause, seek, volume and track duration are all genuine browser playback state rather than a simulation.

The tracks data structure

The tracks array contains objects with name, artist, and src (an MP3 URL). load(i, autoplay) reads these values, updates the DOM with querySelector and textContent, sets audio.src, and optionally starts playback immediately — used when auto-advancing to the next track.

The vinyl spin animation

The vinyl disc uses a CSS @keyframes spin animation: transform: rotate(360deg) at 4s linear infinite. The vinyl starts with animation-play-state: paused. The .spinning class changes it to running. Rather than being toggled from inside togglePlay(), the spin state is driven entirely by the audio element's own play/pause/ended events through a single setPlayingUI() function — so the vinyl, the icon, and the internal playing flag can never drift out of sync with what the browser is actually doing, even when playback stops on its own (buffering, an error, or a track ending).

The play/pause SVG icon swap

document.getElementById('play-icon').innerHTML is set to a pause icon SVG on play and a play icon SVG on pause, inside the same setPlayingUI() function that toggles the vinyl. The inner HTML assignment replaces the entire SVG content, swapping from a triangle (play) to two rectangles (pause), avoiding two separate icon elements that need to be shown and hidden.

Track switching and auto-advance

next() increments idx modulo tracks.length for wraparound, then calls load(idx, autoplay). prev() decrements similarly. Reaching the end of a track fires the audio element's native ended event, which calls next(true) — so auto-advance keeps playing continuously into the next track, and the play/pause icon and vinyl stay correct throughout because they're driven by the same play/pause event listeners regardless of whether the click came from a user or the browser itself.

Real progress, not a timer

The progress bar fill and the elapsed-time label are both written from the audio element's timeupdate event, using audio.currentTime and audio.duration — there's no setInterval anywhere. The total-duration label updates from the loadedmetadata event once the browser knows the real length of the file, which is also why it briefly reads 0:00 right after a track switch.

Click-to-seek and volume

Clicking the progress bar computes a 0–1 fraction from the click's position relative to the bar's own getBoundingClientRect(), then sets audio.currentTime directly — the browser jumps playback to that instant. The volume slider's oninput sets audio.volume to a 0–1 fraction of its 0–100 value, so it's a real, functional control rather than a cosmetic one.

Step by step

How to Use

  1. 1
    Interact with the playerClick play to start real playback and the vinyl spin. Click prev/next to switch tracks — the current one keeps playing through the switch if it was already playing. Drag the volume slider to change the actual audio volume.
  2. 2
    Swap in your own tracksIn the JS panel, update the tracks array with your own name, artist, and src (a direct MP3 URL). Duration is read automatically from the file — you don't set it by hand.
  3. 3
    Add a like button or shuffleCopy a control button from the .controls row and add an onclick to toggle a .liked class, or call load(Math.floor(Math.random() * tracks.length), playing) for shuffle.
  4. 4
    Change the vinyl gradientIn the CSS panel, update the background gradient on .vinyl to change the disc colour.
  5. 5
    Host your own audio filesPoint each track's src at your own hosted MP3 (same-origin or a CORS-enabled CDN). No other code changes are required — load(), seek(), and the progress bar all read straight from the audio element.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for a React component, or "Tailwind" for a React + Tailwind version.

Real-world uses

Common Use Cases

Podcast and audio player widgets
Use as the UI for an embedded podcast player — point each track's src at your episode MP3s and the play/pause, seek, and progress bar all work immediately.
Portfolio background music player
Add an ambient music player to a creative portfolio. The dark card and vinyl aesthetic match developer and designer portfolio aesthetics.
Learn event-driven audio UI state
The vinyl, icon, and progress bar are all driven by the audio element's own play/pause/timeupdate/ended events rather than a variable you update by hand. Edit the panels to see how that keeps everything in sync.
Prototype media player interactions
Use as a prototype to test track switching UX, progress bar behaviour, and control placement with real playback rather than a fake timer.
Swap in your own hosted audio
Replace the src URLs in the tracks array with your own hosted MP3s. load(), seek(), and the progress bar already read entirely from the audio element, so no other logic needs to change.
Music streaming service card UI
Use as a now-playing widget in a music streaming UI. The track array structure maps directly to a playlist API response with name, artist, and src fields.

Got questions?

Frequently Asked Questions

It plays real audio through a native <audio> element, using royalty-free SoundHelix sample tracks so the preview works out of the box. Play, pause, seek, volume, and duration are all genuine browser playback state, not a setInterval simulation.

The vinyl has animation-play-state: paused by default. A setPlayingUI() function toggles it to running and swaps the play/pause icon, and it's called from the audio element's own play, pause, and ended event listeners — so the vinyl can never fall out of sync with what the browser is actually doing.

The audio element's native "ended" event fires when a track finishes, which calls next(true) to load and immediately play the following track — real continuous playback, not just a UI reset.

next() sets idx = (idx + 1) % tracks.length for modulo wraparound and calls load(idx, autoplay). load() reads the tracks[i] object, updates the track name/artist/duration display, and sets audio.src to the new track's file.

Add objects to the tracks array in the JS panel: { name: "Song Name", artist: "Artist", src: "https://your-cdn.com/song.mp3" }. Duration is read automatically from the file via the loadedmetadata event — there's no duration field to set by hand.

Yes. Click "JSX" for a React component. Manage idx and playing as useState, keep a ref to the <audio> element, and attach the play/pause/timeupdate/ended listeners in a useEffect that cleans itself up on unmount.