├── .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 |
--------------------------------------------------------------------------------
/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 |
31 |
32 |
33 |
38 |
39 |
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 3D Music Visualizer
7 |
25 |
26 |
27 |
28 |
31 |
32 |
33 |
38 |
39 |
--------------------------------------------------------------------------------
/example/many.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 3D Music Visualizer
7 |
71 |
72 |
73 |
74 |
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 | });
--------------------------------------------------------------------------------