Tingchenliang commited on
Commit
d85697e
·
verified ·
1 Parent(s): be7c986

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +530 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Voice Dive
3
- emoji: 👁
4
- colorFrom: yellow
5
- colorTo: red
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: voice-dive
3
+ emoji: 🐳
4
+ colorFrom: gray
5
+ colorTo: yellow
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,530 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Voice-Controlled Free Diving Game</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/p5.min.js"></script>
9
+ <style>
10
+ .water-overlay {
11
+ position: absolute;
12
+ top: 0;
13
+ left: 0;
14
+ width: 100%;
15
+ height: 100%;
16
+ background: linear-gradient(to bottom, rgba(0, 120, 255, 0.2), rgba(0, 60, 200, 0.6));
17
+ pointer-events: none;
18
+ z-index: 10;
19
+ }
20
+
21
+ .volume-indicator {
22
+ position: absolute;
23
+ bottom: 20px;
24
+ left: 50%;
25
+ transform: translateX(-50%);
26
+ width: 80%;
27
+ height: 10px;
28
+ background: rgba(255, 255, 255, 0.3);
29
+ border-radius: 5px;
30
+ overflow: hidden;
31
+ z-index: 100;
32
+ }
33
+
34
+ .volume-level {
35
+ height: 100%;
36
+ background: linear-gradient(to right, #4facfe, #00f2fe);
37
+ width: 0%;
38
+ transition: width 0.1s;
39
+ }
40
+
41
+ .depth-meter {
42
+ position: absolute;
43
+ right: 20px;
44
+ top: 50%;
45
+ transform: translateY(-50%);
46
+ width: 30px;
47
+ height: 200px;
48
+ background: rgba(0, 0, 0, 0.5);
49
+ border-radius: 15px;
50
+ overflow: hidden;
51
+ z-index: 100;
52
+ }
53
+
54
+ .depth-fill {
55
+ position: absolute;
56
+ bottom: 0;
57
+ width: 100%;
58
+ height: 0%;
59
+ background: linear-gradient(to top, #4facfe, #00f2fe);
60
+ transition: height 0.2s;
61
+ }
62
+
63
+ .depth-label {
64
+ position: absolute;
65
+ right: 60px;
66
+ top: 50%;
67
+ transform: translateY(-50%);
68
+ color: white;
69
+ font-size: 24px;
70
+ text-shadow: 0 0 5px black;
71
+ z-index: 100;
72
+ }
73
+
74
+ .bubble {
75
+ position: absolute;
76
+ background: rgba(255, 255, 255, 0.6);
77
+ border-radius: 50%;
78
+ filter: blur(1px);
79
+ z-index: 5;
80
+ }
81
+
82
+ .fish {
83
+ position: absolute;
84
+ z-index: 8;
85
+ transition: transform 0.5s;
86
+ }
87
+
88
+ .game-container {
89
+ position: relative;
90
+ width: 100vw;
91
+ height: 100vh;
92
+ overflow: hidden;
93
+ }
94
+
95
+ #videoElement {
96
+ position: absolute;
97
+ top: 0;
98
+ left: 0;
99
+ width: 100%;
100
+ height: 100%;
101
+ object-fit: cover;
102
+ z-index: 1;
103
+ }
104
+
105
+ #canvas {
106
+ position: absolute;
107
+ top: 0;
108
+ left: 0;
109
+ z-index: 2;
110
+ }
111
+
112
+ .start-screen {
113
+ position: absolute;
114
+ top: 0;
115
+ left: 0;
116
+ width: 100%;
117
+ height: 100%;
118
+ background: rgba(0, 0, 0, 0.7);
119
+ display: flex;
120
+ flex-direction: column;
121
+ justify-content: center;
122
+ align-items: center;
123
+ z-index: 200;
124
+ color: white;
125
+ }
126
+
127
+ .start-btn {
128
+ margin-top: 20px;
129
+ padding: 15px 30px;
130
+ background: linear-gradient(to right, #4facfe, #00f2fe);
131
+ border: none;
132
+ border-radius: 30px;
133
+ color: white;
134
+ font-size: 18px;
135
+ cursor: pointer;
136
+ box-shadow: 0 0 15px rgba(0, 242, 254, 0.5);
137
+ transition: all 0.3s;
138
+ }
139
+
140
+ .start-btn:hover {
141
+ transform: scale(1.05);
142
+ box-shadow: 0 0 20px rgba(0, 242, 254, 0.8);
143
+ }
144
+
145
+ .title {
146
+ font-size: 3rem;
147
+ margin-bottom: 20px;
148
+ text-shadow: 0 0 10px #00f2fe;
149
+ background: linear-gradient(to right, #4facfe, #00f2fe);
150
+ -webkit-background-clip: text;
151
+ -webkit-text-fill-color: transparent;
152
+ }
153
+
154
+ .instructions {
155
+ text-align: center;
156
+ max-width: 80%;
157
+ margin-bottom: 30px;
158
+ line-height: 1.6;
159
+ }
160
+
161
+ .silence-warning {
162
+ position: absolute;
163
+ bottom: 50px;
164
+ left: 50%;
165
+ transform: translateX(-50%);
166
+ background: rgba(0, 0, 0, 0.7);
167
+ color: white;
168
+ padding: 10px 20px;
169
+ border-radius: 20px;
170
+ z-index: 150;
171
+ opacity: 0;
172
+ transition: opacity 0.3s;
173
+ }
174
+
175
+ .volume-threshold {
176
+ position: absolute;
177
+ left: 0;
178
+ width: 15%;
179
+ height: 100%;
180
+ background-color: rgba(255, 255, 255, 0.2);
181
+ z-index: 101;
182
+ }
183
+ </style>
184
+ </head>
185
+ <body class="bg-black overflow-hidden">
186
+ <div class="game-container">
187
+ <video id="videoElement" autoplay playsinline></video>
188
+ <canvas id="canvas"></canvas>
189
+ <div class="water-overlay"></div>
190
+
191
+ <div class="volume-indicator">
192
+ <div class="volume-threshold"></div>
193
+ <div class="volume-level" id="volumeLevel"></div>
194
+ </div>
195
+
196
+ <div class="depth-meter">
197
+ <div class="depth-fill" id="depthFill"></div>
198
+ </div>
199
+
200
+ <div class="depth-label" id="depthLabel">0m</div>
201
+
202
+ <div class="silence-warning" id="silenceWarning">
203
+ Take a deep breath and make noise to dive deeper!
204
+ </div>
205
+
206
+ <div class="start-screen" id="startScreen">
207
+ <h1 class="title">VOICE DIVE</h1>
208
+ <p class="instructions">
209
+ Welcome to Voice Dive! Use your voice to control your descent into the deep blue.<br>
210
+ The louder you are, the faster you'll dive. Try to reach the deepest point!<br>
211
+ Stay silent and you'll start floating back up. Make sure to allow camera and microphone access when prompted.
212
+ </p>
213
+ <button class="start-btn" id="startBtn">START DIVING</button>
214
+ </div>
215
+ </div>
216
+
217
+ <script>
218
+ // Game variables
219
+ let video;
220
+ let canvas;
221
+ let ctx;
222
+ let bubbles = [];
223
+ let fishes = [];
224
+ let depth = 0;
225
+ let maxDepth = 1000; // in meters
226
+ let volume = 0;
227
+ let isPlaying = false;
228
+ let animationId;
229
+ let lastBubbleTime = 0;
230
+ let silenceTimer = 0;
231
+ let isSilent = false;
232
+
233
+ // Fish images
234
+ const fishImages = [
235
+ '🐠', '🐟', '🐡', '🦈', '🐋', '🦑', '🐙', '🦀', '🦐', '🐚'
236
+ ];
237
+
238
+ // Initialize the game
239
+ function initGame() {
240
+ video = document.getElementById('videoElement');
241
+ canvas = document.getElementById('canvas');
242
+ ctx = canvas.getContext('2d');
243
+
244
+ // Set canvas size
245
+ canvas.width = window.innerWidth;
246
+ canvas.height = window.innerHeight;
247
+
248
+ // Setup camera
249
+ setupCamera();
250
+
251
+ // Create initial fishes
252
+ createFishes(10);
253
+
254
+ // Start game loop
255
+ gameLoop();
256
+ }
257
+
258
+ // Setup camera
259
+ function setupCamera() {
260
+ if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
261
+ navigator.mediaDevices.getUserMedia({
262
+ video: {
263
+ facingMode: "user",
264
+ width: { ideal: 1280 },
265
+ height: { ideal: 720 }
266
+ }
267
+ })
268
+ .then(function(stream) {
269
+ video.srcObject = stream;
270
+ })
271
+ .catch(function(error) {
272
+ console.error("Camera error: ", error);
273
+ });
274
+ }
275
+ }
276
+
277
+ // Setup microphone
278
+ function setupMicrophone() {
279
+ const audioContext = new (window.AudioContext || window.webkitAudioContext)();
280
+ const analyser = audioContext.createAnalyser();
281
+ analyser.fftSize = 32;
282
+
283
+ navigator.mediaDevices.getUserMedia({ audio: true, video: false })
284
+ .then(function(stream) {
285
+ const microphone = audioContext.createMediaStreamSource(stream);
286
+ microphone.connect(analyser);
287
+
288
+ const dataArray = new Uint8Array(analyser.frequencyBinCount);
289
+
290
+ function analyzeVolume() {
291
+ analyser.getByteFrequencyData(dataArray);
292
+ let sum = 0;
293
+ for (let i = 0; i < dataArray.length; i++) {
294
+ sum += dataArray[i];
295
+ }
296
+ volume = sum / dataArray.length;
297
+
298
+ // Check for no breath (volume below 15%)
299
+ if (volume < 15) {
300
+ silenceTimer++;
301
+ if (silenceTimer > 30 && !isSilent) { // 0.5 seconds of silence
302
+ isSilent = true;
303
+ document.getElementById('silenceWarning').style.opacity = '1';
304
+ }
305
+ } else {
306
+ silenceTimer = 0;
307
+ if (isSilent) {
308
+ isSilent = false;
309
+ document.getElementById('silenceWarning').style.opacity = '0';
310
+ }
311
+ }
312
+
313
+ // Update volume indicator
314
+ document.getElementById('volumeLevel').style.width = `${volume}%`;
315
+
316
+ if (isPlaying) {
317
+ // Change depth based on volume
318
+ if (volume >= 15) { // Normal breathing - descend
319
+ depth += volume / 15; // Adjusted for better control
320
+ } else { // No breath - ascend
321
+ depth = Math.max(0, depth - 1.5); // Faster ascent
322
+ }
323
+
324
+ // Update depth meter
325
+ const depthPercentage = Math.min(100, (depth / maxDepth) * 100);
326
+ document.getElementById('depthFill').style.height = `${depthPercentage}%`;
327
+ document.getElementById('depthLabel').textContent = `${Math.floor(depth)}m`;
328
+
329
+ // Create bubbles when breathing out
330
+ const now = Date.now();
331
+ if (volume >= 15 && now - lastBubbleTime > 100 - volume) {
332
+ createBubbles();
333
+ lastBubbleTime = now;
334
+ }
335
+ }
336
+
337
+ animationId = requestAnimationFrame(analyzeVolume);
338
+ }
339
+
340
+ analyzeVolume();
341
+ })
342
+ .catch(function(error) {
343
+ console.error("Microphone error: ", error);
344
+ });
345
+ }
346
+
347
+ // Create bubbles with physics
348
+ function createBubbles() {
349
+ const bubbleCount = Math.floor(1 + volume / 15);
350
+
351
+ for (let i = 0; i < bubbleCount; i++) {
352
+ const size = 5 + Math.random() * 15;
353
+ const x = window.innerWidth / 2 + (Math.random() - 0.5) * 200;
354
+ const y = window.innerHeight - 50 + (Math.random() - 0.5) * 20;
355
+ const baseSpeed = 0.5 + Math.random() * 2 + volume / 50;
356
+
357
+ // Physics properties
358
+ const horizontalSpeed = (Math.random() - 0.5) * 0.5;
359
+ const rotationSpeed = (Math.random() - 0.5) * 0.02;
360
+ const rotationRadius = 5 + Math.random() * 20;
361
+
362
+ bubbles.push({
363
+ x,
364
+ y,
365
+ size,
366
+ baseSpeed,
367
+ horizontalSpeed,
368
+ rotationSpeed,
369
+ rotationRadius,
370
+ angle: Math.random() * Math.PI * 2,
371
+ opacity: 0.3 + Math.random() * 0.7,
372
+ time: 0
373
+ });
374
+ }
375
+ }
376
+
377
+ // Create fishes
378
+ function createFishes(count) {
379
+ for (let i = 0; i < count; i++) {
380
+ const fishType = fishImages[Math.floor(Math.random() * fishImages.length)];
381
+ const size = 30 + Math.random() * 50;
382
+ const x = Math.random() * window.innerWidth;
383
+ const y = Math.random() * window.innerHeight;
384
+ const speed = 0.1 + Math.random() * 0.5;
385
+ const direction = Math.random() > 0.5 ? 1 : -1;
386
+
387
+ fishes.push({
388
+ type: fishType,
389
+ x,
390
+ y,
391
+ size,
392
+ speed,
393
+ direction,
394
+ startX: x,
395
+ startY: y,
396
+ amplitude: 50 + Math.random() * 100,
397
+ frequency: 0.01 + Math.random() * 0.03
398
+ });
399
+ }
400
+ }
401
+
402
+ // Update bubbles with physics
403
+ function updateBubbles() {
404
+ for (let i = bubbles.length - 1; i >= 0; i--) {
405
+ const bubble = bubbles[i];
406
+
407
+ // Update physics properties
408
+ bubble.time += 0.1;
409
+ bubble.angle += bubble.rotationSpeed;
410
+
411
+ // Calculate movement
412
+ const verticalSpeed = bubble.baseSpeed * (1 + 0.2 * Math.sin(bubble.time));
413
+ bubble.y -= verticalSpeed;
414
+ bubble.x += bubble.horizontalSpeed + Math.sin(bubble.angle) * bubble.rotationRadius;
415
+
416
+ // Remove bubbles that are off screen
417
+ if (bubble.y + bubble.size < 0 ||
418
+ bubble.x + bubble.size < 0 ||
419
+ bubble.x - bubble.size > window.innerWidth) {
420
+ bubbles.splice(i, 1);
421
+ }
422
+ }
423
+ }
424
+
425
+ // Update fishes
426
+ function updateFishes() {
427
+ fishes.forEach(fish => {
428
+ fish.x += fish.speed * fish.direction;
429
+ fish.y = fish.startY + Math.sin(fish.x * fish.frequency) * fish.amplitude;
430
+
431
+ // Reverse direction at screen edges
432
+ if (fish.x > window.innerWidth + fish.size) {
433
+ fish.x = -fish.size;
434
+ } else if (fish.x < -fish.size) {
435
+ fish.x = window.innerWidth + fish.size;
436
+ }
437
+ });
438
+ }
439
+
440
+ // Draw video frame
441
+ function drawVideo() {
442
+ ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
443
+ }
444
+
445
+ // Draw bubbles with physics effects
446
+ function drawBubbles() {
447
+ bubbles.forEach(bubble => {
448
+ // Add slight distortion based on movement
449
+ const distortionX = Math.sin(bubble.time * 2) * bubble.size * 0.1;
450
+ const distortionY = Math.cos(bubble.time * 1.5) * bubble.size * 0.1;
451
+
452
+ ctx.beginPath();
453
+ ctx.ellipse(
454
+ bubble.x + distortionX,
455
+ bubble.y + distortionY,
456
+ bubble.size,
457
+ bubble.size * (0.8 + 0.2 * Math.sin(bubble.time)),
458
+ 0, 0, Math.PI * 2
459
+ );
460
+ ctx.fillStyle = `rgba(255, 255, 255, ${bubble.opacity})`;
461
+ ctx.fill();
462
+
463
+ // Add highlight for more realism
464
+ ctx.beginPath();
465
+ ctx.ellipse(
466
+ bubble.x - bubble.size * 0.3 + distortionX,
467
+ bubble.y - bubble.size * 0.3 + distortionY,
468
+ bubble.size * 0.3,
469
+ bubble.size * 0.15,
470
+ 0, 0, Math.PI * 2
471
+ );
472
+ ctx.fillStyle = `rgba(255, 255, 255, ${bubble.opacity * 0.8})`;
473
+ ctx.fill();
474
+ });
475
+ }
476
+
477
+ // Draw fishes
478
+ function drawFishes() {
479
+ ctx.font = '30px Arial';
480
+ fishes.forEach(fish => {
481
+ ctx.save();
482
+ ctx.translate(fish.x, fish.y);
483
+ if (fish.direction < 0) {
484
+ ctx.scale(-1, 1);
485
+ }
486
+ ctx.fillText(fish.type, 0, 0);
487
+ ctx.restore();
488
+ });
489
+ }
490
+
491
+ // Game loop
492
+ function gameLoop() {
493
+ if (!isPlaying) {
494
+ requestAnimationFrame(gameLoop);
495
+ return;
496
+ }
497
+
498
+ // Clear canvas
499
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
500
+
501
+ // Draw elements
502
+ drawVideo();
503
+ drawBubbles();
504
+ drawFishes();
505
+
506
+ // Update elements
507
+ updateBubbles();
508
+ updateFishes();
509
+
510
+ requestAnimationFrame(gameLoop);
511
+ }
512
+
513
+ // Start game
514
+ document.getElementById('startBtn').addEventListener('click', function() {
515
+ document.getElementById('startScreen').style.display = 'none';
516
+ isPlaying = true;
517
+ setupMicrophone();
518
+ });
519
+
520
+ // Handle window resize
521
+ window.addEventListener('resize', function() {
522
+ canvas.width = window.innerWidth;
523
+ canvas.height = window.innerHeight;
524
+ });
525
+
526
+ // Initialize game when page loads
527
+ window.addEventListener('load', initGame);
528
+ </script>
529
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Tingchenliang/voice-dive" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
530
+ </html>