r0cketboy commited on
Commit
b323c50
·
verified ·
1 Parent(s): 762bcc4

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +242 -323
index.html CHANGED
@@ -4,7 +4,7 @@
4
  <head>
5
  <meta charset="UTF-8">
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>Rhythmic Floating Orbs</title>
8
  <style>
9
  * {
10
  margin: 0;
@@ -66,51 +66,20 @@
66
  height: 100%;
67
  }
68
 
69
- .controls {
70
- position: fixed;
71
- bottom: 2rem;
72
- left: 50%;
73
- transform: translateX(-50%);
74
- display: flex;
75
- gap: 1rem;
76
- z-index: 1000;
77
- flex-wrap: wrap;
78
- justify-content: center;
79
- padding: 0 1rem;
80
- }
81
-
82
- .control-btn {
83
- padding: 0.8rem 1.5rem;
84
- background: rgba(255, 255, 255, 0.1);
85
- border: 1px solid rgba(255, 255, 255, 0.3);
86
- color: #fff;
87
- border-radius: 25px;
88
- cursor: pointer;
89
- font-size: 0.9rem;
90
- transition: all 0.3s ease;
91
- backdrop-filter: blur(10px);
92
- }
93
-
94
- .control-btn:hover {
95
- background: rgba(255, 255, 255, 0.2);
96
- transform: translateY(-2px);
97
- box-shadow: 0 5px 20px rgba(255, 255, 255, 0.2);
98
- }
99
-
100
- .control-btn.active {
101
- background: rgba(255, 255, 255, 0.3);
102
- border-color: rgba(255, 255, 255, 0.6);
103
- }
104
-
105
  .info {
106
  position: fixed;
107
- bottom: 6rem;
108
  left: 50%;
109
  transform: translateX(-50%);
110
  color: rgba(255, 255, 255, 0.5);
111
- font-size: 0.8rem;
112
  text-align: center;
113
  z-index: 1000;
 
 
 
 
 
114
  }
115
 
116
  @media (max-width: 768px) {
@@ -124,19 +93,10 @@
124
  font-size: 1.2rem;
125
  }
126
 
127
- .controls {
128
  bottom: 1rem;
129
- }
130
-
131
- .control-btn {
132
  padding: 0.6rem 1rem;
133
- font-size: 0.8rem;
134
- }
135
-
136
- .info {
137
- bottom: 5rem;
138
- font-size: 0.7rem;
139
- padding: 0 1rem;
140
  }
141
  }
142
  </style>
@@ -144,7 +104,7 @@
144
 
145
  <body>
146
  <header>
147
- <h1 class="title"> Rhythmic Orbs</h1>
148
  <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="built-with">Built with anycoder</a>
149
  </header>
150
 
@@ -152,302 +112,261 @@
152
 
153
  <p class="info">Move your mouse to interact with the orbs • Click to create ripples</p>
154
 
155
- <div class="controls">
156
- <button class="control-btn active" data-mode="wave">Wave</button>
157
- <button class="control-btn" data-mode="pulse">Pulse</button>
158
- <button class="control-btn" data-mode="spiral">Spiral</button>
159
- <button class="control-btn" data-mode="chaos">Chaos</button>
160
- </div>
161
-
162
  <script>
163
  const canvas = document.getElementById('canvas');
164
- const ctx = canvas.getContext('2d');
165
-
166
- let width, height;
167
- let orbs = [];
168
- let mouse = { x: null, y: null, radius: 150 };
169
- let ripples = [];
170
- let time = 0;
171
- let mode = 'wave';
172
-
173
- const LAYERS = 5;
174
- const ORBS_PER_LAYER = 30;
175
-
176
- function resize() {
177
- width = canvas.width = window.innerWidth;
178
- height = canvas.height = window.innerHeight;
179
- initOrbs();
180
- }
181
 
