Tutorials & Guides

Building a Full-Featured Web Music Player: Dev Guide

2026-08-30 👁 26 views 3
Building a Full-Featured Web Music Player: Dev Guide

A step-by-step tutorial covering audio playback, LRC lyric sync, drag-and-drop playlists, floating lyrics, responsive design, and i18n for a complete web music player.

1. Project Overview

This tutorial walks through building a complete web-based music player with 10 original AI-generated songs. The player supports playback controls, synced lyrics, playlists, favorites, and a floating desktop lyrics window — all in pure HTML/CSS/JavaScript with no frameworks.

Core Features Implemented

  • Play / Pause / Previous / Next controls
  • Three play modes: sequential, shuffle, repeat-one
  • LRC-format synchronized lyrics with 1-line / full view toggle
  • Drag-and-drop playlist reordering
  • Favorites and play-count tracking via Cloudflare KV
  • Floating desktop lyrics window with transparency, color, and size controls
  • Progress bar drag-to-seek (mouse + touch)
  • Fully responsive (desktop / tablet / mobile)
  • Bilingual (English / Chinese) with dynamic i18n

2. Project Structure

aigomoon/
├── music.html              # Main player page
├── css/style.css           # All styles (responsive breakpoints)
├── js/
│   ├── main.js             # Shared i18n, header/footer loader
│   └── music.js            # Player logic (~900 lines)
├── assets/
│   ├── music/              # 10 MP3 files
│   ├── lrc/                # 10 LRC lyric files
│   └── images/music-covers/ # 10 album covers
└── functions/
    ├── api/stats.js        # GET play/fav counts
    └── api/click.js        # POST increment plays

3. Core Player Architecture

The player uses a single global Audio object and a play-queue array. Key state variables:

Advertisement
var music_playQueue = [];    // Song IDs in play order
var music_currentIndex = 0;  // Current position in queue
var music_userPlaylist = []; // User's saved playlist (localStorage)
var music_favorites = [];    // Favorite song IDs (localStorage)
var music_lrcLines = [];     // Parsed LRC [{time, text}]

Important design decision: The play queue always equals the user playlist. When a user clicks any song, it's auto-appended to both the queue and the saved playlist. This ensures Next/Previous only navigate within the user's curated list.

4. LRC Lyrics Synchronization

Parsing LRC files is straightforward with regex:

fetch(url).then(r => r.text()).then(text => {
  var lines = text.split('\n');
  for (var i = 0; i < lines.length; i++) {
    var m = lines[i].match(/\[(\d{2}):(\d{2})\.(\d{2})\](.*)/);
    if (m) {
      music_lrcLines.push({
        time: parseInt(m[1])*60 + parseInt(m[2]) + parseInt(m[3])/100,
        text: m[4].trim()
      });
    }
  }
});

On each timeupdate event, find the current line by scanning backward from the end:

audio.addEventListener('timeupdate', function() {
  for (var i = music_lrcLines.length - 1; i >= 0; i--) {
    if (this.currentTime >= music_lrcLines[i].time) {
      // Highlight this line, scroll into view
      break;
    }
  }
});

5. Drag-and-Drop Playlist Reordering

Native HTML5 Drag & Drop API handles reordering. Each row gets draggable="true" in playlist mode:

row.addEventListener('drop', function(e) {
  e.preventDefault();
  var srcId = dragSrc.getAttribute('data-id');
  var targetId = row.getAttribute('data-id');
  // Remove source, insert before target
  music_userPlaylist.splice(srcIdx, 1);
  music_userPlaylist.splice(newTgtIdx, 0, srcId);
  localStorage.setItem('music_playlist', JSON.stringify(music_userPlaylist));
  music_renderSongList();
});

Gotcha: A justDragged flag prevents the post-drag click event from triggering song playback.

6. Floating Desktop Lyrics Window

A draggable overlay window shows 1-2 lines of lyrics. Features include:

  • Drag to move (mouse + touch)
  • Pin position toggle
  • 6 color options for current lyric
  • Font size adjustment (A+ / A-)
  • Transparent background mode with auto-hiding controls (3s timeout, reappear on hover/touch)

Transparent mode CSS uses pointer-events: none when controls are hidden, so clicks pass through to underlying content.

7. Responsive Design Lessons

The trickiest issue was column overflow in the song list. On narrow desktop windows (~1080px), the left list panel was only ~430px wide but the grid had 7 fixed columns totaling 420px — leaving the title column at near-zero width, so song names disappeared while genres remained visible.

Solution: Raise the single-column breakpoint from 900px to 1200px. Below 1200px, switch to stacked layout (list on top, player below) and hide the Artist/Plays columns to give the title column room.

8. i18n Approach

A shared I18N dictionary in main.js holds all UI strings. Elements use data-i18n="key" attributes; applyLang() updates textContent on language switch. Dynamic strings (toasts, loading messages) call music_t('key') at runtime. The music page adds 30+ keys for tabs, buttons, headers, and tooltips.

9. Final Checklist

FeatureImplementation
Audio playbackNative Audio API, preload none
Lyrics syncLRC regex parse + timeupdate scan
Playlist orderDrag & drop, localStorage persist
Play countsCloudflare KV via Pages Functions
Floating lyricsFixed overlay, transparent mode
Responsive1200px breakpoint, column hiding
i18ndata-i18n attributes + runtime t()

With these patterns, you can build a production-quality music player in under 1000 lines of vanilla JavaScript — no build step, no frameworks, just deploy to any static host.