├── .gitignore ├── LICENSE ├── README.md ├── docs ├── SUPER_RELAKS_DABSTEP_[Relax_Dubstep]-2014_(RU.MUSIC-LORD.COM).mp3 ├── index.html └── index.js ├── example ├── index.html ├── many.html └── media │ └── SUPER_RELAKS_DABSTEP_[Relax_Dubstep]-2014_(RU.MUSIC-LORD.COM).mp3 ├── package-lock.json ├── package.json ├── resources ├── image.png ├── many.png └── player.jpg ├── src ├── index.ts ├── planeMeshParameters.ts └── shaders.ts ├── tsconfig.json └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | .idea 3 | /dist -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Ivan Vedenin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

3D Music Visualization

2 | 3 | ## About 4 | 5 | 3D visualization of music using Three.js and web audio API 6 | 7 | [Try it here](https://l1ve4code.github.io/3d-music-visualizer/) 8 | 9 | ### Main screen 10 | 11 | 12 | 13 | ### Additional screen 14 | 15 | 16 | 17 | ### Technologies 18 | 19 | * Language: **TS, JS** 20 | * Library: **THREE.js** 21 | * Technologies: **Web Audio API, Webpack** 22 | 23 | ## Installing 24 | 25 | **Firstly** clone the project. 26 | 27 | ```git 28 | git clone https://github.com/l1ve4code/3d-music-visualizer.git 29 | ``` 30 | 31 | **Secondly** install dependencies 32 | ```cmd 33 | npm install 34 | ``` 35 | 36 | **Thirdly** run project 37 | 38 | ### For watch 39 | 40 | ```cmd 41 | npm run watch 42 | ``` 43 | 44 | ### For build 45 | 46 | ```cmd 47 | npm run build 48 | ``` 49 | 50 | 51 | ## Author 52 | 53 | * Telegram: **[@live4code](https://t.me/live4code)** 54 | * Email: **steven.marelly@gmail.com** 55 | 56 | ## Contributors ✨ 57 | 58 | 59 | 60 | 61 | 62 |

