Glassmorphism✨ Premium

Neu Music Player

A 300 px raised audio player: a 120 px round cover that spins like a record (one turn in 8 s) only while playing, a carved progress bar that moves, previous and next buttons that restart and notify your code. Drives an audio element if it finds one.

CSSJSPlayer

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Glassmorphism has 47 effects, including 6 free. Explore the category →

3 usage examples

Player for a connected turntable — companion app — Neu Music Player example 1

① Player for a connected turntable — companion app

WhenThe app shows the record on the turntable: title, side, track list, and the cover spinning while it plays.
WhyThe round cover that only spins during playback reproduces the record on the platter; when stopped, it freezes where it was, like a record you stop.
SettingsOriginal player (300 px), data-duration="252" for a 4:12 track, starting progress at 42%. The surface's 40 px padding is set to 0: the app screen already has the material's tint. Artist in #4b5563. Turntable and side A track list on the right.
Lullabies of a baby night light — the night light's app — Neu Music Player example 2

② Lullabies of a baby night light — the night light's app

WhenThe parent starts “Au clair de la lune” from their phone without entering the room; the music will stop in 20 minutes.
WhyBig, soft round buttons, easy to hit in the dark (52 px for play); the spinning cover confirms at a glance that the music is playing, without turning the volume up to check.
SettingsOriginal player, data-duration="190". Beside it: the night light's glow, a carved timer, the lullaby list. Started in playback by the scene.
Car stereo of a camper van — dashboard touch screen — Neu Music Player example 3

③ Car stereo of a camper van — dashboard touch screen

WhenOn the road to Annecy, the driver taps Next: the title changes, the bar restarts, the list moves on.
WhyRaised 40 and 52 px buttons are easy to hit on a bumpy road; and the neuplayer:next event lets the stereo pick the next track without touching the player's code.
SettingsPlayer on the dashboard screen, data-duration="214". The scene listens to neuplayer:next to change title, artist and list; in the capture, Next is tapped 1.2 s after the screen appears.

How it works

The .neu-player case (300 px, padding: 24px, border-radius: 24px) rises from the #e0e5ec material through two 10 px / 20 px shadows. Inside, top to bottom: the .neu-player-art cover, a 120 px disc in a #8b5cf6 → #ec4899 gradient with a white music note, raised (6 px / 12 px); the title (15.2 px, #374151) and the artist; a carved 6 px progress groove (2 px / 4 px inset shadows) whose fill reuses the gradient; three round raised buttons, 40 px for previous and next, 52 px for play, which switch to inset shadows while pressed (:active).

The cover always carries the neuSpin animation (one turn in 8 s, linear, looping) but in animation-play-state: paused; the .playing class on the player switches it to running. The rotation therefore resumes where it stopped, like a record you let go. The JavaScript (a loop over every .neu-player on the page) wires the buttons: play toggles .playing and aria-pressed; without an <audio> element, progress moves 0.25 s every quarter second over a duration read from data-duration (180 s by default) and restarts at the end. Previous and next reset the progress to the start, then dispatch neuplayer:prev or neuplayer:next on the player: that is where your code changes track. If the player contains an <audio>, the script plays and pauses it, ties the bar to currentTime / duration and dispatches neuplayer:next when the track ends (measured in Chromium and Firefox with a 4 s file).

