htmlintermediatemitFeatured

Particle Field

A drifting field of connected particles.

4.8k1.9k655265

About this module

Particles drift slowly while nearby particles are connected with lines — the classic networking effect.

#canvas#2d#particles#connections#network

Source code

Installation

  1. 1

    Create the files

    Create `index.html`, `style.css` and `script.js` in the same folder.

  2. 2

    Paste the code

    Copy each file's source into its matching file.

  3. 3

    Open in the browser

    Open `index.html` in any modern browser.

Usage

  1. 1

    Copy the HTML

    Paste the canvas markup where you need it.

    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
    <div class="mh-canvas-wrap">
      <canvas id="mh-canvas"></canvas>
      <span class="mh-canvas-label">PARTICLE FIELD</span>
    </div>
  2. 2

    Wire up the script

    Include `script.js` before the closing body tag.

    const canvas = document.getElementById("mh-canvas");
    const ctx = canvas.getContext("2d");
    let w, h, dpr = Math.min(window.devicePixelRatio || 1, 2);
    const CONFIG = { count: 90, linkDist: 120, speed: 0.35, color: "#818cf8" };
    let parts = [];
    function resize() {
      const rect = canvas.parentElement.getBoundingClientRect();
      w = rect.width; h = rect.height;
      canvas.width = w * dpr; canvas.height = h * dpr;
      canvas.style.width = w + "px"; canvas.style.height = h + "px";
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
      parts = Array.from({ length: CONFIG.count }, () => ({
        x: Math.random() * w, y: Math.random() * h,
        vx: (Math.random() - 0.5) * CONFIG.speed,
        vy: (Math.random() - 0.5) * CONFIG.speed
      }));
    }
    function tick() {
      ctx.clearRect(0, 0, w, h);
      for (const p of parts) {
        p.x += p.vx; p.y += p.vy;
        if (p.x < 0 || p.x > w) p.vx *= -1;
        if (p.y < 0 || p.y > h) p.vy *= -1;
        ctx.beginPath(); ctx.arc(p.x, p.y, 2, 0, Math.PI * 2);
        ctx.fillStyle = CONFIG.color; ctx.fill();
      }
      for (let i = 0; i < parts.length; i++) {
        for (let j = i + 1; j < parts.length; j++) {
          const a = parts[i], b = parts[j];
          const d = Math.hypot(a.x - b.x, a.y - b.y);
          if (d < CONFIG.linkDist) {
            ctx.beginPath(); ctx.moveTo(a.x, a.y); ctx.lineTo(b.x, b.y);
            ctx.strokeStyle = CONFIG.color;
            ctx.globalAlpha = (1 - d / CONFIG.linkDist) * 0.5;
            ctx.stroke(); ctx.globalAlpha = 1;
          }
        }
      }
      requestAnimationFrame(tick);
    }
    resize(); window.addEventListener("resize", resize); tick();
Overview
Categoryanimations
Frameworkhtml
Difficultyintermediate
Licensemit
Versionv1.0.0
Features
  • requestAnimationFrame loop
  • DPR-aware canvas
  • Pauses on hidden tab automatically
Traits
  • ResponsiveYes
  • Dark modeYes
  • AccessibleYes
  • Production readyYes
Browser support
  • Chrome 90+
  • Firefox 90+
  • Safari 15+
  • Edge 90+

Related modules

Command Palette

Search modules and jump anywhere