Sie sind auf Seite 1von 11

1

Three.js Based Audio Spectrum Visualizer

Stefan Liemawan (2101694690), Asoka Wotulo (2101693611), Samuel Putra (2101693630)

BINUS University International


2

Background

Music visualization was popularized by the existence of windows media player. At that

time, it was considered a replacement for video. It was interactive and was default in every PC

out there. It generates a certain animated imagery or illustration based on the audio source

provided, in this case music. It usually is generated in real time, syncs, rendered and reacts with

the music as we’re hearing them live. There are various techniques that can be used to visualize

the music. It ranges from simple ones, to composite effects. Composite effects are audio

visualization effects that are combines various simple visualization algorithms to create a

visualization that is much more complex. Most of the time though, the music’s frequency (pitch)

and amplitude (loudness) are the two main determining factors. Since music is a subjective

matter, to us an effective music visualization is one that attains a high degree of correlation

between the music’s frequency and amplitude. The music needs to correlate and make sense to

the music.

In this computer graphics project, we were asked to create something that uses JavaScript

and WebGL to produce a visualization on browsers. We then decided to create a web-based

music visualization based on Three.js. Early on, we learned Babylon.js, we found that Babylon.js

even though it has a lot o easy sources to learn from, and is still actively developed, for our uses

Three.js was much more convenient. It is much more feature rich ranging from effects, scenes,

cameras, animations, lights, materials, shaders, objects, geometry, data loaders, utility, and much

more. To add to that, it is better documented than Babylon.js, but our pivoting reason was that it

could support most industry standard file formats, this proved useful as we needed to import

audio files and we wanted to minimalize the off chance of a troubleshoot happening.
3

Problems

There were some problems that we had encounter during the creation of this project. Our

aesthetic was black and white, we wanted to make a minimalist visualization with a higher

complexity to it. We didn’t think of adding various colors to aid the visualization. Another

problem is with a live microphone feed to the computer. At first, we wanted to be able to

visualize a live microphone feed to the computer, however we didn’t manage to get it to work.

Adding the camera shake proved to be tricky as well, but we managed to figure that out.

Related Work

To our best knowledge, there are Three.js based audio visualizer projects. One of them is

a project created by Janine, a student of Fullstack Academy on September 29, 2017. She created

the project using the JavaScript library, Three.js, as well as the Web Audio API. In this project

users can choose four songs that exemplify the different average frequencies and can also decide

how wide the visualization will appear on screen. The height of each cube is directly mapped to

an array of frequency data available at every moment, and the color changes based on the

average frequency. The following link, redirects to her project;

https://www.youtube.com/watch?v=wlvLEdYmK1o.

The difference between her projects and ours, is the way the music was visualized. It was

visualized as a wave spectrum comprised of smaller cubes. Our project visualizes the music

using arrayed circles.


4

Implementation
5

Code Snippet

<html>

<head>
<title>Audio</title>
<style>
body {
margin: 0;
color: black;
}
canvas {
width: 100%;
height: 100%;
}
#audio {
position: absolute;
bottom: 2%;
left: 50%;
transform: translateX(-50%);
width: 50%;
height: 5%;
outline: none;
}
#file {
position: absolute;
left: 50%;
transform: translateX(-50%);
}
</style>
</head>

<body>

<input type="file" id="file" accept="audio/*" />


<audio id="audio" controls></audio>

<script src="/node_modules/three/build/three.min.js"></script>
<script
src="/node_modules/three/examples/js/controls/OrbitControls.js"></script>
<script>
6

var id = null;
var white = new THREE.Color('white');
var color = new THREE.Color('black');
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// create scene
var scene = new THREE.Scene();
scene.background = new THREE.Color(white);
var camera = new THREE.PerspectiveCamera(100, window.innerWidth /
window.innerHeight, 0.1, 2000);
camera.position.set(0, 0, 600);
camera.updateProjectionMatrix();

var controls = new THREE.OrbitControls(camera, renderer.domElement);


controls.minDistance = 700;
controls.maxDistance = 900;
controls.update();
var file = document.getElementById("file");
var audio = document.getElementById("audio");
file.onchange = function () {
if (id !== null) cancelAnimationFrame(id);
var files = this.files;
audio.src = URL.createObjectURL(files[0]);
audio.load();
audio.play();
render();
}
var context = new AudioContext();
var src = context.createMediaElementSource(audio);
var analyser = context.createAnalyser();
src.connect(analyser);
analyser.connect(context.destination);
analyser.fftSize = 1024;
var bufferLength = analyser.frequencyBinCount;
var data = new Uint8Array(bufferLength);
//------------------------------------------------------GEOMETRY----------
----------------------------------------------
//group1 circle spectrum
var geometry = new THREE.TorusBufferGeometry(10, 0.03, 16, 100);
var material = new THREE.MeshBasicMaterial({ color: color });
var group = new THREE.Object3D();
scene.add(group);
7

for (var i = 0; i < data.length; i++) {


var mesh = new THREE.Mesh(geometry, material);
if (i % 2 == 0) mesh.position.set(0, 0, i);
else mesh.position.set(0, 0, -i);
group.add(mesh);
}

//group2 outer diamond


