An Audio Player Component Built with Web Components

This self-contained educational page mirrors the article’s ideas: a custom <ui-audio> element, audio-like controls, playlist navigation, playback speed, events, slots, and CSS variable styling. The sound is generated locally with the Web Audio API, so no external files are needed.

Quick Usage: Drop in a Player

Code:
<ui-audio src="tone:major" label="Major arpeggio" controls></ui-audio>
Result:

Muted Attribute and Volume

Code:
<ui-audio src="tone:pulse" label="Muted pulse" muted controls></ui-audio>
Result:

Playlist Navigation with prevsrc and nextsrc

Code:
<ui-audio
  src="tone:major"
  prevsrc="none"
  nextsrc="tone:minor"
  label="Track 1: Major"
  controls></ui-audio>
Result:

The previous button is disabled because prevsrc="none". The next button switches to another generated tone.

Loop Modes: Sequential, Random, Repeat

Code:
<ui-audio
  src="tone:major"
  nextsrc="tone:minor"
  loop="0"
  label="Loop mode demo"
  controls></ui-audio>
Result:

Playback Speed Control

Code:
<ui-audio src="tone:minor" label="Speed adjustable" controls></ui-audio>
Result:

Use the rate menu in the player to change playback speed.

Hide the Rate Button with rate="none"

Code:
<ui-audio src="tone:bright" label="No speed button" rate="none" controls></ui-audio>
Result:

JS Properties and Methods

Code:
<ui-audio id="methodDemo" src="tone:bright" label="Controlled by JavaScript" controls></ui-audio>

<button id="playDemo">play()</button>
<button id="pauseDemo">pause()</button>
<button id="stopDemo">stop()</button>
<button id="fasterDemo">playbackRate = 1.5</button>

<script>
const audio = document.querySelector('#methodDemo');

document.querySelector('#playDemo').addEventListener('click', () => audio.play());
document.querySelector('#pauseDemo').addEventListener('click', () => audio.pause());
document.querySelector('#stopDemo').addEventListener('click', () => audio.stop());
document.querySelector('#fasterDemo').addEventListener('click', () => {
  audio.playbackRate = 1.5;
});
</script>
Result:

Events: play, pause, seek, end, error

Code:
<ui-audio id="eventDemo" src="tone:pulse" label="Event logger" controls></ui-audio>
<div id="eventLog"></div>

<script>
const eventAudio = document.querySelector('#eventDemo');
const eventLog = document.querySelector('#eventLog');

['play', 'pause', 'seek', 'end', 'stop', 'load', 'error'].forEach((type) => {
  eventAudio.addEventListener(type, (event) => {
    const detail = event.detail ? `: ${event.detail}` : '';
    eventLog.prepend(`${type}${detail}\n`);
  });
});
</script>
Result:

Custom Styles with CSS Variables

Code:
<style>
.skin-blue {
  --ui-audio-background: #e0f2fe;
  --ui-audio-color: #0f172a;
  --ui-audio-accent: #0369a1;
}
</style>

<ui-audio class="skin-blue" src="tone:major" label="CSS variable skin" controls></ui-audio>
Result:

Custom Styles with ::part()

Code:
<style>
.skin-red::part(container) {
  background: #7f1d1d;
  color: #fff;
  border-color: #fecaca;
}
</style>

<ui-audio class="skin-red" src="tone:minor" label="Styled through ::part" controls></ui-audio>
Result:

Slot Placeholder

Code:
<ui-audio src="tone:bright" label="Slot example" controls>
  <span class="slot-credit" slot="custom">by zhangxinxu</span>
</ui-audio>
Result:
by zhangxinxu

Background Audio: No controls Attribute

Code:
<ui-audio id="hiddenAudio" src="tone:pulse" label="Hidden player"></ui-audio>

<button id="hiddenToggle">Toggle hidden player</button>
<span id="hiddenStatus">paused</span>

<script>
const hiddenAudio = document.querySelector('#hiddenAudio');
const hiddenToggle = document.querySelector('#hiddenToggle');
const hiddenStatus = document.querySelector('#hiddenStatus');

hiddenToggle.addEventListener('click', () => {
  if (hiddenAudio.paused) {
    hiddenAudio.play();
    hiddenStatus.textContent = 'playing';
  } else {
    hiddenAudio.pause();
    hiddenStatus.textContent = 'paused';
  }
});
</script>
Result:
paused