-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathaudioSphere.js
122 lines (101 loc) · 4.43 KB
/
audioSphere.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const stats = new Stats();
const renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
// renderer.setClearColor(0xFF0000);
renderer.sortObjects = true;
document.body.appendChild(renderer.domElement);
document.body.appendChild(stats.domElement);
camera.position.z = 270;
camera.position.y = 100;
const orbit = new THREE.OrbitControls(camera, render.domElement);
/* PARTICLE SYSTEM */
const particles = new THREE.Geometry();
const particleMaterial = new THREE.PointsMaterial({
color: 0xffffff,
size: 4,
map: new THREE.TextureLoader().load('res/particle.png'),
blending: THREE.AdditiveBlending,
transparent: true,
depthWrite: false
});
// Create a discretized 3D sphere of particles using a spiral discretization
// See https://gist.github.com/aptxwang/628a2b038c6d01ecbc57
// Note that this paper could also be interesting to look more into: https://agupubs.onlinelibrary.wiley.com/doi/epdf/10.1029/2007GC001581
const radius = 100;
const nbPoints = 4000;
const step = 2 / nbPoints;
const turns = 60; // Number of times to turn around the y-axis
for (let i = -1; i <= 1; i += step) {
const phi = Math.acos(i);
const theta = (2 * turns * phi) % (2 * Math.PI);
const particle = new THREE.Vector3(0, 0, 0);
// Note that y and z are flipped in the following calculations since the cartesian coordinate system is in a different rotation in Three.js than it typically is visualized in math courses
particle.x = particle.initX = Math.cos(theta) * Math.sin(phi) * radius;
particle.z = particle.initZ = Math.sin(theta) * Math.sin(phi) * radius;
particle.y = particle.initY = Math.cos(phi) * radius;
particles.vertices.push(particle);
}
// Create the particle system
const particleSystem = new THREE.Points(particles, particleMaterial);
scene.add(particleSystem);
/* AUDIO - Needs a user event to start */
let analyser;
let frequencyData;
const skipFrequencies = 620; // Skip the first frequencies as they have too big values and mess up the shape of the sphere
const playBtn = document.getElementById('play');
playBtn.addEventListener('click', () => {
const audioCtx = new AudioContext();
analyser = audioCtx.createAnalyser();
const audioEl = document.getElementById('audio');
const source = audioCtx.createMediaElementSource(audioEl);
source.connect(analyser);
analyser.connect(audioCtx.destination);
analyser.fftSize = 8192;
analyser.smoothingTimeConstant = 0.8;
frequencyData = new Uint8Array(analyser.frequencyBinCount);
audioEl.play();
// Hide the play button
playBtn.setAttribute('style', 'display: None;');
// Show the audio controls now
audioEl.setAttribute('style', 'display: block;');
});
function render() {
requestAnimationFrame(render);
// Update the particles
if (frequencyData) {
analyser.getByteFrequencyData(frequencyData);
// The frequencies are applied to the particles in a symmetric fashion, from the center row of the sphere.
// This means the low frequencies appear in the middle of the sphere, while the highest frequencies are located at the two poles.
for (let i = 0; i < particles.vertices.length / 2; i++) {
if (i + skipFrequencies < frequencyData.length) {
let particle = particles.vertices[Math.floor(particles.vertices.length / 2) + i];
const factor = frequencyData[i + skipFrequencies] / 256 + 1; // between 1 and 2
particle.x = particle.initX * factor;
particle.y = particle.initY * factor;
particle.z = particle.initZ * factor;
particle = particles.vertices[Math.floor(particles.vertices.length / 2) - i];
particle.x = particle.initX * factor;
particle.y = particle.initY * factor;
particle.z = particle.initZ * factor;
}
}
particleSystem.geometry.verticesNeedUpdate = true; // Need to specify that the object has changed
}
orbit.update();
stats.update();
renderer.render(scene, camera);
}
render();
window.addEventListener(
'resize',
() => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
},
false
);