Slavko Babić
All experiments

January 22, 2026

Teaching an SVG to fire

The little animated neural network from the home hero, taken apart — signals travelling along synapses and nodes pulsing, in pure SVG and CSS.

The animations are illustrative — built for presentation purposes.

The small animated network on the home page started as a doodle: could I make a neural net feel like it’s thinking using nothing but SVG paths and CSS keyframes — no JavaScript at all? This is that doodle, on its own.

There are two tricks doing all the work. The signals travelling along the edges are a single dash pushed around each path: set pathLength="1" so every path is normalised to the same length regardless of its real geometry, give it a short stroke-dasharray, and animate stroke-dashoffset — the dash slides along like a packet moving between neurons. Staggering the delays across a few paths makes it look like traffic rather than a metronome.

The nodes just pulse — a scale-and-opacity keyframe on the “hot” ones — with the same offset delays so the pulse looks like it’s caused by the arriving signal. It isn’t, of course; they’re independent animations that happen to rhyme. That’s most of what stylised motion is: not simulation, just a handful of cues arranged so the eye tells itself a story.

Like everything else on the site it takes its colours from the theme tokens, so it’s violet-on-dark or violet-on-light for free, and prefers-reduced-motion freezes it into a calm static diagram. Cheap, tiny, and it scratched the itch.

The whole experiment fits in one HTML file — copy it or download it:

Source code — neural-net.html(131 lines)Download .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>

Technologies

  • SVG
  • CSS animation

Topics

  • Neural networks