webdevpuneet.com

Floating Particles — Free HTML CSS JS Canvas Snippet

Floating Particles · Animations · Plain HTML, CSS & JS · Live preview

CategoryAnimations
TagsCanvas

Copy & paste

Source Code

<canvas id="canvas"></canvas>
<div class="content">
  <h1>Interactive Particles</h1>
  <p>Move your mouse to repel the particles</p>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Canvas 2D particle system with x,y position and vx,vy velocity per particle
Mouse proximity repel force pushes particles away when distance < 100px
Connection lines between all particle pairs within 120px
Line alpha = 1 - dist/120 — closer pairs have more opaque lines
Particles bounce off canvas edges by reversing velocity component
resize event reinitialises canvas dimensions and particle positions
requestAnimationFrame loop clears canvas and redraws all particles each frame
Export as HTML file, React JSX, or React + Tailwind CSS
Mobile (375px), Tablet (768px), Desktop device preview buttons
Live split-pane editor — preview updates as you type

About this UI Snippet

Floating Particles — Canvas Repel Force, Connection Lines & rAF Loop

Screenshot of the Floating Particles snippet rendered live

A floating particles canvas creates an ambient animated background with particles that drift, connect with lines when close, and flee from the cursor. Used on dark tech landing pages, AI product pages, and creative portfolios — see also the aurora background and gradient mesh hero.

The particle system

Each particle has x, y (position) and vx, vy (velocity). On each frame velocity is applied to position. Particles bounce off canvas edges by reversing velocity. When distance to mouse is less than 100px, a repulsion force pushes the particle away.

Connection lines

All particle pairs within 120px are connected with a line. Line alpha = 1 - dist/120 — closer particles have more opaque lines, creating a natural web.

Responsive canvas

A resize event listener resets canvas dimensions and reinitialises particles, preventing off-screen accumulation after window resize.

The mouse repel force

Each frame, every particle checks its distance to the mouse. If within a repel radius (default 100px), a force vector is computed: dx = particle.x - mouse.x; dy = particle.y - mouse.y; dist = Math.hypot(dx, dy). A repel acceleration is applied: particle.vx += (dx / dist) * force. The force is inversely proportional to distance — particles close to the mouse are pushed harder. This creates an organic "scattering" effect as the cursor moves through the field.

Connection lines

After updating all particle positions, a second pass draws lines between particles within 120px of each other. The line opacity is alpha = (1 - dist/MAX_DIST) * 0.4 — close particles get a visible line; far ones fade to invisible. This creates the characteristic network-graph appearance.

Boundary wrapping

When a particle drifts off the canvas edge, it reappears on the opposite side — x < 0 becomes x = canvas.width. This keeps the particle count stable without complex respawning logic.

Performance

With 80 particles, the O(n²) connection check performs 6,400 distance calculations per frame. For more particles, optimise with a spatial grid: divide the canvas into cells and only check particles in adjacent cells. For 80–120 particles on modern hardware, the naive approach runs at 60fps.

Configuring the particle system

Change PARTICLE_COUNT to adjust density (60–120 is a good range). Increase REPEL_RADIUS for a larger mouse influence zone. Reduce CONNECT_DIST to show fewer connection lines. Change particle speed by adjusting the initial velocity range. All configuration constants are defined at the top of the script for easy customisation without reading through the animation loop code.

Step by step

How to Use

  1. 1
    Move the cursorMove the cursor in the preview to see particles flee from the cursor. Connection lines appear between nearby particles.
  2. 2
    Change particle countIn the JS panel, update const COUNT = 80 to increase or decrease particle density.
  3. 3
    Change repel radiusUpdate the 100 in the distance < 100 repel check for a larger or smaller repel zone.
  4. 4
    Change connection distanceUpdate the 120 in the connection line check. Larger values create denser webs; smaller values create sparser connections.
  5. 5
    Change particle colourUpdate ctx.fillStyle and ctx.strokeStyle in the draw functions.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for a React component, or "Tailwind" for a React + Tailwind version.

Real-world uses

Common Use Cases

AI and data product hero backgrounds
Floating particles on a dark background communicate interconnected systems and emergent complexity — the defining visual language of AI, data analytics, and network products. The mouse repel adds interactivity that rewards cursor movement.
Dark ambient landing page sections
A fullscreen particle field behind hero content creates visual depth without requiring custom illustrations or videos. The subtle motion keeps the page feeling alive without distracting from the main message.
Learn Canvas 2D particle systems and mouse forces
Edit particle count, velocity range, repel radius, and connection distance in the JS panel. Each change directly demonstrates how parameter values affect the visual output — the best way to understand particle system fundamentals.
Network graph and data relationship demos
Extend the connection lines to represent data relationships. Add particle labels and vary line thickness by relationship strength. The base particle system provides the spatial layout and physics that a basic network graph needs.
Interactive screensaver and idle states
The mouse repel creates an interactive quality that keeps pages engaging during idle moments. Use as a screensaver-style animation that activates after a period of inactivity, then returns to normal UI on mouse movement.
Add click-to-burst and particle spawning
On canvas click, add 8-12 new particles at the click position with high outward velocity: particles.push(...Array.from({length: 10}, () => new Particle(e.clientX, e.clientY))). The burst disperses naturally via the existing physics loop.
Related: Matter.js Confetti Cannon
See the Matter.js Confetti Cannon for a related animations pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

Each frame, for every particle, the distance from particle to mouse is calculated. If dist < repelRadius (100px), a repulsion force is computed: force = repelStrength / dist. This force is added to vx and vy in the direction away from the mouse. Closer particles receive stronger repulsion because force is inversely proportional to distance.

For every pair of particles (nested loop), the distance is calculated. If dist < connectDist (120px), a line is drawn between them via ctx.beginPath(), ctx.moveTo(), ctx.lineTo(). The line globalAlpha is set to 1 - dist/connectDist — lines between closer particles are more opaque, creating a natural web density.

Update the COUNT constant for density (80 is default; 40 on mobile for performance). Update the velocity initialisation range: this.vx = (Math.random() - 0.5) * 1.5 — the multiplier (1.5) controls max speed. Higher values create faster, more energetic particles.

Reduce COUNT, increase the minimum connection distance (fewer lines drawn), and use a devicePixelRatio check to avoid rendering on high-DPI screens at double resolution. Add visibility change detection to pause the animation when the tab is not visible: document.addEventListener("visibilitychange", () => { if(document.hidden) cancelAnimationFrame(rafId); else startLoop(); })

Assign a colour to each particle on creation: this.color = colors[Math.floor(Math.random() * colors.length)]. Use ctx.fillStyle = particle.color before drawing. For connection lines, blend the two particle colours: ctx.strokeStyle = "rgba(r,g,b," + alpha + ")" where r,g,b are averaged from the two particle colours.

Yes. Use useRef on the canvas element and useEffect to initialise and start the animation loop. Store particles in a useRef array to avoid triggering re-renders on every frame. Clean up the rAF handle and mousemove/resize listeners in the useEffect return function: return () => { cancelAnimationFrame(rafId); window.removeEventListener(...) }