Анимированная визуализация нейронной сети — слои нейронов с пульсирующими связями и передачей сигнала. Подходит для AI проектов, лендингов и презентаций. Чистый HTML+CSS без библиотек.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Neural Network</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #020818;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
font-family: monospace;
padding: 20px;
}
canvas {
border-radius: 16px;
max-width: 100%;
}
.labels {
display: flex;
justify-content: space-between;
width: 100%;
max-width: 500px;
margin-top: 12px;
font-size: 11px;
color: rgba(255,255,255,0.3);
letter-spacing: 0.08em;
text-transform: uppercase;
}
</style>
</head>
<body>
<canvas id="c" width="500" height="320"></canvas>
<div class="labels">
<span>Input</span>
<span>Hidden 1</span>
<span>Hidden 2</span>
<span>Output</span>
</div>
<script>
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
var W = canvas.width;
var H = canvas.height;
// Архитектура сети
var layers = [4, 6, 6, 3];
// Цвета
var COLOR_NODE = 'rgba(0,207,255,0.9)';
var COLOR_ACTIVE = 'rgba(0,255,156,1)';
var COLOR_CONN = 'rgba(0,207,255,0.08)';
var COLOR_SIGNAL = 'rgba(0,255,156,1)';
var COLOR_OUTPUT = 'rgba(168,85,247,0.9)';
var NODE_R = 14;
var nodes = [];
var conns = [];
// Вычисляем позиции нейронов
var layerX = [];
for (var l = 0; l < layers.length; l++) {
layerX.push(W * 0.1 + l * (W * 0.8 / (layers.length - 1)));
}
for (var l = 0; l < layers.length; l++) {
nodes.push([]);
var count = layers[l];
for (var n = 0; n < count; n++) {
var y = H/2 + (n - (count-1)/2) * (H / (Math.max.apply(null, layers) + 1));
nodes[l].push({
x: layerX[l],
y: y,
active: 0,
pulse: Math.random() * Math.PI * 2
});
}
}
// Связи между слоями
for (var l = 0; l < layers.length - 1; l++) {
for (var a = 0; a < nodes[l].length; a++) {
for (var b = 0; b < nodes[l+1].length; b++) {
conns.push({
from: { l: l, n: a },
to: { l: l+1, n: b },
signal: -1, // -1 = нет сигнала
progress: 0,
active: false
});
}
}
}
// Сигнал который идёт по сети
var signalTimer = 0;
var signalLayer = 0;
var activeInputs = [];
function fireSignal() {
signalLayer = 0;
activeInputs = [];
// Активируем случайные входные нейроны
for (var n = 0; n < nodes[0].length; n++) {
if (Math.random() > 0.4) {
activeInputs.push(n);
nodes[0][n].active = 1;
}
}
// Активируем связи из входного слоя
for (var c = 0; c < conns.length; c++) {
var conn = conns[c];
if (conn.from.l === 0 && activeInputs.indexOf(conn.from.n) !== -1) {
conn.active = true;
conn.progress = 0;
}
}
}
function draw() {
ctx.clearRect(0, 0, W, H);
// Фон
ctx.fillStyle = '#020818';
ctx.fillRect(0, 0, W, H);
var t = Date.now() / 1000;
// Рисуем связи
for (var c = 0; c < conns.length; c++) {
var conn = conns[c];
var from = nodes[conn.from.l][conn.from.n];
var to = nodes[conn.to.l][conn.to.n];
// Базовая линия
ctx.beginPath();
ctx.moveTo(from.x, from.y);
ctx.lineTo(to.x, to.y);
ctx.strokeStyle = COLOR_CONN;
ctx.lineWidth = 1;
ctx.stroke();
// Сигнал
if (conn.active) {
conn.progress += 0.018;
var px = from.x + (to.x - from.x) * conn.progress;
var py = from.y + (to.y - from.y) * conn.progress;
// Линия сигнала
ctx.beginPath();
ctx.moveTo(from.x, from.y);
ctx.lineTo(px, py);
var grad = ctx.createLinearGradient(from.x, from.y, px, py);
grad.addColorStop(0, 'rgba(0,255,156,0)');
grad.addColorStop(1, 'rgba(0,255,156,0.8)');
ctx.strokeStyle = grad;
ctx.lineWidth = 1.5;
ctx.stroke();
// Точка сигнала
ctx.beginPath();
ctx.arc(px, py, 3, 0, Math.PI * 2);
ctx.fillStyle = COLOR_SIGNAL;
ctx.shadowColor = COLOR_SIGNAL;
ctx.shadowBlur = 10;
ctx.fill();
ctx.shadowBlur = 0;
// Дошёл до нейрона
if (conn.progress >= 1) {
conn.active = false;
conn.progress = 0;
var targetNode = nodes[conn.to.l][conn.to.n];
targetNode.active = 1;
// Активируем следующий слой
var nextLayer = conn.to.l + 1;
if (nextLayer < layers.length) {
for (var nc = 0; nc < conns.length; nc++) {
if (conns[nc].from.l === conn.to.l && conns[nc].from.n === conn.to.n) {
if (Math.random() > 0.3) {
conns[nc].active = true;
conns[nc].progress = 0;
}
}
}
}
}
}
}
// Рисуем нейроны
for (var l = 0; l < layers.length; l++) {
for (var n = 0; n < nodes[l].length; n++) {
var node = nodes[l][n];
// Затухание активации
if (node.active > 0) node.active -= 0.01;
if (node.active < 0) node.active = 0;
// Пульсация
var pulse = Math.sin(t * 2 + node.pulse) * 0.3 + 0.7;
// Цвет в зависимости от слоя
var isOutput = l === layers.length - 1;
var baseColor = isOutput ? '168,85,247' : '0,207,255';
var activeColor = isOutput ? '200,100,255' : '0,255,156';
var alpha = node.active > 0
? (0.5 + node.active * 0.5)
: (pulse * 0.4 + 0.2);
// Свечение активного нейрона
if (node.active > 0.3) {
ctx.beginPath();
ctx.arc(node.x, node.y, NODE_R * 2.5, 0, Math.PI * 2);
var glowGrad = ctx.createRadialGradient(node.x, node.y, 0, node.x, node.y, NODE_R * 2.5);
glowGrad.addColorStop(0, 'rgba(' + activeColor + ',' + (node.active * 0.3) + ')');
glowGrad.addColorStop(1, 'rgba(' + activeColor + ',0)');
ctx.fillStyle = glowGrad;
ctx.fill();
}
// Внешний круг
ctx.beginPath();
ctx.arc(node.x, node.y, NODE_R, 0, Math.PI * 2);
ctx.strokeStyle = node.active > 0.3
? 'rgba(' + activeColor + ',' + alpha + ')'
: 'rgba(' + baseColor + ',' + (alpha * 0.6) + ')';
ctx.lineWidth = node.active > 0.3 ? 2 : 1;
ctx.stroke();
// Внутренний круг
ctx.beginPath();
ctx.arc(node.x, node.y, NODE_R * 0.5, 0, Math.PI * 2);
var nodeGrad = ctx.createRadialGradient(
node.x - 3, node.y - 3, 1,
node.x, node.y, NODE_R * 0.5
);
nodeGrad.addColorStop(0, 'rgba(' + (node.active > 0.3 ? activeColor : baseColor) + ',' + alpha + ')');
nodeGrad.addColorStop(1, 'rgba(' + baseColor + ',' + (alpha * 0.3) + ')');
ctx.fillStyle = nodeGrad;
ctx.shadowColor = node.active > 0.3 ? 'rgba(' + activeColor + ',0.8)' : 'transparent';
ctx.shadowBlur = node.active > 0.3 ? 15 : 0;
ctx.fill();
ctx.shadowBlur = 0;
}
}
// Запускаем новый сигнал каждые 2 сек
signalTimer++;
if (signalTimer > 120) {
signalTimer = 0;
fireSignal();
}
requestAnimationFrame(draw);
}
// Первый сигнал через 0.5 сек
setTimeout(fireSignal, 500);
draw();
</script>
</body>
</html>
👁 1