<div class="wrapper">
<div class="player">
<div class="equalizer" id="equalizer">
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
<div class="vertical"></div>
</div>
<div class="controls">
<div class="line progress">
<span class="current-time">0:00</span> / <span class="duration">2:50</span>
<input type="range" class="progress-bar" name="progress" value=0 min=0 max=100 step="0.1" disabled />
</div>
<div class="line">
<button class="play-pause" data-playing="false" role="switch" aria-checked="false">
</button>
<span class="description">Anitek - Komorebi</span>
<div class="volume">
<input type="range" name="volume" min="0" max="2" value="1" step="0.01" id="range-volume" />
<input type="checkbox" name="mute" id="mute">
<label for="mute" class="unmute">
<img src="http://upload.wikimedia.org/wikipedia/commons/3/3f/Mute_Icon.svg" alt="Mute_Icon.svg" title="Mute icon">
</label>
<label for="mute" class="mute">
<img src="http://upload.wikimedia.org/wikipedia/commons/2/21/Speaker_Icon.svg" alt="Speaker_Icon.svg" title="Unmute/speaker icon">
</label>
</div>
</div>
</div>
</div>
</div>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
height: 100%;
}
body {
background-color: #b651b8;
color: #3d143d;
font-family: sans-serif;
font-size: 1.5em;
height: 100%;
}
.wrapper {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
padding: 10px;
width: 100%;
height: 100%;
}
input[type=range] {
appearance: none;
background-color: #3d143d;
border-radius: 2px;
outline: none;
flex-grow: 1;
height: 5px;
}
input[type=range]::slider-thumb {
appearance: none;
appearance: none;
width: 15px;
height: 15px;
border: 2px #3d143d solid;
border-radius: 7.5px;
background-color: #b651b8;
}
input[type=range]::range-thumb {
width: 15px;
height: 15px;
border: 2px #3d143d solid;
border-radius: 7.5px;
background-color: #b651b8;
}
.player {
padding: 10px;
}
.player .equalizer {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
height: 220px;
}
.player .equalizer .vertical {
height: 10px;
width: 10px;
margin: 2px;
background-color: #3d143d;
border-radius: 5px;
transition: all 0.01s;
transition-timing-function: ease-in-out;
}
.player .controls .line {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
justify-content: space-between;
margin: 10px 0;
}
.player .controls .progress {
font-size: 0.5em;
font-weight: bold;
}
.player .controls .progress .duration {
margin: 0 10px 0 0;
}
.player .controls .play-pause {
box-sizing: border-box;
transition: all 0.1s;
transition-timing-function: ease-in-out;
will-change: border-width;
cursor: pointer;
height: 30px;
width: 30px;
border-color: #b651b8 #b651b8 #b651b8 #3d143d;
background-color: #b651b8;
}
.player .controls .play-pause[aria-checked=false] {
border-style: solid;
border-width: 15px 0 15px 30px;
}
.player .controls .play-pause[aria-checked=true] {
border-style: double;
border-width: 0 0 0 30px;
}
.player .controls .description {
margin: 0 10px;
}
.player .controls .volume {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
}
.player .controls .volume input[type=range] {
width: 0px;
margin: 0 10px 0 0;
transition: all 0.1s;
transition-timing-function: ease-in-out;
cursor: pointer;
}
.player .controls .volume:hover input[type=range] {
display: block;
width: 50px;
}
.player .controls .volume input[type=range]::slider-thumb {
display: none;
}
.player .controls .volume input[type=range]::range-thumb {
display: none;
}
.player .controls .volume:hover input[type=range]::slider-thumb {
display: inherit;
}
.player .controls .volume:hover input[type=range]::range-thumb {
display: inherit;
}
.player .controls .volume input[type=checkbox] {
display: none;
}
.player .controls .volume input[type=checkbox]:checked ~ .unmute img, .player .controls .volume input[type=checkbox]:not(:checked) ~ .mute img {
display: block;
}
.player .controls .volume img {
display: none;
cursor: pointer;
height: 30px;
width: 30px;
filter: invert(11%) sepia(10%) saturate(7132%) hue-rotate(265deg) brightness(98%) contrast(99%);
}
function updateEqualizer(analyser, buffer, columns) {
analyser.getByteFrequencyData(buffer);
for (let i = 0; i < columns.length; i += 1) {
let column = columns[i];
const height = (buffer[i] / 255) * 190 + 10;
column.setAttribute("style", `height: ${height}px`);
}
}
function toMinutesAndSeconds(time) {
const minutes = Math.floor(time / 60);
const seconds = Math.floor(time % 60);
if (seconds < 10) {
return `${minutes}:0${seconds}`;
}
return `${minutes}:${seconds}`;
}
function updateProgressBar(track, progressBar, timeDisplay, durationDisplay) {
const currentTime = track.mediaElement.currentTime.toFixed(1);
const duration = track.mediaElement.duration;
timeDisplay.innerHTML = toMinutesAndSeconds(
currentTime > duration ? duration : currentTime
);
progressBar.value = currentTime > duration ? duration : currentTime;
}
console.clear();
// instigate our audio context
let audioElement = new Audio();
audioElement.crossOrigin = "anonymous";
audioElement.src = "https://assets.codepen.io/4358584/Anitek_-_Komorebi.mp3";
audioElement.addEventListener(
"loadedmetadata",
(event) => {
// for cross browser
const AudioContext = window.AudioContext || window.webkitAudioContext;
const audioCtx = new AudioContext();
// load some sound
const track = audioCtx.createMediaElementSource(audioElement);
const playButton = document.querySelector("button");
// play pause audio
playButton.addEventListener(
"click",
function () {
// check if context is in suspended state (autoplay policy)
if (audioCtx.state === "suspended") {
audioCtx.resume();
}
if (this.dataset.playing === "false") {
audioElement.play();
this.dataset.playing = "true";
// if track is playing pause it
} else if (this.dataset.playing === "true") {
audioElement.pause();
this.dataset.playing = "false";
}
let state = this.getAttribute("aria-checked") === "true" ? true : false;
this.setAttribute("aria-checked", state ? "false" : "true");
},
false
);
// if track ends
audioElement.addEventListener(
"ended",
() => {
playButton.dataset.playing = "false";
playButton.setAttribute("aria-checked", "false");
},
false
);
// initialize gain
let gainNode = audioCtx.createGain();
// initialize analyser
let analyser = audioCtx.createAnalyser();
analyser.fftSize = 64; //32 bins
const frequencyBinsCount = analyser.frequencyBinCount;
let buffer = new Uint8Array(frequencyBinsCount);
track.connect(gainNode).connect(analyser).connect(audioCtx.destination);
// equalizer animation
const equalizer = document.getElementById("equalizer");
// 32 bins -> 32 columns
const columns = document.getElementsByClassName("vertical");
setInterval(updateEqualizer, 16, analyser, buffer, columns);
const currentTimeDisplay = document.querySelector(".current-time");
const progressBar = document.querySelector(".progress-bar");
const durationDisplay = document.querySelector(".duration");
durationDisplay.innerHTML = toMinutesAndSeconds(
track.mediaElement.duration
);
progressBar.max = track.mediaElement.duration;
setInterval(
updateProgressBar,
100,
track,
progressBar,
currentTimeDisplay,
durationDisplay
);
const volumeControl = document.querySelector("#range-volume");
volumeControl.addEventListener(
"input",
function () {
gainNode.gain.value = this.value;
},
false
);
// mute button
const mute_button = document.getElementById("mute");
mute_button.addEventListener(
"change",
(event) => {
if (event.target.checked) {
audioElement.muted = true;
} else {
audioElement.muted = false;
}
},
false
);
},
false
);
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.