var geometry2 = new THREE.OctahedronGeometry(20, 0);
var material2 = new THREE.MeshBasicMaterial({ color: color });
var group2 = new THREE.Object3D();
scene.add(group2);
for (var i=0; i<4; i++) {
var mesh2 = new THREE.Mesh(geometry2, material2);
group2.add(mesh2);
}
group2.children[0].position.set(500, 0, 0);
group2.children[1].position.set(-500, 0, 0);
group2.children[2].position.set(0, 500, 0);
group2.children[3].position.set(0, -500, 0);
//group3 inner diamond
var geometry3 = new THREE.OctahedronGeometry(12, 0);
var material3 = new THREE.MeshBasicMaterial({ color: color });
var group3 = new THREE.Object3D();
scene.add(group3);
for (var i = 0; i < 4; i++) {
var mesh3 = new THREE.Mesh(geometry3, material3);
group3.add(mesh3);
}
group3.children[0].position.set(250, 0, 0);
group3.children[1].position.set(-250, 0, 0);
group3.children[2].position.set(0, 250, 0);
group3.children[3].position.set(0, -250, 0);
//group 4 line background
var group4 = new THREE.Object3D();
scene.add(group4);
var parameters = [[3.5, 0xaaaaaa, 0.25], [4.0, 0xaaaaaa, 0.5], [4.5,
0xaaaaaa, 0.75]];
var geometry4 = createGeometry();
for (var i = 0; i < parameters.length; ++i) {
var p = parameters[i];
var material4 = new THREE.LineBasicMaterial({ color: p[1], opacity:
p[2] });
8

var line = new THREE.LineSegments(geometry4, material4);


line.scale.x = line.scale.y = line.scale.z = p[0];
line.userData.originalScale = p[0];
line.rotation.y = Math.random() * Math.PI;
line.updateMatrix();
group4.add(line);
}
// line sphere geometry from
https://github.com/mrdoob/three.js/blob/master/examples/webgl_lines_sphere.html
function createGeometry() {
var geometry = new THREE.BufferGeometry();
var vertices = [];
var vertex = new THREE.Vector3();
for (var i = 0; i < 1500; i++) {
vertex.x = Math.random() * 2 - 1;
vertex.y = Math.random() * 2 - 1;
vertex.z = Math.random() * 2 - 1;
vertex.normalize();
vertex.multiplyScalar(200);
vertices.push(vertex.x, vertex.y, vertex.z);
vertex.multiplyScalar(Math.random() * 0.09 + 1);
vertices.push(vertex.x, vertex.y, vertex.z);
}
geometry.addAttribute('position', new
THREE.Float32BufferAttribute(vertices, 3));
return geometry;
}
controls.enabled = false;
renderer.render(scene, camera);
//------------------------------------------------------------------------
--------------------------------------------

function render() {
// get the frequency of the sound
controls.enabled = true;
analyser.getByteFrequencyData(data);
var avgdata = avg(data);
var lowerHalfArray = data.slice(0, (data.length / 2) - 1);
var upperHalfArray = data.slice((data.length / 2) - 1, data.length -
1);
var lowerAvg = avg(lowerHalfArray);
var upperAvg = avg(upperHalfArray);
9

// color = new THREE.Color(parseInt(lowerAvg) % 255,


parseInt(upperAvg) % 255, parseInt(avgdata) % 255);
// console.log(color);
for (var i = 0; i < data.length; i++) {
group.children[i].scale.x = (data[i] * 0.1) + 0.001;
group.children[i].scale.y = (data[i] * 0.1) + 0.001;
// group.children[i].material.color.setHex(Math.random() *
0xffffff );
}
var speed2 = upperAvg * 0.002;
group2.rotateZ(speed2);
var range2 = upperAvg * 6 + 500;
group2.children[0].position.set(range2, 0, 0);
group2.children[1].position.set(-range2, 0, 0);
group2.children[2].position.set(0, range2, 0);
group2.children[3].position.set(0, -range2, 0);
// for (var i = 0; i < 4; i++)
group2.children[i].material.color.setHex(Math.random() * 0xffffff );
var speed3 = lowerAvg * -0.0002;
group3.rotateZ(speed3);
var range3 = lowerAvg * 1.5 + 250;
group3.children[0].position.set(range3, 0, 0);
group3.children[1].position.set(-range3, 0, 0);
group3.children[2].position.set(0, range3, 0);
group3.children[3].position.set(0, -range3, 0);
// for (var i = 0; i < 4; i++)
group3.children[i].material.color.setHex(Math.random() * 0xffffff );
group4.rotateY(avgdata * 0.0004);
if (lowerAvg / upperAvg < avgdata) {
camera.strength = 15;
camera.damper = 3;
}
// camera shake from http://anpetersen.me/2015/01/16/for-the-sake-of-
screen-shake.html
if (camera.strength > 0) {
var value1 = Math.random() * 2 * camera.strength -
camera.strength;
var value2 = Math.random() * 2 * camera.strength -
camera.strength;
var value3 = Math.random() * 2 * camera.strength -
camera.strength;
camera.position.x += value1;
camera.position.y += value2;
10

camera.position.z += value3;
camera.strength -= camera.damper;
}
id = requestAnimationFrame(render);
controls.update();
renderer.render(scene, camera);
}
function max(params) {
return Math.max.apply(Math, params);
}
function avg(params) {
var sum = 0;
for (var i = 0; i < params.length; i++) sum += params[i];
return sum / params.length;
}

</script>
</body>

</html>
11

Links & References

https://github.com/asokawotulo/CG-Final-Project/blob/master

https://threejs.org/docs/index.html#manual/en

https://threejs.org/docs/#api/en/audio/AudioAnalyser

https://www.youtube.com/watch?v=wlvLEdYmK1o

Das könnte Ihnen auch gefallen