182
- class Orb {
183
- constructor(layer) {
184
- this.layer = layer;
185
- this.layerFactor = (layer + 1) / LAYERS;
186
- // Increased base radius from 3-11 to 8-24
187
- this.baseRadius = 8 + Math.random() * 16 * this.layerFactor;
188
- this.radius = this.baseRadius;
189
- this.x = Math.random() * width;
190
- this.y = Math.random() * height;
191
- this.baseX = this.x;
192
- this.baseY = this.y;
193
- this.vx = 0;
194
- this.vy = 0;
195
- this.phase = Math.random() * Math.PI * 2;
196
- this.speed = 0.5 + Math.random() * 1.5;
197
- this.amplitude = 20 + Math.random() * 40;
198
- this.glowIntensity = 0.3 + Math.random() * 0.7;
199
- this.pulseSpeed = 0.02 + Math.random() * 0.03;
200
- this.angle = Math.random() * Math.PI * 2;
201
- this.orbitRadius = 50 + Math.random() * 100;
202
- this.orbitSpeed = 0.01 + Math.random() * 0.02;
203
- }
204
-
205
- update() {
206
- const layerSpeed = this.layerFactor * 0.5 + 0.5;
207
-
208
- switch(mode) {
209
- case 'wave':
210
- this.x = this.baseX + Math.sin(time * this.speed * 0.02 + this.phase) * this.amplitude;
211
- this.y = this.baseY + Math.cos(time * this.speed * 0.015 + this.phase) * this.amplitude * 0.5;
212
- break;
213
- case 'pulse':
214
- const pulseScale = 1 + Math.sin(time * 0.03 + this.phase) * 0.3;
215
- const centerX = width / 2;
216
- const centerY = height / 2;
217
- const dx = this.baseX - centerX;
218
- const dy = this.baseY - centerY;
219
- this.x = centerX + dx * pulseScale;
220
- this.y = centerY + dy * pulseScale;
221
- break;
222
- case 'spiral':
223
- this.angle += this.orbitSpeed * layerSpeed;
224
- const spiralRadius = this.orbitRadius + Math.sin(time * 0.02) * 30;
225
- this.x = this.baseX + Math.cos(this.angle) * spiralRadius * 0.5;
226
- this.y = this.baseY + Math.sin(this.angle) * spiralRadius * 0.5;
227
- break;
228
- case 'chaos':
229
- this.x += Math.sin(time * 0.05 + this.phase) * 2;
230
- this.y += Math.cos(time * 0.04 + this.phase * 1.5) * 2;
231
- if (this.x < 0) this.x = width;
232
- if (this.x > width) this.x = 0;
233
- if (this.y < 0) this.y = height;
234
- if (this.y > height) this.y = 0;
235
- break;
236
- }
237
-
238
- // Mouse interaction
239
- if (mouse.x !== null && mouse.y !== null) {
240
- const dx = this.x - mouse.x;
241
- const dy = this.y - mouse.y;
242
- const distance = Math.sqrt(dx * dx + dy * dy);
243
-
244
- if (distance < mouse.radius) {
245
- const force = (mouse.radius - distance) / mouse.radius;
246
- const angle = Math.atan2(dy, dx);
247
- this.vx += Math.cos(angle) * force * 3 * this.layerFactor;
248
- this.vy += Math.sin(angle) * force * 3 * this.layerFactor;
249
- this.radius = this.baseRadius * (1 + force * 0.5);
250
- } else {
251
- this.radius += (this.baseRadius - this.radius) * 0.1;
252
- }
253
- }
254
-
255
- // Apply velocity with damping
256
- this.x += this.vx;
257
- this.y += this.vy;
258
- this.vx *= 0.95;
259
- this.vy *= 0.95;
260
-
261
- // Ripple interaction
262
- ripples.forEach(ripple => {
263
- const dx = this.x - ripple.x;
264
- const dy = this.y - ripple.y;
265
- const distance = Math.sqrt(dx * dx + dy * dy);
266
-
267
- if (Math.abs(distance - ripple.radius) < 50) {
268
- const force = (1 - Math.abs(distance - ripple.radius) / 50) * ripple.strength;
269
- const angle = Math.atan2(dy, dx);
270
- this.vx += Math.cos(angle) * force * 2;
271
- this.vy += Math.sin(angle) * force * 2;
272
- }
273
- });
274
-
275
- // Pulsing glow
276
- this.currentGlow = this.glowIntensity * (0.7 + Math.sin(time * this.pulseSpeed + this.phase) * 0.3);
277
- }
278
-
279
- draw() {
280
- const alpha = 0.3 + this.layerFactor * 0.7;
281
- const glowSize = this.radius * (3 + this.currentGlow * 2);
282
-
283
- // Outer glow
284
- const gradient = ctx.createRadialGradient(
285
- this.x, this.y, 0,
286
- this.x, this.y, glowSize
287
- );
288
- gradient.addColorStop(0, `rgba(255, 255, 255, ${alpha * this.currentGlow})`);
289
- gradient.addColorStop(0.3, `rgba(200, 220, 255, ${alpha * this.currentGlow * 0.5})`);
290
- gradient.addColorStop(0.6, `rgba(150, 180, 255, ${alpha * this.currentGlow * 0.2})`);
291
- gradient.addColorStop(1, 'rgba(100, 150, 255, 0)');
292
-
293
- ctx.beginPath();
294
- ctx.arc(this.x, this.y, glowSize, 0, Math.PI * 2);
295
- ctx.fillStyle = gradient;
296
- ctx.fill();
297
-
298
- // Core
299
- const coreGradient = ctx.createRadialGradient(
300
- this.x - this.radius * 0.3, this.y - this.radius * 0.3, 0,
301
- this.x, this.y, this.radius
302
- );
303
- coreGradient.addColorStop(0, `rgba(255, 255, 255, ${alpha})`);
304
- coreGradient.addColorStop(0.5, `rgba(230, 240, 255, ${alpha * 0.9})`);
305
- coreGradient.addColorStop(1, `rgba(200, 220, 255, ${alpha * 0.7})`);
306
-
307
- ctx.beginPath();
308
- ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
309
- ctx.fillStyle = coreGradient;
310
- ctx.fill();
311
- }
312
- }
313
 