The player ships with its script, and with no inline onclick: a mere attribute on the play button would spin the cover, but the bar would stay frozen and previous/next would do nothing (test: 88.2 px before and after 1.2 s of playback). The script moves the bar and fires events your code can listen to, and it stays compatible with a security policy that forbids inline scripts. The three buttons only hold an icon: they therefore carry type="button", an aria-label (“Morceau précédent”, “Lecture”, “Morceau suivant”) and a violet focus ring. .neu-player-scene carries its surface (#e0e5ec, padding: 40px) like the whole family.

Accessibility

  • Named buttons and keyboard: measured in Chromium and Firefox, Tab goes through previous, play, next; Enter and Space activate them; the play button announces its state through aria-pressed (“Lecture, button, pressed”). Focus is shown by outline: 2px solid #8b5cf6 (3.3:1). The play icon itself does not turn into “pause”: only aria-pressed and the spinning cover say the music is playing — see Customize to swap the icon.
  • prefers-reduced-motion not in the code: the cover spins continuously during playback, even when the system asks for less motion. Add @media (prefers-reduced-motion: reduce) { .neu-player-art { animation: none } }: playback is still signaled by aria-pressed and the moving bar.
  • Contrast: the #374151 title measures 8.1:1 on the material, but the #9ca3af artist (12.5 px) only 2.0:1 — switch it to #4b5563 (6.0:1), as in the scenes. The gray #6b7280 icons (3.8:1) and the violet one (3.3:1) pass the 3:1 threshold for graphics; the buttons' outlines only exist through shadows (1.5:1).
  • Progress bar: it is a div with no role, not operable. For a real player, add role="progressbar" with aria-valuenow, or replace it with an input type="range" if users must be able to seek (see Neu Slider, fx-0315).

Browser compatibility

CSS: drop and inset box-shadow, @keyframes on transform: rotate(), animation-play-state, :focus-visible. JavaScript: CustomEvent (Chrome 15, Firefox 11, Safari 6), optional HTMLMediaElement. Measured in Chromium, Firefox and WebKit (Playwright): spinning only while playing, progress moving, previous/next resetting and dispatching their event, no console error. audio.play() is only allowed after a user gesture: that is the case here, since playback starts from a click.

Chrome 86+✓ Full
Firefox 85+✓ Full
Safari 15.4+✓ Full
Edge 86+✓ Full
Mobile iOS 15.4+✓ Full (audio started on tap)
Android Chrome✓ Full

The versions given are those of :focus-visible; before them, everything works except the keyboard focus ring. Without JavaScript, the player shows but no longer reacts: still cover, buttons without effect.

The code

HTML structure to paste into your page (the CSS + JS come with Premium):

index.html — structure
<div class="neu-player-scene">
  <div class="neu-player">
    <!-- optional: data-duration="180" on .neu-player (length in seconds), or <audio src="…"></audio> -->
    <div class="neu-player-art"><svg viewBox="0 0 24 24" aria-hidden="true">…</svg></div>
    <div class="neu-player-info">
      <h4>Midnight Drive</h4>
      <p>Synthwave FM</p>
    </div>
    <div class="neu-player-progress"><div class="neu-player-progress-fill"></div></div>
    <div class="neu-player-controls">
      <button type="button" class="neu-player-btn neu-player-btn--prev" aria-label="Morceau précédent">…</button>
      <button type="button" class="neu-player-btn neu-player-btn--play" aria-label="Lecture" aria-pressed="false">…</button>
      <button type="button" class="neu-player-btn neu-player-btn--next" aria-label="Morceau suivant">…</button>
    </div>
  </div>
</div>
Unlock the full code — $9.90/mo incl. tax

The full HTML + CSS + JS of Neu Music Player, ready to paste — plus all 840 effects. Or $299 once, lifetime access.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Plug in real sound (HTML — <audio src="…"> inside .neu-player) no audio: progress simulated over data-duration Place an <audio src="track.mp3" preload="metadata"></audio> anywhere in the player: play, pause, progress and end of track follow with no other code.
Change track (JS — neuplayer:prev / neuplayer:next events) back to the start of the track, nothing else player.addEventListener('neuplayer:next', () => { audio.src = tracks[++i]; h4.textContent = titles[i]; audio.play(); }). The event bubbles: a single listener on document serves several players.
Play / pause icon (HTML + CSS — .neu-player.playing) fixed play icon Add a second SVG (two bars) in the button and swap them: .neu-player .ico-pause, .neu-player.playing .ico-play { display: none } .neu-player.playing .ico-pause { display: block }.
Spin speed (CSS — animation: 8s linear … neuSpin) one turn in 8 s 1.8 s is the real speed of a 33 rpm record; beyond 12 s, the rotation goes unnoticed.
Cover (CSS — .neu-player-art, #8b5cf6 → #ec4899 gradient) gradient disc with a note Replace the background with the album art: background: url(cover.jpg) center / cover, and remove the SVG. Keep border-radius: 50% for the record effect.

FAQ

No: without an <audio> element, it is the interface alone, with progress simulated over data-duration. Add an <audio> inside .neu-player and the script drives it: play and pause on the button, bar tied to the real position, neuplayer:next event at the end of the track.
Because the player does not know your track list. It does what every player does — go back to the start — then dispatches neuplayer:prev: your code loads the previous track if there is one.
Neu Card is an information container where only the gauge breathes. Neu Music Player is a complete interactive component: three buttons that sink in, a cover whose rotation depends on the playback state, and a bar that moves with the track.