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" /> |