314
- class Ripple {
315
- constructor(x, y) {
316
- this.x = x;
317
- this.y = y;
318
- this.radius = 0;
319
- this.maxRadius = 300;
320
- this.strength = 1;
321
- this.speed = 8;
322
- }
323
-
324
- update() {
325
- this.radius += this.speed;
326
- this.strength = 1 - (this.radius / this.maxRadius);
327
- }
328
-
329
- draw() {
330
- ctx.beginPath();
331
- ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
332
- ctx.strokeStyle = `rgba(255, 255, 255, ${this.strength * 0.3})`;
333
- ctx.lineWidth = 2;
334
- ctx.stroke();
335
- }
336
-
337
- isDead() {
338
- return this.radius >= this.maxRadius;
339
- }
340
- }
341
 
342
- function initOrbs() {
343
- orbs = [];
344
- for (let layer = 0; layer < LAYERS; layer++) {
345
- for (let i = 0; i < ORBS_PER_LAYER; i++) {
346
- orbs.push(new Orb(layer));
347
- }
348
- }
349
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
350
 
351
- function drawConnections() {
352
- orbs.forEach((orb, i) => {
353
- orbs.slice(i + 1).forEach(other => {
354
- if (orb.layer !== other.layer) return;
355
-
356
- const dx = orb.x - other.x;
357
- const dy = orb.y - other.y;
358
- const distance = Math.sqrt(dx * dx + dy * dy);
359
- const maxDistance = 100 + orb.layer * 20;
360
-
361
- if (distance < maxDistance) {
362
- const alpha = (1 - distance / maxDistance) * 0.15 * orb.layerFactor;
363
- ctx.beginPath();
364
- ctx.moveTo(orb.x, orb.y);
365
- ctx.lineTo(other.x, other.y);
366
- ctx.strokeStyle = `rgba(255, 255, 255, ${alpha})`;
367
- ctx.lineWidth = 0.5;
368
- ctx.stroke();
369
- }
370
- });
371
- });
 
 
 
 
372
  }
373
 
374
- function animate() {
375
- ctx.fillStyle = 'rgba(10, 10, 15, 0.1)';
376
- ctx.fillRect(0, 0, width, height);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
377
 
378
- time++;
 
 
379
 
380
- // Update and draw ripples
381
- ripples = ripples.filter(ripple => !ripple.isDead());
382
- ripples.forEach(ripple => {
383
- ripple.update();
384
- ripple.draw();
385
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
386
 
387
- // Draw connections first (behind orbs)
388
- drawConnections();
 
 
 
 
 
 
 
389
 
390
- // Sort orbs by layer for proper depth rendering
391
- orbs.sort((a, b) => a.layer - b.layer);
 
 
392
 
393
- // Update and draw orbs
394
- orbs.forEach(orb => {
395
- orb.update();
396
- orb.draw();
397
- });
 
 
398
 
399
- requestAnimationFrame(animate);
400
- }
 
 
401
 
402
- // Event listeners
403
- window.addEventListener('resize', resize);
 
 
 
 
 
 
404
 
405
- canvas.addEventListener('mousemove', (e) => {
406
- mouse.x = e.clientX;
407
- mouse.y = e.clientY;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
408
  });
 
 
409
 
410
- canvas.addEventListener('mouseleave', () => {
411
- mouse.x = null;
412
- mouse.y = null;
413
- });
414
 
415
- canvas.addEventListener('click', (e) => {
416
- ripples.push(new Ripple(e.clientX, e.clientY));
417
- });
418
 
419
- canvas.addEventListener('touchstart', (e) => {
420
- e.preventDefault();
421
- const touch = e.touches[0];
422
- mouse.x = touch.clientX;
423
- mouse.y = touch.clientY;
424
- ripples.push(new Ripple(touch.clientX, touch.clientY));
425
- });
426
 
427
- canvas.addEventListener('touchmove', (e) => {
428
- e.preventDefault();
429
- const touch = e.touches[0];
430
- mouse.x = touch.clientX;
431
- mouse.y = touch.clientY;
432
- });
433
 
434
- canvas.addEventListener('touchend', () => {
435
- mouse.x = null;
436
- mouse.y = null;
437
- });
438
 
439
- // Mode buttons
440
- document.querySelectorAll('.control-btn').forEach(btn => {
441
- btn.addEventListener('click', () => {
442
- document.querySelectorAll('.control-btn').forEach(b => b.classList.remove('active'));
443
- btn.classList.add('active');
444
- mode = btn.dataset.mode;
445
- });
446
- });
447
 
448
- // Initialize
449
- resize();
450
- animate();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
451
  </script>
452
  </body>
453
 
 
4
  <head>
5
  <meta charset="UTF-8">
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Pulsing Floating Orbs</title>
8
  <style>
9
  * {
10
  margin: 0;
 
66
  height: 100%;
67
  }
68
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  .info {
70
  position: fixed;
71
+ bottom: 2rem;
72
  left: 50%;
73
  transform: translateX(-50%);
74
  color: rgba(255, 255, 255, 0.5);
75
+ font-size: 0.9rem;
76
  text-align: center;
77
  z-index: 1000;
78
+ padding: 0.8rem 1.5rem;
79
+ background: rgba(0, 0, 0, 0.3);
80
+ backdrop-filter: blur(10px);
81
+ border-radius: 25px;
82
+ border: 1px solid rgba(255, 255, 255, 0.1);
83
  }
84
 
85
  @media (max-width: 768px) {
 
93
  font-size: 1.2rem;
94
  }
95
 
96
+ .info {
97
  bottom: 1rem;
98
+ font-size: 0.75rem;
 
 
99
  padding: 0.6rem 1rem;
 
 
 
 
 
 
 
100
  }
101
  }
102
  </style>
 
104
 
105
  <body>
106
  <header>
107
+ <h1 class="title">💫 Pulsing Orbs</h1>
108
  <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="built-with">Built with anycoder</a>
109
  </header>
110
 
 
112
 
113
  <p class="info">Move your mouse to interact with the orbs • Click to create ripples</p>
114
 
 
 
 
 
 
 
 
115
  <script>
116
  const canvas = document.getElementById('canvas');
117
+ const ctx = canvas.getContext('2d');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
 
119
+ let width, height;
120
+ let orbs = [];
121
+ let mouse = { x: null, y: null, radius: 150 };
122
+ let ripples = [];
123
+ let time = 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
 
125
+ const LAYERS = 5;
126
+ const ORBS_PER_LAYER = 30;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
 
128
+ function resize() {
129
+ width = canvas.width = window.innerWidth;
130
+ height = canvas.height = window.innerHeight;
131
+ initOrbs();
132
+ }
133
+
134
+ class Orb {
135
+ constructor(layer) {
136
+ this.layer = layer;
137
+ this.layerFactor = (layer + 1) / LAYERS;
138
+ this.baseRadius = 8 + Math.random() * 16 * this.layerFactor;
139
+ this.radius = this.baseRadius;
140
+ this.x = Math.random() * width;
141
+ this.y = Math.random() * height;
142
+ this.baseX = this.x;
143
+ this.baseY = this.y;
144
+ this.vx = 0;
145
+ this.vy = 0;
146
+ this.phase = Math.random() * Math.PI * 2;
147
+ this.speed = 0.5 + Math.random() * 1.5;
148
+ this.amplitude = 20 + Math.random() * 40;
149
+ this.glowIntensity = 0.3 + Math.random() * 0.7;
150
+ this.pulseSpeed = 0.02 + Math.random() * 0.03;
151
+ this.angle = Math.random() * Math.PI * 2;
152
+ this.orbitRadius = 50 + Math.random() * 100;
153
+ this.orbitSpeed = 0.01 + Math.random() * 0.02;
154
+ }
155
 
156
+ update() {
157
+ // Pulse mode only
158
+ const pulseScale = 1 + Math.sin(time * 0.03 + this.phase) * 0.3;
159
+ const centerX = width / 2;
160
+ const centerY = height / 2;
161
+ const dx = this.baseX - centerX;
162
+ const dy = this.baseY - centerY;
163
+ this.x = centerX + dx * pulseScale;
164
+ this.y = centerY + dy * pulseScale;
165
+
166
+ // Mouse interaction
167
+ if (mouse.x !== null && mouse.y !== null) {
168
+ const dx = this.x - mouse.x;
169
+ const dy = this.y - mouse.y;
170
+ const distance = Math.sqrt(dx * dx + dy * dy);
171
+
172
+ if (distance < mouse.radius) {
173
+ const force = (mouse.radius - distance) / mouse.radius;
174
+ const angle = Math.atan2(dy, dx);
175
+ this.vx += Math.cos(angle) * force * 3 * this.layerFactor;
176
+ this.vy += Math.sin(angle) * force * 3 * this.layerFactor;
177
+ this.radius = this.baseRadius * (1 + force * 0.5);
178
+ } else {
179
+ this.radius += (this.baseRadius - this.radius) * 0.1;
180
+ }
181
  }
182
 
183
+ // Apply velocity with damping
184
+ this.x += this.vx;
185
+ this.y += this.vy;
186
+ this.vx *= 0.95;
187
+ this.vy *= 0.95;
188
+
189
+ // Ripple interaction
190
+ ripples.forEach(ripple => {
191
+ const dx = this.x - ripple.x;
192
+ const dy = this.y - ripple.y;
193
+ const distance = Math.sqrt(dx * dx + dy * dy);
194
+
195
+ if (Math.abs(distance - ripple.radius) < 50) {
196
+ const force = (1 - Math.abs(distance - ripple.radius) / 50) * ripple.strength;
197
+ const angle = Math.atan2(dy, dx);
198
+ this.vx += Math.cos(angle) * force * 2;
199
+ this.vy += Math.sin(angle) * force * 2;
200
+ }
201
+ });
202
 
203
+ // Pulsing glow
204
+ this.currentGlow = this.glowIntensity * (0.7 + Math.sin(time * this.pulseSpeed + this.phase) * 0.3);
205
+ }
206
 
207
+ draw() {
208
+ const alpha = 0.3 + this.layerFactor * 0.7;
209
+ const glowSize = this.radius * (3 + this.currentGlow * 2);
210
+
211
+ // Outer glow
212
+ const gradient = ctx.createRadialGradient(
213
+ this.x, this.y, 0,
214
+ this.x, this.y, glowSize
215
+ );
216
+ gradient.addColorStop(0, `rgba(255, 255, 255, ${alpha * this.currentGlow})`);
217
+ gradient.addColorStop(0.3, `rgba(200, 220, 255, ${alpha * this.currentGlow * 0.5})`);
218
+ gradient.addColorStop(0.6, `rgba(150, 180, 255, ${alpha * this.currentGlow * 0.2})`);
219
+ gradient.addColorStop(1, 'rgba(100, 150, 255, 0)');
220
+
221
+ ctx.beginPath();
222
+ ctx.arc(this.x, this.y, glowSize, 0, Math.PI * 2);
223
+ ctx.fillStyle = gradient;
224
+ ctx.fill();
225
+
226
+ // Core
227
+ const coreGradient = ctx.createRadialGradient(
228
+ this.x - this.radius * 0.3, this.y - this.radius * 0.3, 0,
229
+ this.x, this.y, this.radius
230
+ );
231
+ coreGradient.addColorStop(0, `rgba(255, 255, 255, ${alpha})`);
232
+ coreGradient.addColorStop(0.5, `rgba(230, 240, 255, ${alpha * 0.9})`);
233
+ coreGradient.addColorStop(1, `rgba(200, 220, 255, ${alpha * 0.7})`);
234
+
235
+ ctx.beginPath();
236
+ ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
237
+ ctx.fillStyle = coreGradient;
238
+ ctx.fill();
239
+ }
240
+ }
241
 
242
+ class Ripple {
243
+ constructor(x, y) {
244
+ this.x = x;
245
+ this.y = y;
246
+ this.radius = 0;
247
+ this.maxRadius = 300;
248
+ this.strength = 1;
249
+ this.speed = 8;
250
+ }
251
 
252
+ update() {
253
+ this.radius += this.speed;
254
+ this.strength = 1 - (this.radius / this.maxRadius);
255
+ }
256
 
257
+ draw() {
258
+ ctx.beginPath();
259
+ ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
260
+ ctx.strokeStyle = `rgba(255, 255, 255, ${this.strength * 0.3})`;
261
+ ctx.lineWidth = 2;
262
+ ctx.stroke();
263
+ }
264
 
265
+ isDead() {
266
+ return this.radius >= this.maxRadius;
267
+ }
268
+ }
269
 
270
+ function initOrbs() {
271
+ orbs = [];
272
+ for (let layer = 0; layer < LAYERS; layer++) {
273
+ for (let i = 0; i < ORBS_PER_LAYER; i++) {
274
+ orbs.push(new Orb(layer));
275
+ }
276
+ }
277
+ }
278
 
279
+ function drawConnections() {
280
+ orbs.forEach((orb, i) => {
281
+ orbs.slice(i + 1).forEach(other => {
282
+ if (orb.layer !== other.layer) return;
283
+
284
+ const dx = orb.x - other.x;
285
+ const dy = orb.y - other.y;
286
+ const distance = Math.sqrt(dx * dx + dy * dy);
287
+ const maxDistance = 100 + orb.layer * 20;
288
+
289
+ if (distance < maxDistance) {
290
+ const alpha = (1 - distance / maxDistance) * 0.15 * orb.layerFactor;
291
+ ctx.beginPath();
292
+ ctx.moveTo(orb.x, orb.y);
293
+ ctx.lineTo(other.x, other.y);
294
+ ctx.strokeStyle = `rgba(255, 255, 255, ${alpha})`;
295
+ ctx.lineWidth = 0.5;
296
+ ctx.stroke();
297
+ }
298
  });
299
+ });
300
+ }
301
 
302
+ function animate() {
303
+ ctx.fillStyle = 'rgba(10, 10, 15, 0.1)';
304
+ ctx.fillRect(0, 0, width, height);
 
305
 
306
+ time++;
 
 
307
 
308
+ // Update and draw ripples
309
+ ripples = ripples.filter(ripple => !ripple.isDead());
310
+ ripples.forEach(ripple => {
311
+ ripple.update();
312
+ ripple.draw();
313
+ });
 
314
 
315
+ // Draw connections first (behind orbs)
316
+ drawConnections();
 
 
 
 
317
 
318
+ // Sort orbs by layer for proper depth rendering
319
+ orbs.sort((a, b) => a.layer - b.layer);
 
 
320
 
321
+ // Update and draw orbs
322
+ orbs.forEach(orb => {
323
+ orb.update();
324
+ orb.draw();
325
+ });
326
+
327
+ requestAnimationFrame(animate);
328
+ }
329
 
330
+ // Event listeners
331
+ window.addEventListener('resize', resize);
332
+
333
+ canvas.addEventListener('mousemove', (e) => {
334
+ mouse.x = e.clientX;
335
+ mouse.y = e.clientY;
336
+ });
337
+
338
+ canvas.addEventListener('mouseleave', () => {
339
+ mouse.x = null;
340
+ mouse.y = null;
341
+ });
342
+
343
+ canvas.addEventListener('click', (e) => {
344
+ ripples.push(new Ripple(e.clientX, e.clientY));
345
+ });
346
+
347
+ canvas.addEventListener('touchstart', (e) => {
348
+ e.preventDefault();
349
+ const touch = e.touches[0];
350
+ mouse.x = touch.clientX;
351
+ mouse.y = touch.clientY;
352
+ ripples.push(new Ripple(touch.clientX, touch.clientY));
353
+ });
354
+
355
+ canvas.addEventListener('touchmove', (e) => {
356
+ e.preventDefault();
357
+ const touch = e.touches[0];
358
+ mouse.x = touch.clientX;
359
+ mouse.y = touch.clientY;
360
+ });
361
+
362
+ canvas.addEventListener('touchend', () => {
363
+ mouse.x = null;
364
+ mouse.y = null;
365
+ });
366
+
367
+ // Initialize
368
+ resize();
369
+ animate();
370
  </script>
371
  </body>
372