I am currently working on a 3D audio visualiser with Three.js (based on one I found on GitHub).
When I use a static mp3 file from my workspace, everything works fine. But when i use a URL for my audio file (Example : https://actions.google.com/sounds/v1/weapons/a10_jet_flyby_fire.ogg), the visualisation is not working, like if the code would only accept local files.
Im kind of a newbie in Three.js so I would really aprreciate your help !
Here is the HTML :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Audio Visualizer based on Three.js</title>
<link
href="https://fonts.googleapis.com/css?family=Saira"
rel="stylesheet"
/>
<link rel="stylesheet" href="./styleThree.css" />
</head>
<body>
<div id="content">
<audio id="audio" controls></audio>
<div id="out"></div>
</div>
<!-- partial -->
<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 src="./scriptThree.js"></script>
</body>
</html>
Here is the JS :
var noise = new SimplexNoise();
var vizInit = function () {
var audio = document.getElementById("audio");
audio.src = "MY AUDIO SOURCE";
audio.load();
audio.oncanplaythrough = function () {
audio.play();
play();
};
};
function play() {
var 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;
var 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); // Reduced segments from 20 to 2
var planeMaterial = new THREE.MeshLambertMaterial({
color: 0x1904ce,
side: THREE.DoubleSide,
wireframe: true,
// transparent: true,
// opacity: 0,
});
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, planeMaterial);
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: 0xffffee,
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);
render();
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.5), 0, 1, 0, 3),
modulate(upperAvgFr, 0, 1, 0, 3)
);
group.rotation.y += 0.005;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function makeRoughBall(mesh, bassFr, treFr) {
mesh.geometry.vertices.forEach(function (vertex, i) {
var offset = mesh.geometry.parameters.radius;
var amp = 4;
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();
}
audio.play();
}
window.onload = vizInit();
document.body.addEventListener("touchend", function (ev) {
context.resume();
});
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);
});
}
Mathwin is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.