Solshine commited on
Commit
c4df4e7
·
verified ·
1 Parent(s): 96dc654

Upload folder using huggingface_hub

Browse files
Files changed (2) hide show
  1. WaveformVisualizer.js +207 -244
  2. game.js +0 -40
WaveformVisualizer.js CHANGED
@@ -1,244 +1,207 @@
1
- import * as THREE from 'three';
2
-
3
- const vertexShader = `
4
- void main() {
5
- gl_Position = vec4(position.xy, 0.0, 1.0);
6
- }
7
- `;
8
-
9
- const fragmentShader = `
10
- precision highp float;
11
-
12
- uniform float u_time;
13
- uniform float u_amplitude;
14
- uniform vec2 u_resolution;
15
- uniform vec3 u_color;
16
- uniform float u_breathe;
17
-
18
- // Hand-driven uniforms
19
- uniform vec2 u_handPos; // -1 to 1, normalized hand position
20
- uniform float u_handSpread; // 0-1 finger spread
21
- uniform float u_wristAngle; // -1 to 1
22
- uniform float u_pitch; // 0-1 hand height (low=bass, high=treble)
23
- uniform float u_fingerGlow; // 0-1 average finger extension
24
-
25
- vec2 cmul(vec2 a, vec2 b) {
26
- return vec2(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x);
27
- }
28
-
29
- vec2 cdiv(vec2 a, vec2 b) {
30
- float d = dot(b, b);
31
- return vec2(dot(a, b), a.y * b.x - a.x * b.y) / d;
32
- }
33
-
34
- vec2 conj(vec2 z) {
35
- return vec2(z.x, -z.y);
36
- }
37
-
38
- vec2 mobius(vec2 z, vec2 a) {
39
- return cdiv(z - a, vec2(1.0, 0.0) - cmul(conj(a), z));
40
- }
41
-
42
- float hdist(vec2 z) {
43
- float r = length(z);
44
- if (r >= 1.0) return 10.0;
45
- return log((1.0 + r) / (1.0 - r));
46
- }
47
-
48
- vec2 rot(vec2 p, float a) {
49
- float c = cos(a), s = sin(a);
50
- return vec2(c * p.x - s * p.y, s * p.x + c * p.y);
51
- }
52
-
53
- void main() {
54
- vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution) / min(u_resolution.x, u_resolution.y);
55
-
56
- // Texture breathing
57
- uv *= 1.0 + 0.05 * (u_breathe - 0.5);
58
-
59
- float breathe = 1.0 + 0.15 * u_amplitude;
60
- uv *= 1.1 * breathe;
61
-
62
- float r = length(uv);
63
-
64
- if (r >= 1.0) {
65
- gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
66
- return;
67
- }
68
-
69
- // === HAND-DRIVEN GEOMETRY WARPING ===
70
-
71
- // Warp the disk toward hand position (Möbius transform)
72
- // Hand position pulls the hyperbolic space toward where your hand is
73
- vec2 handWarp = u_handPos * 0.35; // scale to stay within Poincaré disk
74
- uv = mobius(uv, handWarp);
75
-
76
- // Rotation speed driven by wrist angle + audio
77
- float rotSpeed = 0.06 + 0.04 * u_amplitude + u_wristAngle * 0.15;
78
- uv = rot(uv, u_time * rotSpeed);
79
-
80
- // Symmetry: finger spread morphs from 5-fold to 11-fold
81
- float n = 5.0 + u_handSpread * 6.0;
82
- float angleStep = 6.283185 / n;
83
-
84
- // Tiling scale shifts with pitch (hand height)
85
- float tileScale = 3.0 - u_pitch * 1.5; // lower hand = larger tiles, higher = finer
86
- float coshR = cos(3.14159265 / tileScale) / sin(3.14159265 / n);
87
- float sinhR = sqrt(max(coshR * coshR - 1.0, 0.001));
88
- float tr = sinhR / (coshR + 1.0);
89
-
90
- vec2 z = uv;
91
- float iter = 0.0;
92
-
93
- for (int i = 0; i < 40; i++) {
94
- float ang = atan(z.y, z.x);
95
- float sector = floor(ang / angleStep + 0.5) * angleStep;
96
-
97
- z = rot(z, -sector);
98
- iter += abs(sector) > 0.01 ? 1.0 : 0.0;
99
-
100
- vec2 center = vec2(tr, 0.0);
101
- vec2 w = mobius(z, center);
102
-
103
- if (length(w) >= length(z) - 0.0001) break;
104
- z = w;
105
- iter += 1.0;
106
- }
107
-
108
- float d = hdist(z);
109
-
110
- // Color palette: psychedelic, hand-tinted
111
- float t = mod(iter * 0.1 + u_time * 0.02, 1.0);
112
- vec3 col1 = vec3(0.03, 0.01, 0.08);
113
- vec3 col2 = vec3(0.02, 0.10, 0.15);
114
- vec3 col3 = vec3(0.12, 0.02, 0.10);
115
-
116
- vec3 color = mix(col1, col2, sin(iter * 0.7 + u_time * 0.1) * 0.5 + 0.5);
117
- color = mix(color, col3, sin(iter * 1.1 - u_time * 0.15) * 0.5 + 0.5);
118
-
119
- float sat = 0.85 + 0.15 * u_breathe;
120
- color *= sat;
121
-
122
- // Tint toward hand-gesture color (stronger when fingers active)
123
- float satBoost = 0.3 + 0.3 * u_fingerGlow;
124
- color = mix(color, u_color * 0.2, satBoost * 0.4);
125
-
126
- // Edge highlights — glow stronger with finger extension
127
- float edgeGlow = 0.04 - 0.02 * u_amplitude - 0.01 * u_fingerGlow;
128
- float edgeLine = 1.0 - smoothstep(0.0, max(edgeGlow, 0.005), abs(fract(d * 1.5) - 0.5) - 0.45);
129
- vec3 edgeColor = vec3(0.08, 0.12, 0.2) * (1.0 + u_fingerGlow * 2.0 + u_amplitude);
130
- color += edgeColor * edgeLine;
131
-
132
- // Sector pattern
133
- float ang = atan(z.y, z.x);
134
- float sectorPattern = smoothstep(0.02, 0.04, abs(sin(ang * n * 0.5)));
135
- color *= 0.7 + 0.3 * sectorPattern;
136
-
137
- // Audio reactive brightness
138
- color *= 0.8 + 0.25 * u_amplitude;
139
-
140
- // Hand proximity brightening — geometry glows near your hand
141
- float handDist = length(uv - u_handPos * 0.5);
142
- float handGlow = 0.15 * u_fingerGlow / (1.0 + handDist * 4.0);
143
- color += u_color * handGlow;
144
-
145
- // Disk edge fade
146
- float diskEdge = smoothstep(0.98, 0.92, r);
147
- color *= diskEdge;
148
-
149
- color = min(color, vec3(1.0));
150
-
151
- gl_FragColor = vec4(color, 0.5);
152
- }
153
- `;
154
-
155
- export class WaveformVisualizer {
156
- constructor(scene, analyser, canvasWidth, canvasHeight) {
157
- this.scene = scene;
158
- this.analyser = analyser;
159
- this.mesh = null;
160
- this.clock = new THREE.Clock();
161
- this.currentColor = new THREE.Color('#7B4394');
162
- this.targetColor = new THREE.Color('#7B4394');
163
-
164
- this.uniforms = {
165
- u_time: { value: 0.0 },
166
- u_amplitude: { value: 0.0 },
167
- u_resolution: { value: new THREE.Vector2(canvasWidth, canvasHeight) },
168
- u_color: { value: this.currentColor },
169
- u_breathe: { value: 0 },
170
- // Hand-driven uniforms
171
- u_handPos: { value: new THREE.Vector2(0, 0) },
172
- u_handSpread: { value: 0 },
173
- u_wristAngle: { value: 0 },
174
- u_pitch: { value: 0.5 },
175
- u_fingerGlow: { value: 0 }
176
- };
177
-
178
- this._createVisualizer();
179
- }
180
-
181
- _createVisualizer() {
182
- const geometry = new THREE.PlaneGeometry(2, 2);
183
- const material = new THREE.ShaderMaterial({
184
- uniforms: this.uniforms,
185
- vertexShader: vertexShader,
186
- fragmentShader: fragmentShader,
187
- transparent: true,
188
- depthWrite: false,
189
- depthTest: false
190
- });
191
-
192
- this.mesh = new THREE.Mesh(geometry, material);
193
- this.mesh.renderOrder = -1;
194
- this.mesh.frustumCulled = false;
195
- this.scene.add(this.mesh);
196
- }
197
-
198
- update() {
199
- if (!this.analyser || !this.mesh) return;
200
-
201
- this.currentColor.lerp(this.targetColor, 0.05);
202
- this.uniforms.u_time.value = this.clock.getElapsedTime();
203
-
204
- var waveformData = this.analyser.getValue();
205
- var sum = 0;
206
- for (var i = 0; i < waveformData.length; i++) {
207
- sum += waveformData[i] * waveformData[i];
208
- }
209
- var amplitude = Math.sqrt(sum / waveformData.length);
210
- this.uniforms.u_amplitude.value = amplitude;
211
- this.lastAmplitude = amplitude;
212
-
213
- var breathe = 0.5 + 0.5 * Math.sin(this.uniforms.u_time.value * 2.094);
214
- this.uniforms.u_breathe.value = breathe;
215
- }
216
-
217
- // Called from game.js with hand tracking data
218
- updateHandData(data) {
219
- if (!data) return;
220
- if (data.handPos !== undefined) {
221
- this.uniforms.u_handPos.value.set(data.handPos.x || 0, data.handPos.y || 0);
222
- }
223
- if (data.handSpread !== undefined) this.uniforms.u_handSpread.value = data.handSpread;
224
- if (data.wristAngle !== undefined) this.uniforms.u_wristAngle.value = data.wristAngle;
225
- if (data.pitch !== undefined) this.uniforms.u_pitch.value = data.pitch;
226
- if (data.fingerGlow !== undefined) this.uniforms.u_fingerGlow.value = data.fingerGlow;
227
- }
228
-
229
- updateColor(newColor) {
230
- this.targetColor.set(newColor);
231
- }
232
-
233
- updatePosition(canvasWidth, canvasHeight) {
234
- this.uniforms.u_resolution.value.set(canvasWidth, canvasHeight);
235
- }
236
-
237
- dispose() {
238
- if (this.mesh) {
239
- this.scene.remove(this.mesh);
240
- if (this.mesh.geometry) this.mesh.geometry.dispose();
241
- if (this.mesh.material) this.mesh.material.dispose();
242
- }
243
- }
244
- }
 
