Mobile Music Player Screen — Free HTML CSS JS UI

Mobile Music Player Screen · Mobile · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Real elapsed clock
setInterval drives a live m:ss timer.
Play-linked spin
animation-play-state toggles the vinyl record.
CSS vinyl disc
Grooves drawn with a repeating-radial-gradient.
Draggable seek bar
Pointer Events with click-to-seek and knob drag.
Clamped scrubbing
Ratio clamped to 0..1 so you never overshoot.
One render function
Bar and clock always stay in sync.
Mode toggles
Like, shuffle, and repeat flip accent colors.
No dependency
Pure HTML, CSS, and vanilla JavaScript.

About this UI Snippet

Mobile Music Player Screen — Now Playing UI

Screenshot of the Mobile Music Player Screen snippet rendered live

A now-playing screen is the emotional center of a music app — big album art, the track title, a scrubbable progress bar, and a ring of transport controls. This snippet builds a complete, functional one inside a CSS phone frame: the play button starts a real elapsed-time clock, the artwork disc spins only while playing, and the seek bar is draggable with the pointer — in HTML, CSS, and vanilla JavaScript with no dependency or audio file.

A play state that drives everything

Tapping play sets a playing flag, swaps the button glyph to a pause bar, and starts a setInterval that increments the current time every second and re-renders the progress bar. Pausing clears the interval and restores the play glyph. A single render() function converts the elapsed seconds into a percentage for the fill width and knob position and formats the m:ss time, so the visual bar and the clock can never disagree.

The spinning record, tied to playback

The album art holds a small vinyl disc drawn entirely in CSS with a repeating-radial-gradient for the grooves and a pink center label. It has a continuous rotation animation that is paused by default; adding a .playing class flips animation-play-state to running, so the record spins only while the track plays and freezes the instant you pause — a small, satisfying detail that needs no JavaScript beyond the class toggle.

A draggable seek bar

The progress track responds to both a click-to-seek and a drag on the knob using Pointer Events. Clicking anywhere jumps the time to that fraction; pressing the knob starts a drag that follows pointermove on the window (so the finger can leave the bar) and releases on pointerup. The seek math clamps the ratio to 0..1 so you can never scrub past either end, and it reads clientX from touch or mouse events alike.

Transport toggles

Like, shuffle, and repeat are independent toggle buttons that flip an accent color, mirroring how these persistent modes work in real players versus the momentary previous/next buttons. The like heart also swaps between outline and filled.

Accessibility and performance

Every transport control is a real <button> with an aria-label that updates between "Play" and "Pause" as the state changes, so screen readers always announce the current action. When you adapt this for production, the seek bar should become a proper role="slider" with aria-valuenow, aria-valuemin, and aria-valuemax plus arrow-key handling so it is operable without a pointer — the visual track is already in place to style over. Performance is careful about the two moving parts: the vinyl spin is a pure CSS animation whose play state is toggled by a class, so it never touches JavaScript per frame, and the seek drag reads clientX and writes two style values without triggering layout in a loop. The one-second interval that advances the clock is the only timer, and it does a single percentage calculation per tick. Swapping the interval for an <audio> element's timeupdate event removes even that, letting the browser drive both the audio and the progress bar from one source.

Reusing it

Point the play/pause and seek at a real <audio> element — bind currentTime, duration, and the timeupdate event instead of the interval — and feed the art and metadata from your track data. Lift it out of the phone frame for a responsive web player, or keep it framed beside a music player mini-bar to present a full listening experience.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA full-screen now-playing view renders with album art and transport controls.
  2. 2
    Press playThe elapsed time starts counting, the progress bar fills, and the vinyl disc begins spinning.
  3. 3
    Pause itThe clock stops, the button returns to a play triangle, and the disc freezes in place.
  4. 4
    Scrub the trackClick anywhere on the seek bar to jump, or drag the knob to scrub to any position.
  5. 5
    Like the songThe heart toggles between outline and a filled pink state.
  6. 6
    Toggle shuffle and repeatEach mode button flips its accent color independently.

Real-world uses

Common Use Cases

Music apps
The now-playing view above a music player bar.
Podcast players
Reuse the seek bar for an audio waveform visualizer.
Media controls
Pair the transport with a volume control.
App mockups
Present it inside a phone mockup.
Lock-screen media
Match it to a mobile lock screen player.
Learning scrub bars
A reference for pointer-driven seek controls.

Got questions?

Frequently Asked Questions

In the demo it is driven by a setInterval that increments a counter each second and loops at the track length. To play real audio, create an audio element, start it on play, and update the bar from its timeupdate event using currentTime and duration instead of the interval. The render function stays the same — only the time source changes.

The disc has a continuous rotation keyframe that is set to animation-play-state: paused by default. Playing adds a .playing class that switches it to running, and pausing removes the class. Because animation-play-state freezes and resumes without resetting, the record picks up exactly where it stopped.

The track listens for a click to seek and uses Pointer Events for dragging: pressing the knob sets a dragging flag, pointermove on the window updates the position so your finger can leave the bar, and pointerup ends the drag. The x offset is converted to a 0-to-1 ratio, clamped so you cannot scrub past either end, and mapped to the current time.

Shuffle and repeat are persistent modes, so they are toggle buttons that keep an on state and accent color. Previous and next are momentary actions that fire once, so they are plain buttons. This mirrors how real players distinguish sticky modes from one-shot skips.

Hold playing, currentTime, and the toggle states in state, and drive them from a real audio ref. Start and stop it in a handler, and update currentTime from the audio timeupdate event in a useEffect (React), watcher (Vue), or ngZone callback (Angular). Bind the fill width and playing class to state rather than mutating the DOM. Tailwind expresses the gradients and controls with utilities.