Skip to content

Instantly share code, notes, and snippets.

@yportne8
Created February 18, 2022 00:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yportne8/e3f87f4b2bb526c4d1f0c1ba44ba21a0 to your computer and use it in GitHub Desktop.
Save yportne8/e3f87f4b2bb526c4d1f0c1ba44ba21a0 to your computer and use it in GitHub Desktop.
Spiral Wave
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="theme-color" content="#f15f48">
<meta name="application-name" content="React Calculator">
<meta name="description" content="Threejs Audio Visualizer">
<meta name="keywords" content="Threejs, Javascript, Ion Drimba Filho">
<meta name="subject" content="Threejs Audio Visualizer">
<meta name="copyright" content="Ion Drimba Filho">
<meta name="robots" content="index,follow">
<meta name="topic" content="">
<meta name="summary" content="ThreeJs Audio Visualizer Two">
<meta name="author" content="Ion Drimba Filho">
<meta name="url" content="http://iondrimba.github.io/threejs-audio-visualizer/public/index.html">
<meta name="pagename" content="ThreeJs Audio Visualizer Two">
<meta name="category" content="">
<meta name="coverage" content="Worldwide">
<meta name="distribution" content="Global">
<meta name="rating" content="General">
<meta name="subtitle" content="ThreeJs Audio Visualizer Two">
<meta name="target" content="all">
<meta http-equiv="cleartype" content="on">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="Threejs Audio Visualizer Two">
<meta name="twitter:creator" content="Ion Drimba Filho">
<meta name="twitter:title" content="Threejs Audio Visualizer Two">
<meta name="twitter:description" content="ThreeJs Audio Visualizer Two">
<meta name="twitter:image:src" content="https://raw.githubusercontent.com/iondrimba/images/master/demo2.PNG">
<meta property="og:url" content="http://iondrimba.github.io/threejs-audio-visualizer/public/index.html">
<meta property="og:type" content="website">
<meta property="og:title" content="Threejs Audio Visualizer">
<meta property="og:image" content="https://raw.githubusercontent.com/iondrimba/images/master/demo2.PNG">
<meta property="og:description" content="Threejs Audio Visualizer Two">
<meta property="og:site_name" content="Threejs Audio Visualizer Two">
<meta property="article:author" content="https://iondrimbafilho.me/">
<meta property="article:publisher" content="https://iondrimbafilho.me/">
<meta itemprop="name" content="ThreeJs Audio Visualizer Two">
<meta itemprop="description" content="Threejs Audio Visualizer">
<meta itemprop="image" content="https://raw.githubusercontent.com/iondrimba/images/master/demo2.PNG">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<title>Threejs Audio Visualizer Two</title>
<link href="https://fonts.googleapis.com/css?family=Ropa+Sans" rel="stylesheet">
</head>
<body>
<button class="play-intro">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 32 32"
data-tags="play,media control">
<g fill="#fff" transform="scale(0.03125 0.03125)">
<path d="M192 0v1024l640-511.264-640-512.736z" />
</g>
</svg>
</button>
<div class="credits">
<h1>
<a href="https://comtruise.bandcamp.com/album/in-decay" target="_blank" rel="noopener noreferrer">Com Truise </a>/
<a href="https://comtruise.bandcamp.com/track/84-dreamin" target="_blank" rel="noopener noreferrer">84' Dreamin</a>
</h1>
<div class="controls">
<button class="play">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="32" height="32" viewBox="0 0 25 32"
data-tags="play,media control">
<g fill="#fff" transform="scale(0.03125 0.03125)">
<path d="M192 0v1024l640-511.264-640-512.736z" />
</g>
</svg>
</button>
<button class="pause">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32" viewBox="0 0 32 32"
data-tags="pause,media control">
<g fill="#fff" transform="scale(0.03125 0.03125)">
<path d="M352 0h-192c-17.696 0-32 14.336-32 32v960c0 17.696 14.304 32 32 32h192c17.696 0 32-14.304 32-32v-960c0-17.664-14.304-32-32-32zM864 0h-192c-17.696 0-32 14.336-32 32v960c0 17.696 14.304 32 32 32h192c17.696 0 32-14.304 32-32v-960c0-17.664-14.304-32-32-32z"
/>
</g>
</svg>
</button>
</div>
</div>
<div class="loader"></div>
<audio id="audio" crossOrigin="anonymous"></audio>
</body>
</html>
class Loader {
constructor() {
this.callback = null;
}
load(file) {
const request = new XMLHttpRequest();
request.open('GET', file, true);
request.onprogress = (evt) => {
let percent = Math.round((evt.loaded / evt.total) * 100);
this.callback(percent);
};
request.onload = () => { this.complete(file); };
request.send();
}
progress(callback) { this.callback = callback; };
complete() { }
}
class App {
constructor() {
this.loader = new Loader();
this.loader.progress((percent) => {
this.progress(percent);
});
this.playIntro = document.querySelector('.play-intro');
this.loaderBar = document.querySelector('.loader');
this.loader.load('https://iondrimbafilho.me/demo.mp3');
this.loader.complete = this.complete.bind(this);
this.count = 0;
this.percent = 0;
this.playing = false;
this.objects = [];
}
progress(percent) {
this.loaderBar.style.transform = `scale(${percent / 100}, 1)`;
if (percent === 100) {
setTimeout(() => {
requestAnimationFrame(() => {
this.playIntro.classList.add('control-show');
this.loaderBar.classList.add('removeLoader');
this.loaderBar.style.transform = 'scale(1, 0)';
})
}, 300);
}
}
complete(file) {
setTimeout(() => {
this.firstRing = new THREE.Object3D();
this.secondRing = new THREE.Object3D();
this.thirdRing = new THREE.Object3D();
this.fourthRing = new THREE.Object3D();
this.setupAudio();
this.addSoundControls();
this.createScene();
this.createCamera();
this.addAmbientLight();
this.addSpotLight();
this.addCameraControls();
this.addFloor();
this.createRingOfSquares(20, 1, 0x4250ca, this.firstRing);
this.createRingOfSquares(30, 2, 0x721fa1, this.secondRing);
this.createRingOfSquares(40, 3, 0xf95c38, this.thirdRing);
this.createRingOfSquares(50, 4, 0x5e0f86, this.fourthRing);
this.animate();
this.playSound(file);
}, 200);
document.addEventListener('visibilitychange', (evt) => {
if(evt.target.hidden) {
this.pause();
} else {
this.play();
}
}, false);
}
addSoundControls() {
this.btnPlay = document.querySelector('.play');
this.btnPause = document.querySelector('.pause');
this.btnPlay.addEventListener('click', () => {
this.play();
});
this.btnPause.addEventListener('click', () => {
this.pause();
});
}
createRingOfSquares(count, radius, color, group) {
const size = .5;
const geometry = new THREE.BoxGeometry(size, size, size);
const material = new THREE.MeshLambertMaterial({
color
});
for (let index = 0; index < count; index++) {
const l = 360 / count;
const pos = this.radians(l * index);
const obj = this.createObj(color, geometry, material);
const distance = (radius * 2);
const sin = Math.sin(pos) * distance;
const cos = Math.cos(pos) * distance;
obj.position.set(sin, 0, cos);
obj.rotateY(pos);
this.objects.push(obj);
group.add(obj);
}
this.scene.add(group);
}
play() {
this.audioCtx.resume();
this.audioElement.play();
this.btnPlay.classList.remove('control-show');
this.btnPause.classList.add('control-show');
}
pause() {
this.audioElement.pause();
this.btnPause.classList.remove('control-show');
this.btnPlay.classList.add('control-show');
}
createScene() {
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0xf15f48);
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(this.renderer.domElement);
}
createCamera() {
const width = window.innerWidth;
const height = window.innerHeight;
this.camera = new THREE.PerspectiveCamera(70, width/ height, 1, 1000);
this.camera.position.set(-12, 17, 10);
this.scene.add(this.camera);
}
addCameraControls() {
this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
this.controls.enableDamping = true;
this.controls.dampingFactor = 0.04;
document.body.style.cursor = "-moz-grabg";
document.body.style.cursor = "-webkit-grab";
this.controls.addEventListener("start", () => {
requestAnimationFrame(() => {
document.body.style.cursor = "-moz-grabbing";
document.body.style.cursor = "-webkit-grabbing";
});
});
this.controls.addEventListener("end", () => {
requestAnimationFrame(() => {
document.body.style.cursor = "-moz-grab";
document.body.style.cursor = "-webkit-grab";
});
});
}
createObj(color, geometry, material) {
const obj = new THREE.Mesh(geometry, material);
obj.castShadow = true;
obj.receiveShadow = true;
return obj;
}
onResize() {
const ww = window.innerWidth;
const wh = window.innerHeight;
this.camera.aspect = ww / wh;
this.camera.updateProjectionMatrix();
this.renderer.setSize(ww, wh);
}
addFloor() {
const planeGeometry = new THREE.PlaneGeometry(2000, 2000);
const planeMaterial = new THREE.ShadowMaterial({ opacity: 0.08 });
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
planeGeometry.rotateX(- Math.PI / 2);
plane.position.y = -1;
plane.receiveShadow = true;
this.scene.add(plane);
}
moveRingGroup(group, value) {
group.rotation.y += value;
}
addSpotLight() {
const spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(0, 20, 1);
spotLight.castShadow = true;
this.scene.add(spotLight);
const spotLightHelper = new THREE.SpotLightHelper(spotLight);
}
addAmbientLight() {
const light = new THREE.AmbientLight(0xffffff);
this.scene.add(light);
}
animate() {
this.controls.update();
this.drawWave();
this.renderer.render(this.scene, this.camera);
requestAnimationFrame(this.animate.bind(this));
}
radians(degrees) {
return degrees * Math.PI / 180;
}
drawWave() {
if (this.playing) {
this.analyser.getByteFrequencyData(this.frequencyData);
for (var i = 0; i < 140; i++) {
const p = this.frequencyData[i];
const s = this.objects[i];
const z = s.position;
TweenMax.to(z, .2, {
y: p / 20
});
}
}
this.moveRingGroup(this.firstRing, .01);
this.moveRingGroup(this.secondRing, -.01);
this.moveRingGroup(this.thirdRing, .02);
this.moveRingGroup(this.fourthRing, -.02);
}
setupAudio() {
this.audioElement = document.getElementById('audio');
this.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
this.analyser = this.audioCtx.createAnalyser();
this.source = this.audioCtx.createMediaElementSource(this.audioElement);
this.source.connect(this.analyser);
this.source.connect(this.audioCtx.destination);
this.bufferLength = this.analyser.frequencyBinCount;
this.frequencyData = new Uint8Array(this.bufferLength);
this.audioElement.volume = .5;
this.audioElement.addEventListener('playing', () => {
this.playing = true;
});
this.audioElement.addEventListener('pause', () => {
this.playing = false;
});
this.audioElement.addEventListener('ended', () => {
this.playing = false;
});
}
playSound(file) {
setTimeout(() => {
this.playIntro.addEventListener('click', (evt)=>{
evt.currentTarget.classList.remove('control-show');
this.play();
});
this.audioElement.src = file;
}, 500);
}
}
window.app = new App();
window.addEventListener('resize', app.onResize.bind(app));
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r123/three.min.js"></script>
<script src="https://Threejs.org/examples/js/controls/OrbitControls.js"></script>
html, body {
margin: 0;
padding: 0;
font-family: 'Ropa Sans', sans-serif;
background-color: #f15f48;
color: #fff;
box-sizing: border-box;
overflow: hidden;
cursor: -webkit-grab;
cursor: -moz-grab;
}
canvas {
width: 100%;
height: 100%;
}
h1 {
padding: 0;
margin: 0;
color: inherit;
}
.play-intro {
position: absolute;
top: 50%;
left: 50%;
display: none;
transform: translate(-50%, -50%);
height: 100px;
width: 100px;
cursor: pointer;
}
.play-intro svg {
width: 100%;
}
button {
background-color: transparent;
border: 0;
cursor: pointer;
}
.controls {
position: absolute;
right: 30px;
top: 10px;
}
.play {
display: none;
}
.pause {
display: none;
}
.control-show {
display: block;
}
.loader {
position: absolute;
width: 100%;
height: 100%;
top: 0;
z-index: 0;
transform: scale(0, 1);
transform-origin: left;
transition: transform 0.3s cubic-bezier(0.23, 1, 0.32, 1);
background: linear-gradient(to right, #0391fd, #751ea1);
}
a {
color: inherit;
text-decoration: none;
}
.credits {
margin: 20px;
position: absolute;
z-index: 1;
bottom: 10px;
width: 100%;
}
.credits h1 {
font-size: 20pt;
width: 80%;
line-height: 1.4;
}
.credits .controls svg {
width: 50px;
}
.removeLoader {
transform-origin: bottom;
transition: transform 0.3s 0.3s cubic-bezier(0.23, 1, 0.32, 1);
}
@media screen and (min-width: 768px) {
.credits {
margin-left: 20px;
margin-top: 20px;
position: absolute;
z-index: 1;
bottom: inherit;
width: 100%;
}
.credits h1 {
font-size: 22pt;
width: auto;
line-height: 1.4;
}
.credits .controls {
right: 50px;
}
.credits .controls svg {
width: auto;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment