Skip to content

Instantly share code, notes, and snippets.

@AsciencioAlex
Created June 24, 2022 07:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AsciencioAlex/9940c47058ac19e2444f8e2d5a8f4426 to your computer and use it in GitHub Desktop.
Save AsciencioAlex/9940c47058ac19e2444f8e2d5a8f4426 to your computer and use it in GitHub Desktop.
Music Visualizer Using WebAudio And P5.JS
<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">&times;</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>

Music Visualizer Using WebAudio And P5.JS

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.

License.

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" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment