/* ========================================================= NUMEX AI — Neural Network Core Visualizer Canlı sinir ağı animasyonu + içinden akan NUMEX token akışı Saf Canvas — harici bağımlılık yok. ========================================================= */ (() => { "use strict"; /* ---------- Konfigürasyon ---------- */ const CONFIG = { // Katman mimarisi (nöron sayıları) layers: [6, 14, 20, 16, 10, 7, 4, 2, 1], tokenText: "NUMEX", tokenSpawn: 1.7, // sn'de üretilecek token sayısı activationSpeed: 0.9, // aktivasyon yayılım hızı colorHot: [0, 226, 255], // camgöbeği — aktivasyon colorCool: [88, 101, 242],// indigo — ağırlıklar colorText: [255, 220, 90] // amber — NUMEX akışı }; /* ---------- DOM Bağlantıları ---------- */ const netCanvas = document.getElementById("net"); const chartCanvas = document.getElementById("lossChart"); const ctx = netCanvas.getContext("2d"); const cctx = chartCanvas.getContext("2d"); const epochEl = document.getElementById("epoch"); const ppsEl = document.getElementById("pps"); const gaugeFill = document.getElementById("gaugeFill"); const gaugeLabel = document.getElementById("gaugeLabel"); /* ---------- Durum ---------- */ let W = 0, H = 0; let DPR = Math.min(window.devicePixelRatio || 1, 2); let nodePositions = []; // her katman için nöron pozisyonları let tokenParticles = []; // NUMEX harfleri let pulseEvents = []; // sinir darbeleri let connectionCache = []; // ön hesaplanmış bağlantı puanları let epoch = 0; let lastSpawn = 0; let trainProgress = 0; let chartData = new Array(52).fill(1.0); const LETTERS = CONFIG.tokenText.split(""); const FONT_BASIS = 16; /* ---------- Sayı yardımcıları ---------- */ const rnd = (a, b) => a + Math.random() * (b - a); const clamp = (v, a, b) => Math.max(a, Math.min(b, v)); /* ---------- Boyutlandırma ---------- */ function resize() { W = window.innerWidth; H = window.innerHeight; DPR = Math.min(window.devicePixelRatio || 1, 2); netCanvas.width = W * DPR; netCanvas.height = H * DPR; netCanvas.style.width = W + "px"; netCanvas.style.height = H + "px"; ctx.setTransform(DPR, 0, 0, DPR, 0, 0); buildNodes(); } /* ---------- Nöron yerleşimi ---------- */ function buildNodes() { nodePositions = []; const marginX = W * 0.12; const usableW = W - marginX * 2; const usableH = H * 0.72; const topY = H * 0.18; const gapX = usableW / (CONFIG.layers.length - 1); CONFIG.layers.forEach((count, li) => { const col = []; const x = marginX + gapX * li; // Katman yüksekliği nöron sayısıyla orantılı (görsel coşku) const maxH = Math.max(...CONFIG.layers.slice(-3)); const span = Math.max(usableH * (count / maxH), 40); for (let ni = 0; ni < count; ni++) { const t = count === 1 ? 0.5 : ni / (count - 1); const y = topY + span / 2 + (t - 0.5) * span * 1.2 + rnd(-3, 3); // Her nöronun kendine has "canlılık" fazı col.push({ x, y, radius: count <= 2 ? 5 : count <= 6 ? 4 : 3.5, seed: Math.random() * Math.PI * 2, weight: rnd(-1, 1) }); } nodePositions.push(col); }); // Bağlantı önbelleği connectionCache = []; for (let li = 0; li < nodePositions.length - 1; li++) { const left = nodePositions[li]; const right = nodePositions[li + 1]; for (const a of left) { for (const b of right) { connectionCache.push({ ax: a.x, ay: a.y, bx: b.x, by: b.y, l: li, w: Math.random() * 2 - 1 }); } } } } /* ---------- NUMEX token parçacığı oluştur --- Bir nöronun üstünden süzülüp komşu katmana akar. */ function spawnToken() { const li = Math.floor(Math.random() * (nodePositions.length - 2)); const col = nodePositions[li]; if (!col || !col.length) return; const n = col[Math.floor(Math.random() * col.length)]; const target = nodePositions[li + 1][Math.floor(Math.random() * nodePositions[li + 1].length)]; const letter = LETTERS[Math.floor(Math.random() * LETTERS.length)]; tokenParticles.push({ x: n.x, y: n.y, tx: target.x, ty: target.y, t: 0, speed: rnd(1.2, 2.2), // akış hızı letter, size: rnd(FONT_BASIS * 0.7, FONT_BASIS * 1.1), phase: Math.random() * Math.PI * 2, life: 1 }); pushPulse(n.x, n.y, 0.7); } function pushPulse(x, y, r) { pulseEvents.push({ x, y, r, a: 1, t: 0 }); } /* ---------- Aktivasyon darbesi ---------- */ function spawnActivation() { const li = Math.floor(Math.random() * nodePositions.length); const col = nodePositions[li]; if (!col || !col.length) return; const n = col[Math.floor(Math.random() * col.length)]; pushPulse(n.x, n.y, 1.6); } /* ---------- Ana döngü ---------- */ let lastTime = performance.now(); function loop(now) { const dt = Math.min((now - lastTime) / 1000, 0.05); lastTime = now; // Epoch simülasyonu epoch += dt * 24; epochEl.textContent = String(Math.floor(epoch)).padStart(4, "0"); const pps = Math.floor(rnd(18, 44) * 1000); ppsEl.textContent = (pps / 1000).toFixed(1) + "K"; trainProgress = Math.min(1, trainProgress + dt * 0.012); gaugeFill.style.width = (trainProgress * 100).toFixed(1) + "%"; gaugeLabel.textContent = trainProgress >= 1 ? "Training complete ✓" : "Optimizing weights " + (trainProgress * 100).toFixed(1) + "%"; // Token üretimi lastSpawn += dt; while (lastSpawn > 1 / CONFIG.tokenSpawn) { lastSpawn -= 1 / CONFIG.tokenSpawn; spawnToken(); spawnActivation(); } // Böcekleyici — aşırı birikmeyi önle if (tokenParticles.length > 340) tokenParticles.splice(0, tokenParticles.length - 340); updateTokens(dt); updatePulses(dt); updateChart(dt); draw(); requestAnimationFrame(loop); } /* ---------- Token güncelleme ---------- */ function updateTokens(dt) { for (let i = tokenParticles.length - 1; i >= 0; i--) { const p = tokenParticles[i]; p.t += dt * p.speed; // Bezier kavsi veren ara nokta (hafif bombe) const mx = (p.x + p.tx) / 2; const my = Math.min(p.y, p.ty) - rnd(8, 22); p.cx = mx; p.cy = my; if (p.t >= 1) { pushPulse(p.tx, p.ty, 1.1); tokenParticles.splice(i, 1); } } } function updatePulses(dt) { for (let i = pulseEvents.length - 1; i >= 0; i--) { const e = pulseEvents[i]; e.t += dt * 1.6; e.r += dt * 24; e.a = Math.max(0, 1 - e.t); if (e.a <= 0) pulseEvents.splice(i, 1); } } function updateChart(dt) { // Kayıp eğrisi monoton azalan + gürültü const cur = chartData[chartData.length - 1]; const next = Math.max(0.08, cur - dt * 0.06 + rnd(-0.03, 0.03)); chartData.push(clamp(next, 0.08, 1)); chartData.shift(); } /* ---------- Çizim ---------- */ function lerpColor(c1, c2, t) { return [ Math.round(c1[0] + (c2[0] - c1[0]) * t), Math.round(c1[1] + (c2[1] - c1[1]) * t), Math.round(c1[2] + (c2[2] - c1[2]) * t), ]; } function rgba(c, a) { return `rgba(${c[0]},${c[1]},${c[2]},${a})`; } function draw() { // — Arka plan degrade const bg = ctx.createLinearGradient(0, 0, 0, H); bg.addColorStop(0, "#05060f"); bg.addColorStop(0.5, "#070a1a"); bg.addColorStop(1, "#04050c"); ctx.fillStyle = bg; ctx.fillRect(0, 0, W, H); // — İnce grid ctx.strokeStyle = "rgba(88,101,242,0.05)"; ctx.lineWidth = 1; const gs = 54; ctx.beginPath(); for (let x = 0; x < W; x += gs) { ctx.moveTo(x, 0); ctx.lineTo(x, H); } for (let y = 0; y < H; y += gs) { ctx.moveTo(0, y); ctx.lineTo(W, y); } ctx.stroke(); // — Bağlantılar (renk=ağırlık) for (const c of connectionCache) { const alpha = 0.05 + Math.abs(c.w) * 0.16; const col = c.w > 0 ? CONFIG.colorHot : CONFIG.colorCool; ctx.strokeStyle = rgba(col, alpha); ctx.lineWidth = Math.abs(c.w) * 1.6; ctx.beginPath(); ctx.moveTo(c.ax, c.ay); ctx.lineTo(c.bx, c.by); ctx.stroke(); } // — Nöronlar (parlama) nodePositions.forEach((col) => { for (const n of col) { const pulse = (Math.sin(now_t() * 1.2 + n.seed) + 1) / 2; const base = n.weight > 0 ? CONFIG.colorHot : CONFIG.colorCool; const hitC = lerpColor(base, [255,255,255], pulse * 0.4); const g = ctx.createRadialGradient(n.x, n.y, 0, n.x, n.y, n.radius * 4); g.addColorStop(0, rgba(hitC, 0.35 + pulse * 0.3)); g.addColorStop(1, rgba(hitC, 0)); ctx.fillStyle = g; ctx.beginPath(); ctx.arc(n.x, n.y, n.radius * 4, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = rgba(base, 0.5 + pulse * 0.5); ctx.beginPath(); ctx.arc(n.x, n.y, n.radius, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = rgba(hitC, 0.8); ctx.lineWidth = 1; ctx.stroke(); } }); // — Token akışı (NUMEX) ve aktivasyon darbeleri drawPulses(); drawTokens(); // — Alt grafik çizimi drawChart(); } let _t = 0; function now_t() { return _t; } // loop içinde _t güncellenir function drawPulses() { for (const e of pulseEvents) { const col = lerpColor(CONFIG.colorHot, [255,255,255], e.a); ctx.strokeStyle = rgba(col, e.a * 0.8); ctx.lineWidth = 2; ctx.beginPath(); ctx.arc(e.x, e.y, e.r, 0, Math.PI * 2); ctx.stroke(); const g = ctx.createRadialGradient(e.x, e.y, 0, e.x, e.y, e.r); g.addColorStop(0, rgba(col, e.a * 0.4)); g.addColorStop(1, rgba(col, 0)); ctx.fillStyle = g; ctx.beginPath(); ctx.arc(e.x, e.y, e.r, 0, Math.PI * 2); ctx.fill(); } } function drawTokens() { ctx.textAlign = "center"; ctx.textBaseline = "middle"; for (const p of tokenParticles) { // Bezier ara nokta const tt = p.t; const bx = (1 - tt) * (1 - tt) * p.x + 2 * (1 - tt) * tt * p.cx + tt * tt * p.tx; const by = (1 - tt) * (1 - tt) * p.y + 2 * (1 - tt) * tt * p.cy + tt * tt * p.ty; const fade = Math.sin(tt * Math.PI); // başta-sonda silik, ortada parlak // Harfin arkasına hafif glow ctx.shadowBlur = 14; ctx.shadowColor = rgba(CONFIG.colorText, 0.9); ctx.fillStyle = rgba(CONFIG.colorText, 0.35 + fade * 0.65); ctx.font = `700 ${p.size}px 'Segoe UI', system-ui, sans-serif`; ctx.fillText(p.letter, bx, by); ctx.shadowBlur = 0; // İz bırakma (çizgi) ctx.strokeStyle = rgba(CONFIG.colorText, fade * 0.18); ctx.lineWidth = 1.4; ctx.beginPath(); ctx.moveTo(p.x, p.y); ctx.lineTo(bx, by); ctx.stroke(); } } function drawChart() { cctx.clearRect(0, 0, chartCanvas.width, chartCanvas.height); cctx.strokeStyle = "rgba(255,255,255,0.06)"; cctx.lineWidth = 1; for (let i = 0; i <= 2; i++) { const y = (chartCanvas.height / 2) * i; cctx.beginPath(); cctx.moveTo(0, y); cctx.lineTo(chartCanvas.width, y); cctx.stroke(); } cctx.beginPath(); chartData.forEach((v, i) => { const x = (i / (chartData.length - 1)) * chartCanvas.width; const y = (1 - v) * chartCanvas.height; i === 0 ? cctx.moveTo(x, y) : cctx.lineTo(x, y); }); cctx.strokeStyle = rgba(CONFIG.colorHot, 0.9); cctx.lineWidth = 2; cctx.stroke(); } /* ---------- Başlangıç ---------- */ window.addEventListener("resize", resize); resize(); // Gerçek zaman _t güncellemesi için loop'a entegre const _origLoop = loop; function finalLoop(now) { _t = now / 1000; _origLoop(now); } requestAnimationFrame(finalLoop); })();