Skip to content

Instantly share code, notes, and snippets.

@YamimakiReru
Last active February 9, 2026 05:20
Show Gist options
  • Select an option

  • Save YamimakiReru/af7439cd32a4de77709b18d004fbd34e to your computer and use it in GitHub Desktop.

Select an option

Save YamimakiReru/af7439cd32a4de77709b18d004fbd34e to your computer and use it in GitHub Desktop.
Enhance WordPress Audio Album Plugin: Sequential Track Playback and Volume Synchronization
/**
* Enhance WordPress Audio Album Plugin: Sequential Track Playback and Volume Synchronization
*
* Add the following features to the [Audio Album]( https://ja.wordpress.org/plugins/audio-album/ ) plugin.
* - Start playing the next audio source automatically when the previous one ends.
* - Synchronize volumes between audio elements when any one of them changes.
*
* To set up this script, upload it to your site and load it as a JavaScript file AFTER the Audio Album JS files.
* Insert a <script> tag or use wp_enqueue_script() in functions.php.
* wp_enqueue_script( 'audioalbum-r', get_template_directory_uri() . '/audioalbum-r.js', ['audioalbum'], null, true );
*
* Works in my site with Audio Album v1.5.1.
*
* Live demo
* https://symphonic.lol/en/music/audioalbum-r-js-live-demo
*
* SPDX-FileCopyrightText: 2026 Reru YAMIMAKI
* SPDX-License-Identifier: MIT-0
*/
'use strict';
(() => {
class RAudioAlbum {
/** @param {HTMLElement} album */
constructor(album) {
this.album = album;
}
/** @type {boolean} */
isSyncing = false;
/** @type {HTMLAudioElement[]} */
audios = [];
init() {
this.audios = Array.from(this.album.querySelectorAll('audio'));
for (const a of this.audios) {
a.addEventListener('ended', this.onAudioEnded.bind(this));
a.addEventListener('volumechange', this.onVolumeChange.bind(this));
}
}
/**
* Start playing the next audio source when the previous one ends.
* @param {Event} ev
*/
onAudioEnded(ev) {
/** @type {HTMLAudioElement} */
const a = ev.target;
const nextIndex = (1 + this.audios.findIndex(a2 => a2 == a)) % this.audios.length;
// An exception occurs unless the previous audio has completely stopped.
setTimeout(() => {
this.audios[nextIndex].play();
}, 100);
}
/**
* Synchronize volumes between audio elements when any one of them changes.
* @param {Event} ev
*/
onVolumeChange(ev) {
if (this.isSyncing) {
return;
}
this.isSyncing = true;
try {
/** @type {HTMLAudioElement} */
const a = ev.target;
for (const a2 of this.audios) {
if (a == a2) {
continue;
}
a2.volume = a.volume;
a2.muted = a.muted;
}
} finally {
// Release the lock after event propagation ends.
Promise.resolve().then(() => {
this.isSyncing = false;
});
}
}
}
// Audio Album v1.5.1 does not create container elements for each album, including their <audio> elements.
// -> Consider containers as outer elements whose direct children are .track elements.
const audioAlbums = document.querySelectorAll('*:has(>.track)');
for (const album of audioAlbums) {
new RAudioAlbum(album).init();
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment