Slavko Babić
Svi eksperimenti

22. januar 2026.

Kako animirati neuronsku mrežu u SVG-u

Mala animirana neuronska mreža sa naslovne strane, rastavljena na delove — signali putuju kroz veze, čvorovi pulsiraju, sve u čistom SVG-u i CSS-u.

Animacije su ilustrativne — urađene za potrebe prezentacije.

Mala animirana mreža sa naslovne strane počela je kao mali eksperiment: može li neuronska mreža da deluje kao da razmišlja koristeći samo SVG putanje i CSS keyframe-ove, bez ijednog reda JavaScripta? Ovo je taj eksperiment, izdvojen u posebnu stranicu.

Sve se svodi na dva jednostavna trika. Signali koji putuju kroz veze zapravo su samo jedna kratka crtica koja klizi duž putanje. Postaviš pathLength="1" da svaka putanja bude normalizovana na istu dužinu, dodaš kratak stroke-dasharray i animiraš stroke-dashoffset — i izgleda kao da paket putuje između neurona. Kada malo razmakneš kašnjenja između putanja, dobiješ utisak saobraćaja umesto metronoma.

Čvorovi samo pulsiraju — jednostavna animacija veličine i providnosti na aktivnim čvorovima. Kašnjenja su usklađena sa signalima, pa izgleda kao da puls nastaje baš u trenutku kada signal stigne. Naravno, nije tako. To su dve potpuno nezavisne animacije koje se samo lepo poklope. Veći deo stilizovane animacije upravo je u tome: ne simuliraš stvarnost, već složiš nekoliko vizuelnih nagoveštaja od kojih oko samo napravi priču.

Kao i sve ostalo na sajtu, boje preuzima iz tokena teme, pa automatski radi u svetlom i tamnom režimu. Ako je uključen prefers-reduced-motion, animacija se zamrzne u miran statičan dijagram. Malo koda, bez biblioteka i sasvim dovoljno da zadovoljim radoznalost.

Ceo eksperiment staje u jedan HTML fajl — kopiraj ga ili preuzmi:

Izvorni kod — neural-net.html(131 linija)Preuzmi .html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Neural net — SVG + CSS, no JavaScript</title>
<!--
  A tiny animated neural network in pure SVG + CSS — no JavaScript.
  From slavkobabic.com/experiments — copy freely.

  The two tricks:
  - signals: a short dash pushed around each path (pathLength="1" normalises
    every path to the same length; animating stroke-dashoffset slides the dash)
  - nodes: a scale/opacity pulse with the same staggered delays, so the pulse
    *looks* caused by the arriving signal.
-->
<style>
  :root {
    color-scheme: dark;
    --bg: #0f0e17;
    --bg-raised: #1a1826;
    --fg-muted: #a09cb0;
    --border: #312e40;
    --accent: #a78bfa;
    --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  }

  @media (prefers-color-scheme: light) {
    :root {
      color-scheme: light;
      --bg: #fbfaff;
      --bg-raised: #f1effa;
      --fg-muted: #5a5568;
      --border: #dedbe8;
      --accent: #6d28d9;
    }
  }

  body {
    margin: 0;
    min-height: 100vh;
    display: grid;
    place-items: center;
    background: var(--bg);
  }

  .net {
    width: min(90vw, 480px);
    height: auto;
  }

  .edge {
    stroke: var(--border);
    stroke-width: 1.2;
    fill: none;
  }

  /* Signal packets travelling along a synapse: a short dash pushed around
     the normalized (pathLength=1) path. */
  .flow {
    stroke: var(--accent);
    stroke-width: 1.6;
    fill: none;
    stroke-dasharray: 0.12 0.88;
    animation: netflow 3.2s linear infinite;
    opacity: 0.9;
  }

  .flow.f2 { animation-delay: -1.1s; }
  .flow.f3 { animation-delay: -2.2s; }

  .node {
    fill: var(--bg-raised);
    stroke: var(--fg-muted);
    stroke-width: 1.2;
  }

  .node.hot {
    fill: var(--accent);
    stroke: var(--accent);
    transform-box: fill-box;
    transform-origin: center;
    animation: netpulse 3.2s var(--ease-out) infinite;
  }

  .node.hot.p2 { animation-delay: -1.1s; }
  .node.hot.p3 { animation-delay: -2.2s; }

  @keyframes netflow {
    to { stroke-dashoffset: -1; }
  }

  @keyframes netpulse {
    0%, 100% { transform: scale(1); opacity: 0.75; }
    50% { transform: scale(1.35); opacity: 1; }
  }

  /* Reduced motion: freeze into a calm static diagram. */
  @media (prefers-reduced-motion: reduce) {
    * { animation: none !important; }
  }
</style>
</head>
<body>

<svg class="net" viewBox="0 0 300 220" aria-hidden="true">
  <path class="edge" d="M40 40 L150 30" />
  <path class="edge" d="M40 40 L150 110" />
  <path class="edge" d="M40 110 L150 30" />
  <path class="edge" d="M40 110 L150 110" />
  <path class="edge" d="M40 180 L150 110" />
  <path class="edge" d="M40 180 L150 190" />
  <path class="edge" d="M150 30 L260 75" />
  <path class="edge" d="M150 110 L260 75" />
  <path class="edge" d="M150 110 L260 145" />
  <path class="edge" d="M150 190 L260 145" />
  <path class="flow" d="M40 40 L150 30 L260 75" pathLength="1" />
  <path class="flow f2" d="M40 110 L150 110 L260 145" pathLength="1" />
  <path class="flow f3" d="M40 180 L150 110 L260 75" pathLength="1" />
  <circle class="node" cx="40" cy="40" r="7" />
  <circle class="node hot" cx="40" cy="110" r="7" />
  <circle class="node" cx="40" cy="180" r="7" />
  <circle class="node hot p2" cx="150" cy="30" r="8" />
  <circle class="node hot p3" cx="150" cy="110" r="8" />
  <circle class="node" cx="150" cy="190" r="8" />
  <circle class="node hot" cx="260" cy="75" r="7" />
  <circle class="node hot p2" cx="260" cy="145" r="7" />
</svg>

</body>
</html>

Tehnologije

  • SVG
  • CSS animation

Teme

  • Neuronske mreže