1
+ import * as THREE from 'three';
2
+
3
+ const vertexShader = `
4
+ void main() {
5
+ gl_Position = vec4(position.xy, 0.0, 1.0);
6
+ }
7
+ `;
8
+
9
+ const fragmentShader = `
10
+ precision highp float;
11
+
12
+ uniform float u_time;
13
+ uniform float u_amplitude;
14
+ uniform vec2 u_resolution;
15
+ uniform vec3 u_color;
16
+ uniform float u_breathe;
17
+
18
+ vec2 cmul(vec2 a, vec2 b) {
19
+ return vec2(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x);
20
+ }
21
+
22
+ vec2 cdiv(vec2 a, vec2 b) {
23
+ float d = dot(b, b);
24
+ return vec2(dot(a, b), a.y * b.x - a.x * b.y) / d;
25
+ }
26
+
27
+ vec2 conj(vec2 z) {
28
+ return vec2(z.x, -z.y);
29
+ }
30
+
31
+ vec2 mobius(vec2 z, vec2 a) {
32
+ return cdiv(z - a, vec2(1.0, 0.0) - cmul(conj(a), z));
33
+ }
34
+
35
+ float hdist(vec2 z) {
36
+ float r = length(z);
37
+ if (r >= 1.0) return 10.0;
38
+ return log((1.0 + r) / (1.0 - r));
39
+ }
40
+
41
+ vec2 rot(vec2 p, float a) {
42
+ float c = cos(a), s = sin(a);
43
+ return vec2(c * p.x - s * p.y, s * p.x + c * p.y);
44
+ }
45
+
46
+ void main() {
47
+ vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution) / min(u_resolution.x, u_resolution.y);
48
+
49
+ // Texture breathing: subtle scale pulsation independent of audio
50
+ uv *= 1.0 + 0.05 * (u_breathe - 0.5);
51
+
52
+ float breathe = 1.0 + 0.15 * u_amplitude;
53
+ uv *= 1.1 * breathe;
54
+
55
+ float r = length(uv);
56
+
57
+ if (r >= 1.0) {
58
+ gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
59
+ return;
60
+ }
61
+
62
+ float rotSpeed = 0.08 + 0.05 * u_amplitude;
63
+ uv = rot(uv, u_time * rotSpeed);
64
+
65
+ float n = 7.0;
66
+ float angleStep = 6.283185 / n;
67
+
68
+ float coshR = cos(3.14159265 / 3.0) / sin(3.14159265 / n);
69
+ float sinhR = sqrt(coshR * coshR - 1.0);
70
+ float tr = sinhR / (coshR + 1.0);
71
+
72
+ vec2 z = uv;
73
+ float iter = 0.0;
74
+
75
+ for (int i = 0; i < 40; i++) {
76
+ float ang = atan(z.y, z.x);
77
+ float sector = floor(ang / angleStep + 0.5) * angleStep;
78
+
79
+ z = rot(z, -sector);
80
+ iter += abs(sector) > 0.01 ? 1.0 : 0.0;
81
+
82
+ vec2 center = vec2(tr, 0.0);
83
+ vec2 w = mobius(z, center);
84
+
85
+ if (length(w) >= length(z) - 0.0001) break;
86
+ z = w;
87
+ iter += 1.0;
88
+ }
89
+
90
+ float d = hdist(z);
91
+
92
+ // Color palette: deep purples, teals, magentas — psychedelic but dark
93
+ float t = mod(iter * 0.1 + u_time * 0.02, 1.0);
94
+ vec3 col1 = vec3(0.03, 0.01, 0.08); // deep purple-black
95
+ vec3 col2 = vec3(0.02, 0.10, 0.15); // dark teal
96
+ vec3 col3 = vec3(0.12, 0.02, 0.10); // dark magenta
97
+
98
+ vec3 color = mix(col1, col2, sin(iter * 0.7 + u_time * 0.1) * 0.5 + 0.5);
99
+ color = mix(color, col3, sin(iter * 1.1 - u_time * 0.15) * 0.5 + 0.5);
100
+
101
+ // Breathe-modulated saturation
102
+ float sat = 0.85 + 0.15 * u_breathe;
103
+ color *= sat;
104
+
105
+ // Tint toward the hand-gesture color
106
+ float satBoost = 0.3 + 0.2 * u_amplitude;
107
+ color = mix(color, u_color * 0.15, satBoost * 0.3);
108
+
109
+ // Edge highlights with glow from amplitude
110
+ float edgeThreshold = (0.04 - 0.02 * u_amplitude) * (1.0 + 0.3 * u_breathe);
111
+ float edgeLine = 1.0 - smoothstep(0.0, edgeThreshold, abs(fract(d * 1.5) - 0.5) - 0.45);
112
+ color += vec3(0.06, 0.08, 0.14) * edgeLine * (1.0 + u_amplitude);
113
+
114
+ // Sector pattern
115
+ float ang = atan(z.y, z.x);
116
+ float sectorPattern = smoothstep(0.02, 0.04, abs(sin(ang * n * 0.5)));
117
+ color *= 0.7 + 0.3 * sectorPattern;
118
+
119
+ // Audio reactive brightness
120
+ color *= 0.8 + 0.25 * u_amplitude;
121
+
122
+ // Disk edge fade
123
+ float diskEdge = smoothstep(0.98, 0.92, r);
124
+ color *= diskEdge;
125
+
126
+ color = min(color, vec3(1.0));
127
+
128
+ gl_FragColor = vec4(color, 0.45);
129
+ }
130
+ `;
131
+
132
+ export class WaveformVisualizer {
133
+ constructor(scene, analyser, canvasWidth, canvasHeight) {
134
+ this.scene = scene;
135
+ this.analyser = analyser;
136
+ this.mesh = null;
137
+ this.clock = new THREE.Clock();
138
+ this.currentColor = new THREE.Color('#7B4394');
139
+ this.targetColor = new THREE.Color('#7B4394');
140
+
141
+ this.uniforms = {
142
+ u_time: { value: 0.0 },
143
+ u_amplitude: { value: 0.0 },
144
+ u_resolution: { value: new THREE.Vector2(canvasWidth, canvasHeight) },
145
+ u_color: { value: this.currentColor },
146
+ u_breathe: { value: 0 }
147
+ };
148
+
149
+ this._createVisualizer();
150
+ }
151
+
152
+ _createVisualizer() {
153
+ const geometry = new THREE.PlaneGeometry(2, 2);
154
+ const material = new THREE.ShaderMaterial({
155
+ uniforms: this.uniforms,
156
+ vertexShader: vertexShader,
157
+ fragmentShader: fragmentShader,
158
+ transparent: true,
159
+ depthWrite: false,
160
+ depthTest: false
161
+ });
162
+
163
+ this.mesh = new THREE.Mesh(geometry, material);
164
+ this.mesh.renderOrder = -1;
165
+ this.mesh.frustumCulled = false;
166
+ this.scene.add(this.mesh);
167
+ }
168
+
169
+ update() {
170
+ if (!this.analyser || !this.mesh) return;
171
+
172
+ // Interpolate color
173
+ this.currentColor.lerp(this.targetColor, 0.05);
174
+
175
+ // Update time
176
+ this.uniforms.u_time.value = this.clock.getElapsedTime();
177
+
178
+ // Compute RMS amplitude from analyser
179
+ var waveformData = this.analyser.getValue();
180
+ var sum = 0;
181
+ for (var i = 0; i < waveformData.length; i++) {
182
+ sum += waveformData[i] * waveformData[i];
183
+ }
184
+ var amplitude = Math.sqrt(sum / waveformData.length);
185
+ this.uniforms.u_amplitude.value = amplitude;
186
+ this.lastAmplitude = amplitude;
187
+
188
+ var breathe = 0.5 + 0.5 * Math.sin(this.uniforms.u_time.value * 2.094); // ~3 second cycle
189
+ this.uniforms.u_breathe.value = breathe;
190
+ }
191
+
192
+ updateColor(newColor) {
193
+ this.targetColor.set(newColor);
194
+ }
195
+
196
+ updatePosition(canvasWidth, canvasHeight) {
197
+ this.uniforms.u_resolution.value.set(canvasWidth, canvasHeight);
198
+ }
199
+
200
+ dispose() {
201
+ if (this.mesh) {
202
+ this.scene.remove(this.mesh);
203
+ if (this.mesh.geometry) this.mesh.geometry.dispose();
204
+ if (this.mesh.material) this.mesh.material.dispose();
205
+ }
206
+ }
207
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
game.js CHANGED
@@ -1584,46 +1584,6 @@ export var Game = /*#__PURE__*/ function() {
1584
  this._updateBeatIndicator();
1585
  if (this.waveformVisualizer) {
1586
  this.waveformVisualizer.update();
1587
- // Pipe hand data into the hyperbolic geometry
1588
- var synthHand = this.hands[0];
1589
- if (synthHand && synthHand.landmarks) {
1590
- var palm = synthHand.landmarks[9];
1591
- // Normalize hand pos to -1..1 (mirrored X)
1592
- var hx = -(palm.x * 2 - 1);
1593
- var hy = -(palm.y * 2 - 1);
1594
- // Compute finger glow (avg distance from palm)
1595
- var fglow = 0;
1596
- var tipIndices = [8, 12, 16, 20];
1597
- for (var ti = 0; ti < tipIndices.length; ti++) {
1598
- var tip = synthHand.landmarks[tipIndices[ti]];
1599
- var fdx = tip.x - palm.x, fdy = tip.y - palm.y;
1600
- fglow += Math.sqrt(fdx*fdx + fdy*fdy);
1601
- }
1602
- var palmDist = Math.abs(synthHand.landmarks[0].y - palm.y) || 0.05;
1603
- fglow = Math.min(1, (fglow / 4) / (palmDist * 2));
1604
- // Pitch from Y position (0=bottom, 1=top)
1605
- var pitch = 1 - palm.y;
1606
- // Hand spread
1607
- var tips = [synthHand.landmarks[4], synthHand.landmarks[8], synthHand.landmarks[12], synthHand.landmarks[16], synthHand.landmarks[20]];
1608
- var spreadSum = 0;
1609
- for (var si = 0; si < tips.length - 1; si++) {
1610
- var sdx = tips[si].x - tips[si+1].x, sdy = tips[si].y - tips[si+1].y;
1611
- spreadSum += Math.sqrt(sdx*sdx + sdy*sdy);
1612
- }
1613
- var spread = Math.min(1, (spreadSum / 4) / (palmDist * 2));
1614
- // Wrist angle
1615
- var wdx = palm.x - synthHand.landmarks[0].x;
1616
- var wdy = palm.y - synthHand.landmarks[0].y;
1617
- var wAngle = Math.max(-1, Math.min(1, Math.atan2(wdx, -wdy) / (Math.PI / 2)));
1618
-
1619
- this.waveformVisualizer.updateHandData({
1620
- handPos: { x: hx, y: hy },
1621
- handSpread: spread,
1622
- wristAngle: wAngle,
1623
- pitch: pitch,
1624
- fingerGlow: fglow
1625
- });
1626
- }
1627
  }
1628
  if (this.mandalaVisualizer) {
1629
  var mandalaHands = this.hands.map(function(h) { return h.points3D || null; }).filter(Boolean);
 
1584
  this._updateBeatIndicator();
1585
  if (this.waveformVisualizer) {
1586
  this.waveformVisualizer.update();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1587
  }
1588
  if (this.mandalaVisualizer) {
1589
  var mandalaHands = this.hands.map(function(h) { return h.points3D || null; }).filter(Boolean);