<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/84/three.min.js"></script> <script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/controls/OrbitControls.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.3/dat.gui.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/simplex-noise/2.3.0/simplex-noise.min.js"></script> <script type="module" src="https://threejs.org/build/three.module.js"></script> <script type="module"> var noise = new SimplexNoise(); var dataArray; var context; function vizInit() { var audio = document.querySelector("audio"); var canvas = document.getElementById("audioVisualizer"); context = new AudioContext(); var src = context.createMediaElementSource(audio); var analyser = context.createAnalyser(); src.connect(analyser); analyser.connect(context.destination); analyser.fftSize = 512; var bufferLength = analyser.frequencyBinCount; dataArray = new Uint8Array(bufferLength); var scene = new THREE.Scene(); var group = new THREE.Group(); var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(0, 0, 100); camera.lookAt(scene.position); scene.add(camera); var renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); var planeGeometry = new THREE.PlaneGeometry(0, 0, 0, 0); var planeMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff, side: THREE.DoubleSide, wireframe: true }); var plane = new THREE.Mesh(planeGeometry, planeMaterial); plane.rotation.x = -0.5 * Math.PI; plane.position.set(0, 30, 0); group.add(plane); var plane2 = new THREE.Mesh(planeGeometry, new THREE.MeshLambertMaterial({ color: 0xffffff, transparent: true, opacity: 0.5 })); plane2.rotation.x = -0.5 * Math.PI; plane2.position.set(0, -30, 0); group.add(plane2); var icosahedronGeometry = new THREE.IcosahedronGeometry(10, 4); var lambertMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff, wireframe: true }); var ball = new THREE.Mesh(icosahedronGeometry, lambertMaterial); ball.position.set(0, 0, 0); group.add(ball); var ambientLight = new THREE.AmbientLight(0xaaaaaa); scene.add(ambientLight); var spotLight = new THREE.SpotLight(0xffffff); spotLight.intensity = 0.9; spotLight.position.set(-10, 40, 20); spotLight.lookAt(ball); spotLight.castShadow = true; scene.add(spotLight); scene.add(group); document.getElementById('out').appendChild(renderer.domElement); window.addEventListener('resize', onWindowResize, false); function render() { analyser.getByteFrequencyData(dataArray); var lowerHalfArray = dataArray.slice(0, (dataArray.length / 2) - 1); var upperHalfArray = dataArray.slice((dataArray.length / 2) - 1, dataArray.length - 1); var overallAvg = avg(dataArray); var lowerMax = max(lowerHalfArray); var lowerAvg = avg(lowerHalfArray); var upperMax = max(upperHalfArray); var upperAvg = avg(upperHalfArray); var lowerMaxFr = lowerMax / lowerHalfArray.length; var lowerAvgFr = lowerAvg / lowerHalfArray.length; var upperMaxFr = upperMax / upperHalfArray.length; var upperAvgFr = upperAvg / upperHalfArray.length; makeRoughGround(plane, modulate(upperAvgFr, 0, 1, 0.5, 4)); makeRoughGround(plane2, modulate(lowerMaxFr, 0, 1, 0.5, 4)); makeRoughBall(ball, modulate(Math.pow(lowerMaxFr, 0.8), 0, 1, 0, 8), modulate(upperAvgFr, 0, 1, 0, 4)); group.rotation.y += 0.005; renderer.render(scene, camera); drawBars(dataArray, 'visualizer2'); requestAnimationFrame(render); } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } function drawBars(dataArray, canvasId) { const canvas = document.getElementById(canvasId); const ctx = canvas.getContext('2d'); const desiredBars = 100; const barWidth = Math.floor(canvas.width / desiredBars); const barSpacing = 5; ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = 'rgba(255, 255, 255, 0.99)'; for (let i = 0; i < desiredBars; i++) { const barHeight = dataArray[Math.floor(i * (dataArray.length / desiredBars))] / 255 * canvas.height; const x = i * (barWidth + barSpacing); const y = canvas.height - barHeight; ctx.fillRect(x, y, barWidth, barHeight); } } function makeRoughBall(mesh, bassFr, treFr) { mesh.geometry.vertices.forEach(function (vertex, i) { var offset = mesh.geometry.parameters.radius; var amp = 7; var time = window.performance.now(); vertex.normalize(); var rf = 0.00001; var distance = (offset + bassFr ) + noise.noise3D(vertex.x + time *rf*7, vertex.y + time*rf*8, vertex.z + time*rf*9) * amp * treFr; vertex.multiplyScalar(distance); }); mesh.geometry.verticesNeedUpdate = true; mesh.geometry.normalsNeedUpdate = true; mesh.geometry.computeVertexNormals(); mesh.geometry.computeFaceNormals(); } function makeRoughGround(mesh, distortionFr) { mesh.geometry.vertices.forEach(function (vertex, i) { var amp = 2; var time = Date.now(); var distance = (noise.noise2D(vertex.x + time * 0.0003, vertex.y + time * 0.0001) + 0) * distortionFr * amp; vertex.z = distance; }); mesh.geometry.verticesNeedUpdate = true; mesh.geometry.normalsNeedUpdate = true; mesh.geometry.computeVertexNormals(); mesh.geometry.computeFaceNormals(); } function fractionate(val, minVal, maxVal) { return (val - minVal)/(maxVal - minVal); } function modulate(val, minVal, maxVal, outMin, outMax) { var fr = fractionate(val, minVal, maxVal); var delta = outMax - outMin; return outMin + (fr * delta); } function avg(arr) { var total = arr.reduce(function(sum, b) { return sum + b; }); return (total / arr.length); } function max(arr) { return arr.reduce(function(a, b){ return Math.max(a, b); }); } render(); } window.onload = vizInit; document.querySelector('.start-audio-btn').addEventListener('click', function() { context.resume().then(() => { document.querySelector("audio").play(); }); }); </script>
Credits: https://github.com/santosharron/audio-visualizer-three-js