Liverdox
-------------------------------------------------------------------------------- /docs/SUPER_RELAKS_DABSTEP_[Relax_Dubstep]-2014_(RU.MUSIC-LORD.COM).mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/l1ve4code/3d-music-visualizer/0804b61f3c7f071c1a76c5f6c11a95e9abf6727e/docs/SUPER_RELAKS_DABSTEP_[Relax_Dubstep]-2014_(RU.MUSIC-LORD.COM).mp3 -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 3D Music Visualizer 7 | 25 | 26 | 27 | 28 |
29 | 30 |
31 | 32 | 33 | 38 | 39 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 3D Music Visualizer 7 | 25 | 26 | 27 | 28 |
29 | 30 |
31 | 32 | 33 | 38 | 39 | -------------------------------------------------------------------------------- /example/many.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 3D Music Visualizer 7 | 71 | 72 | 73 | 74 |
75 |
76 |
77 | 78 |
79 |
80 | 85 |
86 | 87 | 88 | 94 | 95 | -------------------------------------------------------------------------------- /example/media/SUPER_RELAKS_DABSTEP_[Relax_Dubstep]-2014_(RU.MUSIC-LORD.COM).mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/l1ve4code/3d-music-visualizer/0804b61f3c7f071c1a76c5f6c11a95e9abf6727e/example/media/SUPER_RELAKS_DABSTEP_[Relax_Dubstep]-2014_(RU.MUSIC-LORD.COM).mp3 -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "3d-music-visualizer", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "dist/index.js", 6 | "types": "index.d.ts", 7 | "scripts": { 8 | "watch": "http-server -o ./example/", 9 | "dev": "webpack --mode=development", 10 | "build": "webpack --mode=production" 11 | }, 12 | "author": "", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@babel/core": "7.12.10", 16 | "@babel/preset-env": "7.12.11", 17 | "@babel/preset-typescript": "7.12.7", 18 | "@types/three": "^0.139.0", 19 | "babel-loader": "^8.2.4", 20 | "ts-loader": "^9.2.8", 21 | "typescript": "^4.6.3", 22 | "webpack": "^5.71.0", 23 | "webpack-cli": "^4.9.2", 24 | "webpack-dev-server": "^4.8.0", 25 | "webpack-merge": "^5.8.0", 26 | "http-server": "^14.1.0" 27 | }, 28 | "dependencies": { 29 | "three": "^0.139.2" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /resources/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/l1ve4code/3d-music-visualizer/0804b61f3c7f071c1a76c5f6c11a95e9abf6727e/resources/image.png -------------------------------------------------------------------------------- /resources/many.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/l1ve4code/3d-music-visualizer/0804b61f3c7f071c1a76c5f6c11a95e9abf6727e/resources/many.png -------------------------------------------------------------------------------- /resources/player.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/l1ve4code/3d-music-visualizer/0804b61f3c7f071c1a76c5f6c11a95e9abf6727e/resources/player.jpg -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | import shaders from "./shaders"; 3 | import planeMeshParameters from "./planeMeshParameters"; 4 | 5 | 6 | function init(audio: HTMLAudioElement, container: HTMLElement | Window = document.body) { 7 | const uniforms = { 8 | u_time: { 9 | type: "f", 10 | value: 2.0, 11 | }, 12 | u_amplitude: { 13 | type: "f", 14 | value: 4.0, 15 | }, 16 | u_data_arr: { 17 | type: "float[64]", 18 | value: new Uint8Array(), 19 | }, 20 | }; 21 | 22 | const audioContext = new window.AudioContext(); 23 | 24 | const source = audioContext.createMediaElementSource(audio); 25 | const analyser = audioContext.createAnalyser(); 26 | source.connect(analyser); 27 | analyser.connect(audioContext.destination); 28 | analyser.fftSize = 1024; 29 | const dataArray = new Uint8Array(analyser.frequencyBinCount); 30 | 31 | _initThree(); 32 | 33 | 34 | function _initThree() { 35 | const isWindow = container instanceof Window; 36 | const _container = isWindow ? document.body : container; 37 | const width = isWindow ? window.innerWidth : container.clientWidth; 38 | const height = isWindow ? window.innerHeight : container.clientHeight; 39 | const scene = new THREE.Scene(); 40 | 41 | 42 | const ambientLight = new THREE.AmbientLight(0xaaaaaa); 43 | ambientLight.castShadow = false; 44 | 45 | const spotLight = new THREE.SpotLight(0xffffff, ); 46 | spotLight.intensity = 0.9; 47 | spotLight.position.set(-10, 40, 20); 48 | spotLight.castShadow = true; 49 | 50 | const camera = new THREE.PerspectiveCamera( 51 | 85, 52 | width / height, 53 | 1, 54 | 1000 55 | ); 56 | camera.position.z = 80; 57 | 58 | const renderer = new THREE.WebGLRenderer(); 59 | renderer.setSize(width, height); 60 | renderer.setClearAlpha(0); 61 | 62 | const canvas = renderer.domElement; 63 | canvas.style.opacity = "1"; 64 | canvas.style.transition = "opacity 0.4s"; 65 | _container.appendChild(canvas); 66 | 67 | const planeGeometry = new THREE.PlaneGeometry(64, 64, 64, 64); 68 | const planeMaterial = new THREE.ShaderMaterial({ 69 | vertexShader: shaders.vertex, 70 | fragmentShader: shaders.fragment, 71 | uniforms: uniforms, 72 | wireframe: true 73 | }); 74 | 75 | let planeMeshArray = []; 76 | 77 | planeMeshParameters.forEach(item => { 78 | const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial); 79 | 80 | if(item.rotation.x == undefined) { 81 | planeMesh.rotation.y = item.rotation.y; 82 | } else { 83 | planeMesh.rotation.x = item.rotation.x; 84 | } 85 | 86 | planeMesh.scale.x = item.scale; 87 | planeMesh.scale.y = item.scale; 88 | planeMesh.scale.z = item.scale; 89 | planeMesh.position.x = item.position.x; 90 | planeMesh.position.y = item.position.y; 91 | planeMesh.position.z = item.position.z; 92 | 93 | planeMeshArray.push(planeMesh) 94 | scene.add(planeMesh); 95 | }); 96 | 97 | scene.add(ambientLight); 98 | scene.add(spotLight); 99 | 100 | 101 | const render = () => { 102 | analyser.getByteFrequencyData(dataArray); 103 | uniforms.u_data_arr.value = dataArray; 104 | camera.rotation.z += 0.001; 105 | renderer.render(scene, camera); 106 | } 107 | 108 | 109 | let idRequestAnimationFrame = 0; 110 | let idTimeout: NodeJS.Timeout; 111 | let animate = () => { 112 | idRequestAnimationFrame = requestAnimationFrame(animate); 113 | render(); 114 | } 115 | audio.addEventListener("play", () => { 116 | canvas.style.opacity = "1"; 117 | clearTimeout(idTimeout); 118 | animate(); 119 | audioContext.resume(); 120 | }); 121 | audio.addEventListener("pause", () => { 122 | canvas.style.opacity = "0"; 123 | idTimeout = setTimeout(() => cancelAnimationFrame(idRequestAnimationFrame), 400); 124 | }); 125 | window.addEventListener("resize", () => { 126 | const width = isWindow ? window.innerWidth : container.clientWidth; 127 | const height = isWindow ? window.innerHeight : container.clientHeight; 128 | console.log(width, height); 129 | renderer.setSize(width, height); 130 | camera.aspect = width / height; 131 | camera.updateProjectionMatrix(); 132 | canvas.width = width; 133 | canvas.height = height; 134 | }); 135 | } 136 | } 137 | 138 | module.exports = { 139 | init 140 | } -------------------------------------------------------------------------------- /src/planeMeshParameters.ts: -------------------------------------------------------------------------------- 1 | const planeMeshParameters = [ 2 | { 3 | rotation: { 4 | x: -Math.PI / 2, 5 | }, 6 | scale: 2, 7 | position: { 8 | x: 0, 9 | y: 40, 10 | z: 10 11 | } 12 | }, 13 | { 14 | rotation: { 15 | x: Math.PI / 2, 16 | }, 17 | scale: 2, 18 | position: { 19 | x: 0, 20 | y: -40, 21 | z: 10 22 | } 23 | }, 24 | { 25 | rotation: { 26 | y: Math.PI / 2, 27 | }, 28 | scale: 2, 29 | position: { 30 | x: 40, 31 | y: 0, 32 | z: 10 33 | } 34 | }, 35 | { 36 | rotation: { 37 | y: -Math.PI / 2, 38 | }, 39 | scale: 2, 40 | position: { 41 | x: -40, 42 | y: 0, 43 | z: 10 44 | } 45 | }, 46 | { 47 | rotation: { 48 | x: -Math.PI / 2, 49 | }, 50 | scale: 2, 51 | position: { 52 | x: 0, 53 | y: 40, 54 | z: -118 55 | } 56 | }, 57 | { 58 | rotation: { 59 | x: Math.PI / 2, 60 | }, 61 | scale: 2, 62 | position: { 63 | x: 0, 64 | y: -40, 65 | z: -118 66 | } 67 | }, 68 | { 69 | rotation: { 70 | y: Math.PI / 2, 71 | }, 72 | scale: 2, 73 | position: { 74 | x: 40, 75 | y: 0, 76 | z: -118 77 | } 78 | }, 79 | { 80 | rotation: { 81 | y: -Math.PI / 2, 82 | }, 83 | scale: 2, 84 | position: { 85 | x: -40, 86 | y: 0, 87 | z: -118 88 | } 89 | } 90 | ]; 91 | 92 | export default planeMeshParameters; -------------------------------------------------------------------------------- /src/shaders.ts: -------------------------------------------------------------------------------- 1 | const shaders = { 2 | vertex: ` 3 | varying float x; 4 | varying float y; 5 | varying float z; 6 | varying vec3 vUv; 7 | uniform float u_time; 8 | uniform float u_amplitude; 9 | uniform float[64] u_data_arr; 10 | void main() { 11 | vUv = position; 12 | x = abs(position.x); 13 | y = abs(position.y); 14 | float floor_x = round(x); 15 | float floor_y = round(y); 16 | float x_multiplier = (64.0 - x) / 4.0; 17 | float y_multiplier = (64.0 - y) / 4.0; 18 | z = sin(u_data_arr[int(floor_x)] / 50.0 + u_data_arr[int(floor_y)] / 50.0) * u_amplitude; 19 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position.x, position.y, z, 1.0); 20 | } 21 | `, 22 | fragment: ` 23 | varying float x; 24 | varying float y; 25 | varying float z; 26 | varying vec3 vUv; 27 | uniform float u_time; 28 | uniform float[64] u_data_arr; 29 | void main() { 30 | gl_FragColor = vec4((u_data_arr[32])/255.0, 0, (u_data_arr[8])/255.0, 1.0); 31 | // gl_FragColor = vec4((64.0 - abs(x)) / 32.0, (32.0 - abs(y)) / 32.0, (abs(x + y) / 2.0) / 32.0, 1.0); 32 | } 33 | ` 34 | } 35 | 36 | export default shaders; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist", 4 | "noImplicitAny": true, 5 | "sourceMap": true, 6 | "declaration": true, 7 | "module": "commonjs", 8 | "strict": true, 9 | }, 10 | "include": ["src"], 11 | "exclude": [ 12 | "node_modules", 13 | "dist" 14 | ] 15 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | module.exports = ({development}) => ({ 4 | entry: "./src/index.ts", 5 | devtool: development ? "inline-source-map" : false, 6 | mode: development ? "development" : "production", 7 | module: { 8 | rules: [ 9 | { 10 | test: /\.ts/, 11 | use: ["babel-loader", "ts-loader"], 12 | exclude: /node_modules/, 13 | }, 14 | ], 15 | }, 16 | resolve: { 17 | extensions: [".ts", ".js"], 18 | }, 19 | output: { 20 | library: { 21 | name: "MusicVisualizer", 22 | type: "umd", 23 | umdNamedDefine: true, 24 | }, 25 | path: path.resolve(__dirname, "dist"), 26 | filename: "index.js", 27 | } 28 | }); --------------------------------------------------------------------------------