CompactAI commited on
Commit
aaa61fe
·
verified ·
1 Parent(s): fcf16d3

Delete main.js

Browse files
Files changed (1) hide show
  1. main.js +0 -926
main.js DELETED
@@ -1,926 +0,0 @@
1
- /**
2
- * FMN-GPT Website Interactive Visualizations
3
- * ==========================================
4
- * Canvas-based animations and interactive demos
5
- */
6
-
7
- // ========================================
8
- // Configuration
9
- // ========================================
10
-
11
- const CONFIG = {
12
- colors: {
13
- accent: '#e85d3b',
14
- accentLight: '#ff8a6b',
15
- secondary: '#d4a853',
16
- secondaryLight: '#e8c87a',
17
- bg: '#faf8f5',
18
- bgAlt: '#f5f0e8',
19
- bgDark: '#1a1815',
20
- text: '#2d2a26',
21
- textLight: '#6b6560',
22
- textMuted: '#9a948d',
23
- border: '#e5e0d8',
24
- success: '#50c878',
25
- info: '#4a9eff'
26
- },
27
- neuron: {
28
- count: 112,
29
- maxLoops: 30,
30
- layers: 6
31
- }
32
- };
33
-
34
- // ========================================
35
- // Utility Functions
36
- // ========================================
37
-
38
- function lerp(a, b, t) {
39
- return a + (b - a) * t;
40
- }
41
-
42
- function clamp(val, min, max) {
43
- return Math.max(min, Math.min(max, val));
44
- }
45
-
46
- function randomRange(min, max) {
47
- return Math.random() * (max - min) + min;
48
- }
49
-
50
- function hexToRgba(hex, alpha) {
51
- const r = parseInt(hex.slice(1, 3), 16);
52
- const g = parseInt(hex.slice(3, 5), 16);
53
- const b = parseInt(hex.slice(5, 7), 16);
54
- return `rgba(${r}, ${g}, ${b}, ${alpha})`;
55
- }
56
-
57
- // ========================================
58
- // Hero Neuron Canvas Animation
59
- // ========================================
60
-
61
- class NeuronNetwork {
62
- constructor(canvas) {
63
- this.canvas = canvas;
64
- this.ctx = canvas.getContext('2d');
65
- this.neurons = [];
66
- this.connections = [];
67
- this.particles = [];
68
- this.mouse = { x: 0, y: 0 };
69
- this.animationId = null;
70
-
71
- this.resize();
72
- this.init();
73
- this.bindEvents();
74
- this.animate();
75
- }
76
-
77
- resize() {
78
- const rect = this.canvas.parentElement.getBoundingClientRect();
79
- this.canvas.width = rect.width * window.devicePixelRatio;
80
- this.canvas.height = rect.height * window.devicePixelRatio;
81
- this.ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
82
- this.width = rect.width;
83
- this.height = rect.height;
84
- }
85
-
86
- init() {
87
- // Create neurons
88
- const count = Math.min(50, Math.floor(this.width * this.height / 15000));
89
- this.neurons = [];
90
-
91
- for (let i = 0; i < count; i++) {
92
- this.neurons.push({
93
- x: randomRange(50, this.width - 50),
94
- y: randomRange(50, this.height - 50),
95
- vx: randomRange(-0.3, 0.3),
96
- vy: randomRange(-0.3, 0.3),
97
- radius: randomRange(3, 6),
98
- pulse: randomRange(0, Math.PI * 2),
99
- pulseSpeed: randomRange(0.02, 0.05)
100
- });
101
- }
102
-
103
- // Create initial connections
104
- this.updateConnections();
105
- }
106
-
107
- updateConnections() {
108
- this.connections = [];
109
- const maxDist = 150;
110
-
111
- for (let i = 0; i < this.neurons.length; i++) {
112
- for (let j = i + 1; j < this.neurons.length; j++) {
113
- const dx = this.neurons[i].x - this.neurons[j].x;
114
- const dy = this.neurons[i].y - this.neurons[j].y;
115
- const dist = Math.sqrt(dx * dx + dy * dy);
116
-
117
- if (dist < maxDist) {
118
- this.connections.push({
119
- from: i,
120
- to: j,
121
- strength: 1 - dist / maxDist
122
- });
123
- }
124
- }
125
- }
126
- }
127
-
128
- bindEvents() {
129
- window.addEventListener('resize', () => {
130
- this.resize();
131
- this.init();
132
- });
133
-
134
- this.canvas.addEventListener('mousemove', (e) => {
135
- const rect = this.canvas.getBoundingClientRect();
136
- this.mouse.x = e.clientX - rect.left;
137
- this.mouse.y = e.clientY - rect.top;
138
- });
139
- }
140
-
141
- animate() {
142
- this.ctx.clearRect(0, 0, this.width, this.height);
143
-
144
- // Update neurons
145
- for (const neuron of this.neurons) {
146
- neuron.x += neuron.vx;
147
- neuron.y += neuron.vy;
148
- neuron.pulse += neuron.pulseSpeed;
149
-
150
- // Bounce off walls
151
- if (neuron.x < 20 || neuron.x > this.width - 20) neuron.vx *= -1;
152
- if (neuron.y < 20 || neuron.y > this.height - 20) neuron.vy *= -1;
153
-
154
- // Mouse interaction
155
- const dx = this.mouse.x - neuron.x;
156
- const dy = this.mouse.y - neuron.y;
157
- const dist = Math.sqrt(dx * dx + dy * dy);
158
- if (dist < 100 && dist > 0) {
159
- neuron.x -= dx * 0.01;
160
- neuron.y -= dy * 0.01;
161
- }
162
- }
163
-
164
- // Update connections
165
- this.updateConnections();
166
-
167
- // Draw connections
168
- for (const conn of this.connections) {
169
- const from = this.neurons[conn.from];
170
- const to = this.neurons[conn.to];
171
-
172
- this.ctx.beginPath();
173
- this.ctx.moveTo(from.x, from.y);
174
- this.ctx.lineTo(to.x, to.y);
175
- this.ctx.strokeStyle = hexToRgba(CONFIG.colors.accent, conn.strength * 0.3);
176
- this.ctx.lineWidth = conn.strength * 2;
177
- this.ctx.stroke();
178
- }
179
-
180
- // Draw neurons
181
- for (const neuron of this.neurons) {
182
- const pulseRadius = neuron.radius + Math.sin(neuron.pulse) * 2;
183
-
184
- // Glow
185
- const gradient = this.ctx.createRadialGradient(
186
- neuron.x, neuron.y, 0,
187
- neuron.x, neuron.y, pulseRadius * 3
188
- );
189
- gradient.addColorStop(0, hexToRgba(CONFIG.colors.accent, 0.3));
190
- gradient.addColorStop(1, hexToRgba(CONFIG.colors.accent, 0));
191
-
192
- this.ctx.beginPath();
193
- this.ctx.arc(neuron.x, neuron.y, pulseRadius * 3, 0, Math.PI * 2);
194
- this.ctx.fillStyle = gradient;
195
- this.ctx.fill();
196
-
197
- // Core
198
- this.ctx.beginPath();
199
- this.ctx.arc(neuron.x, neuron.y, pulseRadius, 0, Math.PI * 2);
200
- this.ctx.fillStyle = CONFIG.colors.accent;
201
- this.ctx.fill();
202
- }
203
-
204
- this.animationId = requestAnimationFrame(() => this.animate());
205
- }
206
-
207
- destroy() {
208
- if (this.animationId) {
209
- cancelAnimationFrame(this.animationId);
210
- }
211
- }
212
- }
213
-
214
- // ========================================
215
- // FMN Visualization
216
- // ========================================
217
-
218
- class FMNVisualization {
219
- constructor(canvas) {
220
- this.canvas = canvas;
221
- this.ctx = canvas.getContext('2d');
222
- this.time = 0;
223
- this.animationId = null;
224
- this.dataFlow = [];
225
-
226
- this.resize();
227
- this.initDataFlow();
228
- this.animate();
229
- }
230
-
231
- resize() {
232
- const rect = this.canvas.parentElement.getBoundingClientRect();
233
- const dpr = window.devicePixelRatio || 1;
234
- this.canvas.width = 400 * dpr;
235
- this.canvas.height = 300 * dpr;
236
- this.ctx.scale(dpr, dpr);
237
- this.width = 400;
238
- this.height = 300;
239
- }
240
-
241
- initDataFlow() {
242
- this.dataFlow = [];
243
- for (let i = 0; i < 8; i++) {
244
- this.dataFlow.push({
245
- progress: Math.random(),
246
- speed: 0.003 + Math.random() * 0.002,
247
- path: Math.floor(Math.random() * 3)
248
- });
249
- }
250
- }
251
-
252
- animate() {
253
- this.ctx.clearRect(0, 0, this.width, this.height);
254
- this.time += 0.016;
255
-
256
- const centerX = this.width / 2;
257
- const centerY = this.height / 2;
258
-
259
- // Background
260
- this.ctx.fillStyle = '#faf8f5';
261
- this.ctx.fillRect(0, 0, this.width, this.height);
262
-
263
- const inputY = centerY - 90;
264
- const w1Y = centerY - 25;
265
- const w2Y = centerY + 25;
266
- const outputY = centerY + 90;
267
-
268
- // Draw static connection lines first (faded)
269
- this.ctx.strokeStyle = 'rgba(107, 101, 96, 0.15)';
270
- this.ctx.lineWidth = 1;
271
-
272
- // Input to W1 connections
273
- for (let i = 0; i < 5; i++) {
274
- for (let j = 0; j < 3; j++) {
275
- const x1 = centerX - 80 + i * 40;
276
- const x2 = centerX - 40 + j * 40;
277
- this.ctx.beginPath();
278
- this.ctx.moveTo(x1, inputY + 14);
279
- this.ctx.lineTo(x2, w1Y - 14);
280
- this.ctx.stroke();
281
- }
282
- }
283
-
284
- // W2 to output connections
285
- for (let i = 0; i < 3; i++) {
286
- for (let j = 0; j < 5; j++) {
287
- const x1 = centerX - 40 + i * 40;
288
- const x2 = centerX - 80 + j * 40;
289
- this.ctx.beginPath();
290
- this.ctx.moveTo(x1, w2Y + 14);
291
- this.ctx.lineTo(x2, outputY - 14);
292
- this.ctx.stroke();
293
- }
294
- }
295
-
296
- // Input layer neurons
297
- for (let i = 0; i < 5; i++) {
298
- const x = centerX - 80 + i * 40;
299
- this.drawNeuron(x, inputY, 14, '#4a9eff');
300
- }
301
-
302
- // Projection layer 1
303
- for (let i = 0; i < 3; i++) {
304
- const x = centerX - 40 + i * 40;
305
- this.drawNeuron(x, w1Y, 16, CONFIG.colors.accent);
306
- }
307
-
308
- // Projection layer 2
309
- for (let i = 0; i < 3; i++) {
310
- const x = centerX - 40 + i * 40;
311
- this.drawNeuron(x, w2Y, 16, CONFIG.colors.secondary);
312
- }
313
-
314
- // Output layer neurons
315
- for (let i = 0; i < 5; i++) {
316
- const x = centerX - 80 + i * 40;
317
- this.drawNeuron(x, outputY, 14, '#50c878');
318
- }
319
-
320
- // Operation symbol
321
- this.ctx.font = 'bold 20px Inter, sans-serif';
322
- this.ctx.fillStyle = CONFIG.colors.textMuted;
323
- this.ctx.textAlign = 'center';
324
- this.ctx.textBaseline = 'middle';
325
- this.ctx.fillText('?', centerX, centerY);
326
-
327
- // Animated data flow particles
328
- for (const particle of this.dataFlow) {
329
- particle.progress += particle.speed;
330
- if (particle.progress > 1) {
331
- particle.progress = 0;
332
- particle.path = Math.floor(Math.random() * 3);
333
- }
334
-
335
- const alpha = Math.sin(particle.progress * Math.PI) * 0.8;
336
- const inputIdx = particle.path;
337
- const wIdx = particle.path % 3;
338
- const outIdx = particle.path;
339
-
340
- const inputX = centerX - 80 + inputIdx * 40;
341
- const w1X = centerX - 40 + wIdx * 40;
342
- const w2X = centerX - 40 + wIdx * 40;
343
- const outX = centerX - 80 + outIdx * 40;
344
-
345
- let px, py;
346
- if (particle.progress < 0.33) {
347
- const t = particle.progress / 0.33;
348
- px = lerp(inputX, w1X, t);
349
- py = lerp(inputY, w1Y, t);
350
- } else if (particle.progress < 0.66) {
351
- const t = (particle.progress - 0.33) / 0.33;
352
- px = lerp(w1X, w2X, t);
353
- py = lerp(w1Y, w2Y, t);
354
- } else {
355
- const t = (particle.progress - 0.66) / 0.34;
356
- px = lerp(w2X, outX, t);
357
- py = lerp(w2Y, outputY, t);
358
- }
359
-
360
- this.ctx.beginPath();
361
- this.ctx.arc(px, py, 4, 0, Math.PI * 2);
362
- this.ctx.fillStyle = `rgba(232, 93, 59, ${alpha})`;
363
- this.ctx.fill();
364
- }
365
-
366
- // Labels
367
- this.ctx.font = '11px Inter, sans-serif';
368
- this.ctx.fillStyle = CONFIG.colors.textMuted;
369
- this.ctx.textAlign = 'center';
370
- this.ctx.textBaseline = 'top';
371
- this.ctx.fillText('Input', centerX, inputY - 30);
372
- this.ctx.fillText('[REDACTED]', centerX - 70, w1Y - 8);
373
- this.ctx.fillText('[REDACTED]', centerX + 70, w2Y - 8);
374
- this.ctx.fillText('Output', centerX, outputY + 25);
375
-
376
- this.animationId = requestAnimationFrame(() => this.animate());
377
- }
378
-
379
- drawNeuron(x, y, radius, color) {
380
- // Subtle shadow
381
- this.ctx.beginPath();
382
- this.ctx.arc(x, y + 2, radius, 0, Math.PI * 2);
383
- this.ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
384
- this.ctx.fill();
385
-
386
- // Main circle
387
- this.ctx.beginPath();
388
- this.ctx.arc(x, y, radius, 0, Math.PI * 2);
389
- this.ctx.fillStyle = color;
390
- this.ctx.fill();
391
-
392
- // Highlight
393
- this.ctx.beginPath();
394
- this.ctx.arc(x - radius * 0.3, y - radius * 0.3, radius * 0.4, 0, Math.PI * 2);
395
- this.ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
396
- this.ctx.fill();
397
- }
398
-
399
- destroy() {
400
- if (this.animationId) {
401
- cancelAnimationFrame(this.animationId);
402
- }
403
- }
404
- }
405
-
406
- // ========================================
407
- // Routing Visualization
408
- // ========================================
409
-
410
- class RoutingVisualization {
411
- constructor(canvas) {
412
- this.canvas = canvas;
413
- this.ctx = canvas.getContext('2d');
414
- this.time = 0;
415
- this.particles = [];
416
- this.animationId = null;
417
-
418
- this.resize();
419
- this.initParticles();
420
- this.animate();
421
- }
422
-
423
- resize() {
424
- const dpr = window.devicePixelRatio || 1;
425
- this.canvas.width = 400 * dpr;
426
- this.canvas.height = 300 * dpr;
427
- this.ctx.scale(dpr, dpr);
428
- this.width = 400;
429
- this.height = 300;
430
- }
431
-
432
- initParticles() {
433
- this.particles = [];
434
- const layers = 6;
435
- const layerSpacing = this.width / (layers + 1);
436
-
437
- for (let i = 0; i < 12; i++) {
438
- const startLayer = Math.floor(Math.random() * layers);
439
- const startY = randomRange(60, this.height - 60);
440
- this.particles.push({
441
- x: (startLayer + 1) * layerSpacing,
442
- y: startY,
443
- targetX: 0,
444
- targetY: 0,
445
- speed: 0.8 + Math.random() * 0.4,
446
- color: Math.random() > 0.5 ? CONFIG.colors.accent : CONFIG.colors.secondary,
447
- trail: []
448
- });
449
- this.setNewTarget(this.particles[i]);
450
- }
451
- }
452
-
453
- setNewTarget(particle) {
454
- const layers = 6;
455
- const layerSpacing = this.width / (layers + 1);
456
- const targetLayer = Math.floor(Math.random() * layers);
457
- particle.targetX = (targetLayer + 1) * layerSpacing;
458
- particle.targetY = randomRange(60, this.height - 60);
459
- }
460
-
461
- animate() {
462
- this.ctx.clearRect(0, 0, this.width, this.height);
463
- this.time += 0.016;
464
-
465
- // Background
466
- this.ctx.fillStyle = '#faf8f5';
467
- this.ctx.fillRect(0, 0, this.width, this.height);
468
-
469
- const layers = 6;
470
- const layerSpacing = this.width / (layers + 1);
471
-
472
- // Draw layer columns
473
- for (let i = 1; i <= layers; i++) {
474
- const x = i * layerSpacing;
475
-
476
- // Layer column background
477
- this.ctx.fillStyle = 'rgba(229, 224, 216, 0.3)';
478
- this.ctx.fillRect(x - 15, 40, 30, this.height - 70);
479
-
480
- // Layer label
481
- this.ctx.font = '10px Inter, sans-serif';
482
- this.ctx.fillStyle = CONFIG.colors.textMuted;
483
- this.ctx.textAlign = 'center';
484
- this.ctx.fillText(`L${i - 1}`, x, this.height - 15);
485
- }
486
-
487
- // Title
488
- this.ctx.font = 'bold 13px Inter, sans-serif';
489
- this.ctx.fillStyle = CONFIG.colors.text;
490
- this.ctx.textAlign = 'center';
491
- this.ctx.fillText('Cross-Layer Backward Routing', this.width / 2, 22);
492
-
493
- // Update and draw particles with trails
494
- for (const p of this.particles) {
495
- const dx = p.targetX - p.x;
496
- const dy = p.targetY - p.y;
497
- const dist = Math.sqrt(dx * dx + dy * dy);
498
-
499
- if (dist < 3) {
500
- this.setNewTarget(p);
501
- } else {
502
- const moveX = (dx / dist) * p.speed;
503
- const moveY = (dy / dist) * p.speed;
504
- p.x += moveX;
505
- p.y += moveY;
506
-
507
- // Add to trail
508
- p.trail.push({ x: p.x, y: p.y });
509
- if (p.trail.length > 15) {
510
- p.trail.shift();
511
- }
512
- }
513
-
514
- // Draw trail
515
- for (let i = 0; i < p.trail.length - 1; i++) {
516
- const alpha = (i / p.trail.length) * 0.4;
517
- this.ctx.beginPath();
518
- this.ctx.moveTo(p.trail[i].x, p.trail[i].y);
519
- this.ctx.lineTo(p.trail[i + 1].x, p.trail[i + 1].y);
520
- this.ctx.strokeStyle = `rgba(232, 93, 59, ${alpha})`;
521
- this.ctx.lineWidth = 2;
522
- this.ctx.stroke();
523
- }
524
-
525
- // Draw particle
526
- this.ctx.beginPath();
527
- this.ctx.arc(p.x, p.y, 5, 0, Math.PI * 2);
528
- this.ctx.fillStyle = p.color;
529
- this.ctx.fill();
530
- }
531
-
532
- this.animationId = requestAnimationFrame(() => this.animate());
533
- }
534
-
535
- destroy() {
536
- if (this.animationId) {
537
- cancelAnimationFrame(this.animationId);
538
- }
539
- }
540
- }
541
-
542
- // ========================================
543
- // Recurrent Mixer Visualization
544
- // ========================================
545
-
546
- class RecurrentVisualization {
547
- constructor(canvas) {
548
- this.canvas = canvas;
549
- this.ctx = canvas.getContext('2d');
550
- this.time = 0;
551
- this.states = [];
552
- this.animationId = null;
553
-
554
- this.resize();
555
- this.initStates();
556
- this.animate();
557
- }
558
-
559
- resize() {
560
- const dpr = window.devicePixelRatio || 1;
561
- this.canvas.width = 400 * dpr;
562
- this.canvas.height = 300 * dpr;
563
- this.ctx.scale(dpr, dpr);
564
- this.width = 400;
565
- this.height = 300;
566
- }
567
-
568
- initStates() {
569
- this.states = [];
570
- for (let i = 0; i < 8; i++) {
571
- this.states.push({
572
- value: randomRange(0.3, 0.7),
573
- target: randomRange(0.3, 0.7),
574
- gate: randomRange(0.2, 0.8),
575
- history: []
576
- });
577
- }
578
- }
579
-
580
- animate() {
581
- this.ctx.clearRect(0, 0, this.width, this.height);
582
- this.time += 0.016;
583
-
584
- // Background
585
- this.ctx.fillStyle = '#faf8f5';
586
- this.ctx.fillRect(0, 0, this.width, this.height);
587
-
588
- const barWidth = 32;
589
- const barSpacing = 46;
590
- const startX = (this.width - (this.states.length * barSpacing)) / 2 + 5;
591
- const baseY = this.height - 50;
592
- const maxHeight = 140;
593
-
594
- // Title
595
- this.ctx.font = 'bold 13px Inter, sans-serif';
596
- this.ctx.fillStyle = CONFIG.colors.text;
597
- this.ctx.textAlign = 'center';
598
- this.ctx.fillText('Per-Channel Gated State', this.width / 2, 22);
599
-
600
- for (let i = 0; i < this.states.length; i++) {
601
- const state = this.states[i];
602
- const x = startX + i * barSpacing;
603
-
604
- // Update state smoothly
605
- state.gate = 0.4 + Math.sin(this.time * 0.8 + i * 0.7) * 0.35;
606
- state.value = lerp(state.value, state.target, state.gate * 0.08);
607
-
608
- // Randomly change target
609
- if (Math.random() < 0.008) {
610
- state.target = randomRange(0.2, 0.95);
611
- }
612
-
613
- // Store history
614
- state.history.push(state.value);
615
- if (state.history.length > 50) state.history.shift();
616
-
617
- // Draw bar background
618
- this.ctx.fillStyle = 'rgba(229, 224, 216, 0.4)';
619
- this.ctx.beginPath();
620
- this.ctx.roundRect(x - barWidth / 2, baseY - maxHeight, barWidth, maxHeight, 4);
621
- this.ctx.fill();
622
-
623
- // Draw current state bar
624
- const currentHeight = maxHeight * state.value;
625
- const gradient = this.ctx.createLinearGradient(x, baseY, x, baseY - currentHeight);
626
- gradient.addColorStop(0, CONFIG.colors.accent);
627
- gradient.addColorStop(1, CONFIG.colors.accentLight);
628
- this.ctx.fillStyle = gradient;
629
- this.ctx.beginPath();
630
- this.ctx.roundRect(x - barWidth / 2, baseY - currentHeight, barWidth, currentHeight, 4);
631
- this.ctx.fill();
632
-
633
- // Draw gate indicator
634
- const gateHeight = 8;
635
- const gateY = baseY - maxHeight - 18;
636
- const gateFilled = state.gate * barWidth;
637
-
638
- this.ctx.fillStyle = 'rgba(229, 224, 216, 0.6)';
639
- this.ctx.fillRect(x - barWidth / 2, gateY, barWidth, gateHeight);
640
-
641
- this.ctx.fillStyle = CONFIG.colors.success;
642
- this.ctx.fillRect(x - barWidth / 2, gateY, gateFilled, gateHeight);
643
-
644
- // Channel label
645
- this.ctx.font = '10px Inter, sans-serif';
646
- this.ctx.fillStyle = CONFIG.colors.textMuted;
647
- this.ctx.textAlign = 'center';
648
- this.ctx.fillText(`c${i}`, x, baseY + 15);
649
- }
650
-
651
- // Legend
652
- this.ctx.font = '10px Inter, sans-serif';
653
- this.ctx.fillStyle = CONFIG.colors.textMuted;
654
- this.ctx.textAlign = 'left';
655
- this.ctx.fillText('Gate:', 15, 22);
656
- this.ctx.fillStyle = CONFIG.colors.success;
657
- this.ctx.fillRect(45, 14, 25, 8);
658
-
659
- this.animationId = requestAnimationFrame(() => this.animate());
660
- }
661
-
662
- destroy() {
663
- if (this.animationId) {
664
- cancelAnimationFrame(this.animationId);
665
- }
666
- }
667
- }
668
-
669
- // ========================================
670
- // Loop Counter Visualization
671
- // ========================================
672
-
673
- class LoopVisualization {
674
- constructor(canvas, slider, indicator, valueDisplay) {
675
- this.canvas = canvas;
676
- this.ctx = canvas.getContext('2d');
677
- this.slider = slider;
678
- this.indicator = indicator;
679
- this.valueDisplay = valueDisplay;
680
- this.maxLoops = 30;
681
- this.loopCounts = [];
682
- this.animationId = null;
683
-
684
- this.resize();
685
- this.initLoops();
686
- this.bindEvents();
687
- this.animate();
688
- }
689
-
690
- resize() {
691
- const dpr = window.devicePixelRatio || 1;
692
- this.canvas.width = 400 * dpr;
693
- this.canvas.height = 300 * dpr;
694
- this.ctx.scale(dpr, dpr);
695
- this.width = 400;
696
- this.height = 300;
697
- }
698
-
699
- initLoops() {
700
- this.loopCounts = [];
701
- for (let i = 0; i < 20; i++) {
702
- this.loopCounts.push({
703
- count: Math.floor(randomRange(0, this.maxLoops * 0.6)),
704
- targetCount: Math.floor(randomRange(5, this.maxLoops)),
705
- incrementing: Math.random() > 0.2
706
- });
707
- }
708
- this.updateIndicator();
709
- }
710
-
711
- bindEvents() {
712
- if (this.slider) {
713
- this.slider.addEventListener('input', (e) => {
714
- this.maxLoops = parseInt(e.target.value);
715
- if (this.valueDisplay) {
716
- this.valueDisplay.textContent = this.maxLoops;
717
- }
718
- this.updateIndicator();
719
- });
720
- }
721
- }
722
-
723
- updateIndicator() {
724
- if (!this.indicator) return;
725
-
726
- this.indicator.innerHTML = '';
727
- for (let i = 0; i < this.maxLoops; i++) {
728
- const dot = document.createElement('div');
729
- dot.className = 'loop-dot';
730
- if (i < this.maxLoops * 0.7) {
731
- dot.classList.add('active');
732
- } else if (i < this.maxLoops) {
733
- dot.classList.add('exhausted');
734
- }
735
- this.indicator.appendChild(dot);
736
- }
737
- }
738
-
739
- animate() {
740
- this.ctx.clearRect(0, 0, this.width, this.height);
741
-
742
- // Background
743
- this.ctx.fillStyle = '#faf8f5';
744
- this.ctx.fillRect(0, 0, this.width, this.height);
745
-
746
- const cols = 5;
747
- const rows = 4;
748
- const cellWidth = this.width / cols;
749
- const cellHeight = (this.height - 50) / rows;
750
-
751
- // Title
752
- this.ctx.font = 'bold 13px Inter, sans-serif';
753
- this.ctx.fillStyle = CONFIG.colors.text;
754
- this.ctx.textAlign = 'center';
755
- this.ctx.fillText('Neuron Loop Budget', this.width / 2, 22);
756
-
757
- for (let i = 0; i < this.loopCounts.length; i++) {
758
- const state = this.loopCounts[i];
759
- const col = i % cols;
760
- const row = Math.floor(i / cols);
761
- const x = col * cellWidth + cellWidth / 2;
762
- const y = row * cellHeight + cellHeight / 2 + 35;
763
-
764
- // Smoothly increment towards target
765
- if (state.incrementing) {
766
- if (state.count < state.targetCount) {
767
- state.count += 0.05;
768
- } else if (Math.random() < 0.02) {
769
- state.targetCount = Math.floor(randomRange(state.count, this.maxLoops));
770
- }
771
-
772
- if (state.count >= this.maxLoops) {
773
- state.count = this.maxLoops;
774
- state.incrementing = false;
775
- }
776
- }
777
-
778
- const radius = 26;
779
- const progress = Math.min(state.count / this.maxLoops, 1);
780
- const exhausted = state.count >= this.maxLoops;
781
-
782
- // Background ring
783
- this.ctx.beginPath();
784
- this.ctx.arc(x, y, radius, 0, Math.PI * 2);
785
- this.ctx.strokeStyle = 'rgba(229, 224, 216, 0.6)';
786
- this.ctx.lineWidth = 5;
787
- this.ctx.stroke();
788
-
789
- // Progress ring
790
- if (progress > 0) {
791
- this.ctx.beginPath();
792
- this.ctx.arc(x, y, radius, -Math.PI / 2, -Math.PI / 2 + progress * Math.PI * 2);
793
- this.ctx.strokeStyle = exhausted ? CONFIG.colors.textMuted : CONFIG.colors.accent;
794
- this.ctx.lineWidth = 5;
795
- this.ctx.lineCap = 'round';
796
- this.ctx.stroke();
797
- }
798
-
799
- // Center fill
800
- this.ctx.beginPath();
801
- this.ctx.arc(x, y, radius - 10, 0, Math.PI * 2);
802
- this.ctx.fillStyle = exhausted ? 'rgba(154, 148, 141, 0.1)' : 'rgba(232, 93, 59, 0.1)';
803
- this.ctx.fill();
804
-
805
- // Center count
806
- this.ctx.font = 'bold 13px Inter, sans-serif';
807
- this.ctx.fillStyle = exhausted ? CONFIG.colors.textMuted : CONFIG.colors.text;
808
- this.ctx.textAlign = 'center';
809
- this.ctx.textBaseline = 'middle';
810
- this.ctx.fillText(Math.floor(state.count).toString(), x, y);
811
- }
812
-
813
- this.animationId = requestAnimationFrame(() => this.animate());
814
- }
815
-
816
- destroy() {
817
- if (this.animationId) {
818
- cancelAnimationFrame(this.animationId);
819
- }
820
- }
821
- }
822
-
823
- // ========================================
824
- // Tab Switching
825
- // ========================================
826
-
827
- function initTabs() {
828
- const tabBtns = document.querySelectorAll('.tab-btn');
829
- const tabPanes = document.querySelectorAll('.tab-pane');
830
-
831
- tabBtns.forEach(btn => {
832
- btn.addEventListener('click', () => {
833
- const tabId = btn.dataset.tab;
834
-
835
- // Update buttons
836
- tabBtns.forEach(b => b.classList.remove('active'));
837
- btn.classList.add('active');
838
-
839
- // Update panes
840
- tabPanes.forEach(pane => {
841
- pane.classList.remove('active');
842
- if (pane.id === `${tabId}-pane`) {
843
- pane.classList.add('active');
844
- }
845
- });
846
- });
847
- });
848
- }
849
-
850
- // ========================================
851
- // Scroll Animations
852
- // ========================================
853
-
854
- function initScrollAnimations() {
855
- const observer = new IntersectionObserver((entries) => {
856
- entries.forEach(entry => {
857
- if (entry.isIntersecting) {
858
- entry.target.classList.add('visible');
859
- }
860
- });
861
- }, {
862
- threshold: 0.1,
863
- rootMargin: '0px 0px -50px 0px'
864
- });
865
-
866
- document.querySelectorAll('.fade-in-up').forEach(el => {
867
- observer.observe(el);
868
- });
869
- }
870
-
871
- // ========================================
872
- // Initialize Everything
873
- // ========================================
874
-
875
- let heroNetwork = null;
876
- let fmnViz = null;
877
- let routingViz = null;
878
- let recurrentViz = null;
879
- let loopViz = null;
880
-
881
- document.addEventListener('DOMContentLoaded', () => {
882
- // Hero animation
883
- const heroCanvas = document.getElementById('neuron-canvas');
884
- if (heroCanvas) {
885
- heroNetwork = new NeuronNetwork(heroCanvas);
886
- }
887
-
888
- // Tab visualizations
889
- const fmnCanvas = document.getElementById('fmn-canvas');
890
- if (fmnCanvas) {
891
- fmnViz = new FMNVisualization(fmnCanvas);
892
- }
893
-
894
- const routingCanvas = document.getElementById('routing-canvas');
895
- if (routingCanvas) {
896
- routingViz = new RoutingVisualization(routingCanvas);
897
- }
898
-
899
- const recurrentCanvas = document.getElementById('recurrent-canvas');
900
- if (recurrentCanvas) {
901
- recurrentViz = new RecurrentVisualization(recurrentCanvas);
902
- }
903
-
904
- const loopsCanvas = document.getElementById('loops-canvas');
905
- const loopSlider = document.getElementById('loop-slider');
906
- const loopIndicator = document.getElementById('loop-indicator');
907
- const loopValue = document.getElementById('loop-value');
908
- if (loopsCanvas) {
909
- loopViz = new LoopVisualization(loopsCanvas, loopSlider, loopIndicator, loopValue);
910
- }
911
-
912
- // Initialize tabs
913
- initTabs();
914
-
915
- // Initialize scroll animations
916
- initScrollAnimations();
917
- });
918
-
919
- // Cleanup on page unload
920
- window.addEventListener('beforeunload', () => {
921
- if (heroNetwork) heroNetwork.destroy();
922
- if (fmnViz) fmnViz.destroy();
923
- if (routingViz) routingViz.destroy();
924
- if (recurrentViz) recurrentViz.destroy();
925
- if (loopViz) loopViz.destroy();
926
- });