<canvas></canvas>
<button id="toggle" style="display: block" disabled="true">
<i class="fa fa-spinner fa-pulse fa-fw"></i>
</button>
body {
overflow: hidden;
margin: 0px;
padding: 0px;
width:100%;
height:100vh;
background-color: #000;
}
.transculent {
opacity: 0;
}
button{
position: absolute;
overflow: hidden;
font-size:7rem;
top: 0px;
left: 0px;
width:100%;
height: 100%;
background-color: rgba( 255, 255, 255, 0);
color: rgba(255, 255, 255, 0.5);
border:0px;
}
let context = new (window.AudioContext || window.webkitAudioContext)();
if (!context.createGain)
 context.createGain = context.createGainNode;
if (!context.createDelay)
 context.createDelay = context.createDelayNode;
if (!context.createScriptProcessor)
 context.createScriptProcessor = context.createJavaScriptNode;
window.requestAnimFrame = (function () {
 return window.requestAnimationFrame ||
 window.webkitRequestAnimationFrame ||
 window.mozRequestAnimationFrame ||
 window.oRequestAnimationFrame ||
 window.msRequestAnimationFrame ||
 function (callback) {
 window.setTimeout(callback, 1000 / 60);
 };
})();
function playSound(buffer, time) {
 let source = context.createBufferSource();
 source.buffer = buffer;
 source.connect(context.destination);
 source[source.start ? 'start' : 'noteOn'](time);
}
function loadSounds(obj, soundMap, callback) {
 const names = [];
 const paths = [];
 for (const name in soundMap) {
 const path = soundMap[name];
 names.push(name);
 paths.push(path);
 }
 const bufferLoader = new BufferLoader(context, paths, function (bufferList) {
 for (let i = 0; i < bufferList.length; i++) {
 const buffer = bufferList[i];
 const name = names[i];
 obj[name] = buffer;
 }
 if (callback) {
 callback();
 }
 });
 bufferLoader.load();
}
function BufferLoader(context, urlList, callback) {
 this.context = context;
 this.urlList = urlList;
 this.onload = callback;
 this.bufferList = new Array();
 this.loadCount = 0;
}
const song = "https://res.cloudinary.com/dlszhsahq/video/upload/v1696907055/pop/16_Roby_Benvenuto_Gringo_iu0if7.mp3";
BufferLoader.prototype.loadBuffer = function (url, index) {
 const request = new XMLHttpRequest();
 request.open('GET', song, true);
 request.responseType = "arraybuffer";
 const loader = this;
 request.onload = function () {
 loader.context.decodeAudioData(
 request.response,
 function (buffer) {
 if (!buffer) {
 alert('error decoding file data');
 return;
 }
 loader.bufferList[index] = buffer;
 if (++loader.loadCount == loader.urlList.length)
 loader.onload(loader.bufferList);
 },
 function (error) {
 console.error('decodeAudioData error', error);
 }
 );
 }
 request.onerror = function () {
 alert('BufferLoader: XHR error');
 }
 request.send();
};
BufferLoader.prototype.load = function () {
 for (let i = 0; i < this.urlList.length; ++i)
 this.loadBuffer(this.urlList[i], i);
};
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const source = audioContext.createBufferSource();
const request = new XMLHttpRequest();
request.open('GET', song, true);
request.responseType = 'arraybuffer';
request.onload = function () {
 audioContext.decodeAudioData(request.response, function (buffer) {
 source.buffer = buffer;
 source.loop = true;
 source.start(0);
 }, function (e) {
 console.log('Audio error! ', e);
 });
}
request.send();
const WIDTH = window.innerWidth; // 현재 창의 너비를 WIDTH로 설정
const HEIGHT = window.innerHeight; // 현재 창의 높이를 HEIGHT로 설정
const SMOOTHING = 0.8;
const FFT_SIZE = 2048;
function VisualizerSample() {
 this.analyser = context.createAnalyser();
 this.analyser.connect(context.destination);
 this.analyser.minDecibels = -240;
 this.analyser.maxDecibels = 0;
 loadSounds(this, {
 buffer: source.buffer
 }, onLoaded);
 this.freqs = new Uint8Array(this.analyser.frequencyBinCount);
 this.times = new Uint8Array(this.analyser.frequencyBinCount);
 function onLoaded() {
 const button = document.querySelector('button');
 button.removeAttribute('disabled');
 button.innerHTML = '<i class="fa fa-play" aria-hidden="true"></i>';
 };
 this.isPlaying = false;
 this.startTime = 0;
 this.startOffset = 0;
}
VisualizerSample.prototype.togglePlayback = function () {
 if (this.isPlaying) {
 this.source[this.source.stop ? 'stop' : 'noteOff'](0);
 this.startOffset += context.currentTime - this.startTime;
 console.log('paused at', this.startOffset);
 } else {
 this.startTime = context.currentTime;
 console.log('started at', this.startOffset);
 this.source = context.createBufferSource();
 this.source.connect(this.analyser);
 this.source.buffer = this.buffer;
 this.source.loop = true;
 this.source[this.source.start ? 'start' : 'noteOn'](0, this.startOffset % this.buffer.duration);
 requestAnimFrame(this.draw.bind(this));
 }
 this.isPlaying = !this.isPlaying;
}
VisualizerSample.prototype.draw = function () {
 this.analyser.smoothingTimeConstant = SMOOTHING;
 this.analyser.fftSize = FFT_SIZE;
 this.analyser.getByteFrequencyData(this.freqs);
 this.analyser.getByteTimeDomainData(this.times);
 const width = Math.floor(1 / this.freqs.length, 10);
 const canvas = document.querySelector('canvas');
 const drawContext = canvas.getContext('2d');
 canvas.width = WIDTH;
 canvas.height = HEIGHT;
 for (let i = 0; i < this.analyser.frequencyBinCount; i++) {
 const value = this.freqs[i];
 const percent = value / 256;
 const height = HEIGHT * percent;
 const offset = HEIGHT - height - 1;
 const barWidth = WIDTH / this.analyser.frequencyBinCount;
 const hue = i / this.analyser.frequencyBinCount * 360;
 drawContext.fillStyle = 'hsl(' + hue + ', 100%, 50%)';
 drawContext.fillRect(i * barWidth, offset, barWidth, height);
 }

 for (let i = 0; i < this.analyser.frequencyBinCount; i++) {
 const value = this.times[i];
 const percent = value / 256;
 const height = HEIGHT * percent * 2;
 const offset = HEIGHT - height / 2 - 1;
 const barWidth = WIDTH / this.analyser.frequencyBinCount;
 drawContext.fillStyle = 'white';
 drawContext.fillRect(i * barWidth, offset, 1, 1);
 }
 if (this.isPlaying) {
 requestAnimFrame(this.draw.bind(this));
 }
}

const sample = new VisualizerSample();
document.querySelector('button').addEventListener('click', function () {
 sample.togglePlayback();
 console.log(this.className);
 if (this.className === '') {
 this.className += 'transculent';
 } else {
 this.className = '';
 }
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.