Paste either snippet into your page — a Custom HTML block on WordPress, Shopify, or Squarespace works too. Both snippets update live as you customize, so what you copy is exactly what you configured.
<!-- Free Audio Player widget by haynes lab — https://hayneslab.dev/tools/widgets/audio-player -->
<script data-hl-src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3" data-hl-title="SoundHelix Song 1" data-hl-artist="SoundHelix">
/*!
* haynes lab — Audio Player widget (free)
* Docs & embed code: https://hayneslab.dev/tools/widgets/audio-player
*/
(function () {
'use strict';
var PREFIX = 'hlw-audio-player';
var STYLE_ID = 'hlw-audio-player-styles';
function findScript() {
return document.currentScript ||
document.querySelector('script[src*="widgets/audio-player.js"]');
}
function injectStyles() {
if (document.getElementById(STYLE_ID)) return;
var css =
'.' + PREFIX + ', .' + PREFIX + ' * { box-sizing: border-box; }' +
'.' + PREFIX + ' { position: relative; max-width: 480px; margin: 0 auto; padding: 18px 20px 26px;' +
' font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;' +
' font-size: 16px; line-height: 1.5; color: #1f2937; background: #ffffff;' +
' border: 1px solid #e5e7eb; border-radius: 12px; }' +
'.' + PREFIX + '__meta { margin-bottom: 14px; min-width: 0; }' +
'.' + PREFIX + '__title { font-weight: 600; font-size: 16px; color: #111827;' +
' overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }' +
'.' + PREFIX + '__artist { font-size: 13px; color: #6b7280; margin-top: 2px;' +
' overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }' +
'.' + PREFIX + '__controls { display: flex; align-items: center; gap: 12px; }' +
'.' + PREFIX + '__play { flex: 0 0 auto; width: 44px; height: 44px; border: 0; border-radius: 50%;' +
' background: var(--hlw-accent); color: #ffffff; cursor: pointer; padding: 0;' +
' display: flex; align-items: center; justify-content: center; }' +
'.' + PREFIX + '__play:hover { filter: brightness(1.1); }' +
'.' + PREFIX + '__play:focus-visible { outline: 2px solid var(--hlw-accent); outline-offset: 2px; }' +
'.' + PREFIX + '__play svg { display: block; }' +
'.' + PREFIX + '__middle { flex: 1 1 auto; min-width: 0; }' +
'.' + PREFIX + '__seek { display: block; width: 100%; height: 6px; margin: 0; cursor: pointer;' +
' accent-color: var(--hlw-accent); }' +
'.' + PREFIX + '__seek:focus-visible { outline: 2px solid var(--hlw-accent); outline-offset: 2px; }' +
'.' + PREFIX + '__times { display: flex; justify-content: space-between; margin-top: 4px;' +
' font-size: 12px; color: #6b7280; font-variant-numeric: tabular-nums; }' +
'.' + PREFIX + '__speed { flex: 0 0 auto; padding: 4px 6px; font-size: 13px; color: #374151;' +
' background: #ffffff; border: 1px solid #e5e7eb; border-radius: 8px; cursor: pointer;' +
' font-family: inherit; }' +
'.' + PREFIX + '__speed:focus-visible { outline: 2px solid var(--hlw-accent); outline-offset: 2px; }' +
'.' + PREFIX + '__credit { position: absolute; right: 10px; bottom: 6px; font-size: 11px;' +
' display: inline-flex; align-items: center; gap: 4px; color: #9ca3af; text-decoration: none; }' +
'.' + PREFIX + '__credit svg { display: block; }' +
'.' + PREFIX + '__credit:hover { color: #6b7280; text-decoration: underline; }';
var style = document.createElement('style');
style.id = STYLE_ID;
style.textContent = css;
document.head.appendChild(style);
}
function el(tag, className, text) {
var node = document.createElement(tag);
if (className) node.className = className;
if (text != null) node.textContent = text;
return node;
}
function logoSvg() {
var svgNS = 'http://www.w3.org/2000/svg';
var svg = document.createElementNS(svgNS, 'svg');
svg.setAttribute('viewBox', '0 0 32 32');
svg.setAttribute('width', '13');
svg.setAttribute('height', '13');
svg.setAttribute('aria-hidden', 'true');
svg.setAttribute('focusable', 'false');
function circle(cx, cy, r, attrs) {
var c = document.createElementNS(svgNS, 'circle');
c.setAttribute('cx', cx);
c.setAttribute('cy', cy);
c.setAttribute('r', r);
for (var key in attrs) c.setAttribute(key, attrs[key]);
svg.appendChild(c);
}
circle(16, 16, 16, { fill: '#0f0a0a' });
circle(16, 16, 12, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1, opacity: 0.3 });
circle(16, 16, 9, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1, opacity: 0.5 });
circle(16, 16, 6, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1.2, opacity: 0.9 });
circle(16, 16, 3, { fill: '#06b6d4' });
circle(24.5, 7.5, 2, { fill: '#f97316' });
circle(7, 16, 1.8, { fill: '#f97316' });
circle(20, 20, 1.5, { fill: '#f97316' });
return svg;
}
function iconSvg(kind) {
var svgNS = 'http://www.w3.org/2000/svg';
var svg = document.createElementNS(svgNS, 'svg');
svg.setAttribute('viewBox', '0 0 24 24');
svg.setAttribute('width', '20');
svg.setAttribute('height', '20');
svg.setAttribute('aria-hidden', 'true');
svg.setAttribute('focusable', 'false');
svg.setAttribute('fill', 'currentColor');
var path = document.createElementNS(svgNS, 'path');
if (kind === 'play') {
path.setAttribute('d', 'M8 5.5v13c0 .8.9 1.3 1.6.9l10-6.5c.6-.4.6-1.4 0-1.8l-10-6.5C8.9 4.2 8 4.7 8 5.5z');
} else {
path.setAttribute('d', 'M7 5h3.5v14H7zM13.5 5H17v14h-3.5z');
}
svg.appendChild(path);
return svg;
}
function formatTime(seconds) {
if (!isFinite(seconds) || seconds < 0) return '0:00';
var total = Math.floor(seconds);
var m = Math.floor(total / 60);
var s = total % 60;
return m + ':' + (s < 10 ? '0' : '') + s;
}
function build(script) {
var src = script.getAttribute('data-hl-src');
if (!src) {
console.warn('[haynes lab audio-player] Missing required data-hl-src attribute (URL of the audio file). Nothing rendered.');
return;
}
var title = script.getAttribute('data-hl-title') || 'Audio track';
var artist = script.getAttribute('data-hl-artist') || '';
var accent = script.getAttribute('data-hl-accent') || '#f97316';
injectStyles();
var root = el('div', PREFIX);
root.setAttribute('role', 'region');
root.setAttribute('aria-label', 'Audio player');
root.style.setProperty('--hlw-accent', accent);
var audio = document.createElement('audio');
audio.preload = 'metadata';
audio.src = src;
root.appendChild(audio);
var meta = el('div', PREFIX + '__meta');
meta.appendChild(el('div', PREFIX + '__title', title));
if (artist) meta.appendChild(el('div', PREFIX + '__artist', artist));
root.appendChild(meta);
var controls = el('div', PREFIX + '__controls');
var playBtn = el('button', PREFIX + '__play');
playBtn.type = 'button';
playBtn.setAttribute('aria-label', 'Play');
playBtn.appendChild(iconSvg('play'));
var middle = el('div', PREFIX + '__middle');
var seek = document.createElement('input');
seek.type = 'range';
seek.className = PREFIX + '__seek';
seek.min = '0';
seek.max = '1000';
seek.value = '0';
seek.step = '1';
seek.setAttribute('aria-label', 'Seek');
var times = el('div', PREFIX + '__times');
var currentEl = el('span', null, '0:00');
var durationEl = el('span', null, '0:00');
times.appendChild(currentEl);
times.appendChild(durationEl);
middle.appendChild(seek);
middle.appendChild(times);
var speed = document.createElement('select');
speed.className = PREFIX + '__speed';
speed.setAttribute('aria-label', 'Playback speed');
var rates = ['0.5', '0.75', '1', '1.25', '1.5', '2'];
rates.forEach(function (r) {
var opt = document.createElement('option');
opt.value = r;
opt.textContent = r + '×';
if (r === '1') opt.selected = true;
speed.appendChild(opt);
});
controls.appendChild(playBtn);
controls.appendChild(middle);
controls.appendChild(speed);
root.appendChild(controls);
var seeking = false;
function setPlayState(playing) {
playBtn.setAttribute('aria-label', playing ? 'Pause' : 'Play');
while (playBtn.firstChild) playBtn.removeChild(playBtn.firstChild);
playBtn.appendChild(iconSvg(playing ? 'pause' : 'play'));
}
playBtn.addEventListener('click', function () {
if (audio.paused) {
var p = audio.play();
if (p && typeof p.catch === 'function') {
p.catch(function () { setPlayState(false); });
}
} else {
audio.pause();
}
});
audio.addEventListener('play', function () { setPlayState(true); });
audio.addEventListener('pause', function () { setPlayState(false); });
audio.addEventListener('ended', function () { setPlayState(false); });
audio.addEventListener('loadedmetadata', function () {
durationEl.textContent = formatTime(audio.duration);
});
audio.addEventListener('timeupdate', function () {
currentEl.textContent = formatTime(audio.currentTime);
if (!seeking && isFinite(audio.duration) && audio.duration > 0) {
seek.value = String(Math.round((audio.currentTime / audio.duration) * 1000));
}
});
seek.addEventListener('input', function () {
seeking = true;
if (isFinite(audio.duration) && audio.duration > 0) {
currentEl.textContent = formatTime((Number(seek.value) / 1000) * audio.duration);
}
});
seek.addEventListener('change', function () {
if (isFinite(audio.duration) && audio.duration > 0) {
audio.currentTime = (Number(seek.value) / 1000) * audio.duration;
}
seeking = false;
});
speed.addEventListener('change', function () {
audio.playbackRate = Number(speed.value);
});
var credit = el('a', PREFIX + '__credit');
credit.appendChild(logoSvg());
credit.appendChild(document.createTextNode('Powered by haynes lab'));
credit.href = 'https://hayneslab.dev/tools/widgets/audio-player';
credit.target = '_blank';
credit.rel = 'noopener';
root.appendChild(credit);
if (script.parentNode && script.parentNode.nodeName !== 'HEAD') {
script.insertAdjacentElement('afterend', root);
} else {
document.body.appendChild(root);
}
}
function init() {
var script = findScript();
if (!script) {
console.warn('[haynes lab audio-player] Could not locate its own script tag. Nothing rendered.');
return;
}
build(script);
}
if (document.body) {
init();
} else {
document.addEventListener('DOMContentLoaded', init);
}
})();
</script>