The top bar in the visualizer shows the frequencies. The middle bar is a simple beats detection bar. The background color changes if significant beats are detected. The bottom bar is a frequency volume bar.
A Pen by Alex Asciencio on CodePen.
<div class="w3-container"> | |
<div class="w3-container w3-third w3-center w3-padding w3-margin-top"> | |
<audio controls="controls" id="audio"></audio> | |
</div> | |
<div class="w3-container w3-third w3-center w3-padding w3-margin-top"> | |
<input type="file" id="inputFile" onchange="checkAndPlayFiles(this.files)" /> | |
</div> | |
<div class="w3-btn w3-red w3-hover-purple w3-padding w3-margin-top w3-container w3-round-large" id="openControls"> | |
Show Controls | |
</div> | |
</div> | |
<div class="w3-modal" id="controlsModal"> | |
<div class="w3-modal-content w3-container w3-padding-jumbo"> | |
<span id="closeBtn" class="w3-button w3-display-topright">×</span> | |
<div> | |
<div class="w3-container w3-text-shadow w3-large"> | |
<div class="w3-third w3-center"> | |
Gain: | |
</div> | |
<div class="w3-third"> | |
<input type="range" min="0" max="1" step="0.01" value="0.9" id="gainSlider" /> | |
</div> | |
<div class="w3-third" id="gainDisplay">0.9</div> | |
</div> | |
<div class="w3-container w3-text-shadow w3-large"> | |
<div class="w3-third w3-center"> | |
Balance: | |
</div> | |
<div class="w3-third"> | |
<input type="range" min="-1" max="1" step="0.01" value="0" id="balanceSlider" /> | |
</div> | |
<div class="w3-third" id="balanceDisplay">0</div> | |
</div> | |
</div> | |
<br> | |
<div class="w3-center w3-text-shadow w3-large w3-margin-top w3-padding-top"> | |
<div> | |
<u>Equalizer:</u> | |
</div> | |
<br> | |
<div> | |
<div class="w3-third">60Hz</div> | |
<input class="w3-third" type="range" min="-30" max="30" value="0" step="1" oninput="changeFilterGain(this.value, 0)" /> | |
<div class="w3-third" id="filter0">0dB</div> | |
</div> | |
<div> | |
<div class="w3-third">100Hz</div> | |
<input class="w3-third" type="range" min="-30" max="30" value="0" step="1" oninput="changeFilterGain(this.value, 1)" /> | |
<div class="w3-third" id="filter1">0dB</div> | |
</div> | |
<div> | |
<div class="w3-third">350Hz</div> | |
<input class="w3-third" type="range" min="-30" max="30" value="0" step="1" oninput="changeFilterGain(this.value, 2)" /> | |
<div class="w3-third" id="filter2">0dB</div> | |
</div> | |
<div> | |
<div class="w3-third">1000Hz</div> | |
<input class="w3-third" type="range" min="-30" max="30" value="0" step="1" oninput="changeFilterGain(this.value, 3)" /> | |
<div class="w3-third" id="filter3">0dB</div> | |
</div> | |
<div> | |
<div class="w3-third">3500Hz</div> | |
<input class="w3-third" type="range" min="-30" max="30" value="0" step="1" oninput="changeFilterGain(this.value, 4)" /> | |
<div class="w3-third" id="filter4">0dB</div> | |
</div> | |
<div> | |
<div class="w3-third">10000Hz</div> | |
<input class="w3-third" type="range" min="-30" max="30" value="0" step="1" oninput="changeFilterGain(this.value, 5)" /> | |
<div class="w3-third" id="filter5">0dB</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="w3-padding-top w3-center" id="canvasParent"> | |
</div> |
The top bar in the visualizer shows the frequencies. The middle bar is a simple beats detection bar. The background color changes if significant beats are detected. The bottom bar is a frequency volume bar.
A Pen by Alex Asciencio on CodePen.
var URL = window.URL || window.webkitURL; | |
var audioContext = new AudioContext(); | |
var audio = document.getElementById("audio"); | |
var fileSelected = false; | |
var setupCompleted = false; | |
var controlsModal = document.getElementById("controlsModal"); | |
var closeButton = document.getElementById("closeBtn"); | |
var openControls = document.getElementById("openControls"); | |
openControls.addEventListener("click", function() { | |
controlsModal.style.display = "block"; | |
}); | |
closeButton.addEventListener("click", function() { | |
controlsModal.style.display = "none"; | |
}); | |
var gainSlider = document.getElementById("gainSlider"); | |
var balanceSlider = document.getElementById("balanceSlider"); | |
var gainDisplay = document.getElementById("gainDisplay"); | |
var balanceDisplay = document.getElementById("balanceDisplay"); | |
var inputFile = document.getElementById("inputFile"); | |
var i; | |
var newColor; | |
gainSlider.oninput = function() { | |
gainNode.gain.value = parseFloat(gainSlider.value); | |
gainDisplay.innerText = parseFloat(gainSlider.value); | |
}; | |
balanceSlider.oninput = function() { | |
balance.pan.value = parseFloat(balanceSlider.value); | |
balanceDisplay.innerText = parseFloat(balanceSlider.value); | |
}; | |
var filters = []; | |
[60, 100, 350, 1000, 3500, 10000].forEach(function(value, index) { | |
var equalizer = audioContext.createBiquadFilter(); | |
equalizer.frequency.value = value; | |
equalizer.type = "peaking"; | |
equalizer.gain.value = 0; | |
filters.push(equalizer); | |
}); | |
var gainNode = null; | |
var balance; | |
var mediaElementSrc; | |
var analyzer; | |
var freqAnalyzer; | |
var bufferLength; | |
var dataArray; | |
var freqDataArray; | |
var freqBufferLength; | |
function checkAndPlayFiles(files) { | |
var file = files[0]; | |
console.log(file); | |
if (audio.canPlayType(file.type)) { | |
var blobUrl = URL.createObjectURL(file); | |
audio.src = blobUrl; | |
} else window.alert("Unable To Play File"); | |
fileSelected = true; | |
setupCompleted = false; | |
setupEveryThing(); | |
} | |
function changeFilterGain(value, index) { | |
var elementValue = parseFloat(value); | |
filters[index].gain.value = elementValue; | |
var outputElement = document.getElementById("filter" + index); | |
outputElement.innerText = elementValue + "dB"; | |
} | |
var isMobile; | |
var buildAudioGraph = function() { | |
if (gainNode) return; | |
gainNode = audioContext.createGain(); | |
balance = audioContext.createStereoPanner(); | |
mediaElementSrc = audioContext.createMediaElementSource(audio); | |
analyzer = audioContext.createAnalyser(); | |
analyzer.smoothingTimeConstant = 0.3; | |
isMobile = false; | |
if ( | |
/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test( | |
navigator.userAgent | |
) || | |
/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test( | |
navigator.userAgent.substr(0, 4) | |
) | |
) | |
isMobile = true; | |
if (isMobile) analyzer.fftSize = 64; | |
else analyzer.fftSize = 512; | |
bufferLength = analyzer.frequencyBinCount; | |
dataArray = new Uint8Array(bufferLength); | |
freqAnalyzer = audioContext.createAnalyser(); | |
freqAnalyzer.fftSize = 64; | |
freqBufferLength = freqAnalyzer.frequencyBinCount; | |
freqDataArray = new Uint8Array(freqBufferLength); | |
mediaElementSrc.connect(gainNode); | |
gainNode.connect(balance); | |
balance.connect(filters[0]); | |
for (var i = 0; i < filters.length - 1; i++) | |
filters[i].connect(filters[i + 1]); | |
filters[filters.length - 1].connect(analyzer); | |
filters[filters.length - 1].connect(freqAnalyzer); | |
analyzer.connect(audioContext.destination); | |
freqAnalyzer.connect(audioContext.destination); | |
}; | |
// Beat Bar Class | |
function BeatBar(y, barWidth, barHeight, speed, maxHistory) { | |
this.x = 0; | |
this.y = y; | |
this.width = barWidth; | |
this.height = barHeight; | |
this.speed = speed; | |
this.maxHistory = maxHistory; | |
this.history = []; | |
this.constantProduct = 255 / maxHistory; | |
this.color = color(255); | |
this.checkPosition = function(currentWidth) { | |
if (this.x <= currentWidth) this.x = currentWidth; | |
}; | |
this.displayTrail = function() { | |
noStroke(); | |
for (var i = 0; i < this.history.length; i++) { | |
fill( | |
this.color.levels[0], | |
this.color.levels[1], | |
this.color.levels[2], | |
i * this.constantProduct | |
); | |
this.history[i].x += random(-2, 2); | |
this.history[i].y += random(-2, 2); | |
/* rect(this.history[i].x, this.history[i].y, this.width, | |
map(i, 0, this.history.length, this.height / 2, this.height)); */ | |
ellipse( | |
this.history[i].x, | |
this.history[i].y, | |
map(i, 0, this.history.length, this.height / 2, this.height) | |
); | |
} | |
}; | |
this.changeColor = function() { | |
this.color = color(random(100, 256), random(100, 256), random(100, 256)); | |
}; | |
this.display = function() { | |
if (this.history.length >= this.maxHistory) this.history.splice(0, 1); | |
this.history.push({ | |
x: this.x + this.width / 2, | |
y: this.y + this.height / 2 | |
}); | |
noStroke(); | |
fill(this.color); | |
this.x -= this.speed; | |
rect(this.x, this.y, this.width, this.height); | |
}; | |
} | |
// P5 Part Starts Here | |
var barWidth; | |
var freqBarWidth; | |
var currentFrameCount = 0; | |
var currentValue; | |
var prevValue; | |
var beatBarWidth = 0; | |
var sensitivity = 0.3; | |
var backgroundColor = 0; | |
var trailBeatBar; | |
var beatBarColor; | |
var maxValue; | |
var criticalPoint; | |
function setupEveryThing() { | |
if (!fileSelected) return; | |
buildAudioGraph(); | |
var canvas = createCanvas(window.innerWidth - 20, window.innerHeight - 120); | |
canvas.parent("canvasParent"); | |
barWidth = width / bufferLength; | |
freqBarWidth = width / freqBufferLength; | |
trailBeatBar = new BeatBar(height * 0.45, 10, height * 0.07, 5, 51); | |
beatBarColor = color(0, 204, 255); | |
maxValue = analyzer.fftSize * 255 * 255 * 0.3; | |
criticalPoint = analyzer.fftSize * 255 * 255 * 0.06; | |
setupCompleted = true; | |
console.log("Setup Called and Audio Graph Created"); | |
} | |
function draw() { | |
if (!fileSelected || !setupCompleted) return; | |
background(backgroundColor); | |
analyzer.getByteTimeDomainData(dataArray); | |
var x = barWidth / 2; | |
var barHeight; | |
var fromColor = color(255, 0, 0); | |
var toColor = color(0, 0, 255); | |
noStroke(); | |
rectMode(CENTER); | |
for (i = 0; i < bufferLength; i++) { | |
newColor = lerpColor(fromColor, toColor, i / bufferLength); | |
fill(newColor); | |
barHeight = map(dataArray[i], 0, 255, 0, height * 0.35); | |
rect(x, height * 0.1785, barWidth, barHeight); | |
x += barWidth; | |
} | |
rectMode(CORNER); | |
analyzer.getByteFrequencyData(dataArray); | |
currentValue = getSumSquaredValue(dataArray); | |
if (currentValue > sensitivity * prevValue) { | |
if (currentValue > maxValue) currentValue = maxValue; | |
beatBarWidth = map(currentValue, 0, maxValue, 0, width); | |
} | |
fill(beatBarColor); | |
rect(0, height * 0.45, beatBarWidth, height * 0.07); | |
if (currentValue - prevValue > criticalPoint) { | |
backgroundColor = color(random(0, 101), random(0, 101), random(0, 101)); | |
trailBeatBar.changeColor(); | |
beatBarColor = color(random(150, 256), random(150, 256), random(150, 256)); | |
} | |
prevValue = currentValue; | |
trailBeatBar.checkPosition(beatBarWidth); | |
trailBeatBar.display(); | |
if (!isMobile) trailBeatBar.displayTrail(); | |
noStroke(); | |
x = 0; | |
fromColor = color(235, 106, 20); | |
toColor = color(26, 255, 26); | |
freqAnalyzer.getByteFrequencyData(freqDataArray); | |
for (i = 0; i < freqBufferLength; i++) { | |
newColor = lerpColor(fromColor, toColor, i / freqBufferLength); | |
fill(newColor); | |
barHeight = map(freqDataArray[i], 0, 255, 0, height * 0.35); | |
rect(x, height - barHeight, freqBarWidth, barHeight); | |
x += freqBarWidth; | |
} | |
fill(255); | |
rectMode(CORNER); | |
textSize(32); | |
currentFrameCount = parseInt(frameRate()); | |
text(currentFrameCount, width - 50, 10, width - 10, 100); | |
} | |
function getAverage(dataArray) { | |
var sum = 0; | |
for (var i = 0; i < dataArray.length; i++) | |
sum += dataArray[i]; | |
sum /= dataArray.length; | |
return sum; | |
} | |
function getSumSquaredValue(dataArray) { | |
var sum = 0; | |
for (var i = 0; i < dataArray.length; i++) | |
sum += Math.pow(dataArray[i], 2); | |
return sum; | |
} | |
function radialGradient(x, y, radius) { | |
var fromColor = color(255, 0, 0); | |
var toColor = color(0, 0, 255); | |
for (var r = radius; r > 0; --r) { | |
var newColor = lerpColor(fromColor, toColor, r / circleRadius); | |
fill(newColor); | |
ellipse(x, y, r); | |
} | |
} | |
function linearGradient(x, y, w, h, c1, c2, axis) { | |
noFill(); | |
var inter; | |
var c; | |
if (axis === "Y_AXIS") { | |
// Top to bottom gradient | |
for (i = y; i <= y + h; i++) { | |
inter = map(i, y, y + h, 0, 1); | |
c = lerpColor(c1, c2, inter); | |
stroke(c); | |
line(x, i, x + w, i); | |
} | |
} else if (axis === "X_AXIS") { | |
// Left to right gradient | |
for (i = x; i <= x + w; i++) { | |
inter = map(i, x, x + w, 0, 1); | |
c = lerpColor(c1, c2, inter); | |
stroke(c); | |
line(i, y, i, y + h); | |
} | |
} | |
} |
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.7/addons/p5.dom.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.7/p5.min.js"></script> |
<link href="https://www.w3schools.com/w3css/3/w3.css" rel="stylesheet" /> |