Particle Network — Free HTML CSS JS Constellation Snippet
Particle Network · Animations · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Particle Network — Connected Constellation Reacting to the Cursor

The particle network is the classic interactive backdrop where dozens of dots drift across the screen and draw connecting lines whenever they come near each other, forming an ever-shifting constellation that also reaches toward your cursor. This snippet builds it on an HTML <canvas> with plain vanilla JavaScript — sharp on retina screens, with no library.
Drifting nodes
Each node is a point with a position and a slow random velocity. Every frame the loop advances each node by its velocity and bounces it off the edges by flipping the relevant velocity component when it crosses a boundary. This gives a gentle, perpetual drift with nodes wandering and ricocheting softly off the walls — alive but calm. Node count scales with viewport width (min(W, 900) / 12), so the field stays appropriately dense on phones and desktops alike, recomputed on resize.
Connecting nearby nodes
The defining behavior is the linking. A double loop checks every pair of nodes, and when two are within LINK pixels (130), it draws a line between them whose opacity is 1 - distance / LINK — so links are brightest when nodes are close and fade to nothing as they drift apart. The result is a web that continuously forms and dissolves as nodes move, the hallmark of the constellation effect. This pairwise check is O(n²), which is why keeping the node count modest matters for smoothness.
Reaching toward the cursor
The cursor participates in the network. For each node, the loop also measures the distance to the mouse and, within a larger radius (LINK * 1.5), draws a brighter line from the node to the cursor. So as you move, the nearby part of the web stretches toward your pointer like it is being attracted — the interactive touch that makes the backdrop feel responsive. When the pointer leaves, the mouse position is parked far off-screen so those links disappear.
Crisp on every display
Canvas is resolution-dependent, so resize() reads devicePixelRatio (capped at 2 for performance), sizes the backing buffer accordingly, and applies a setTransform so all drawing is done in CSS pixels but rendered at device resolution. Without this the dots and lines would look soft on HiDPI screens. The handler also re-seeds the node field for the new dimensions.
Layering
The canvas fills the hero behind the content, which sits above with pointer-events: none so moving over the headline still feeds the cursor interaction below. A radial background gradient gives the constellation a subtle glow at the center and darkens the edges.
Customizing it
Change LINK for a denser or sparser web, adjust the node count divisor and velocity range for more or fewer, faster or slower nodes, recolor via the COLOR RGB string, or widen the cursor reach. For large fields, swap the O(n²) check for a spatial grid. Pair it with an aurora text headline or a dot pattern section for a connected, modern hero.
Build with AI
Build, Understand, Optimize, and Extend It With AI
You don't have to trace the pairwise distance checks by hand to understand what's expensive here. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why the link-drawing loop over every pair of nodes is O(n squared), and how the LINK radius and the 1 minus distance over LINK opacity formula combine to fade lines in and out. The same assistant is well suited to optimizing it, for example replacing the brute-force pairwise check with a spatial grid or quadtree so the node count can scale into the thousands without dropping frames, or checking whether the devicePixelRatio cap of 2 is the right tradeoff for a given device mix. It's also a fast way to extend the effect: ask it to make nodes actively repel or attract each other instead of just bouncing off walls, color links by a data value instead of a flat RGB string, or add a subtle pulse to nodes when a new connection forms. Treat the code less like a finished artifact and more like a starting point for a conversation.
Prompt to recreate it
Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:
Build a drifting "particle network" constellation background in plain HTML, CSS, and vanilla JavaScript using only the Canvas 2D API and requestAnimationFrame — no libraries, no WebGL.
Requirements:
- A full-bleed canvas positioned behind page content, sized from devicePixelRatio (capped at 2) via a resize function that also rebuilds the node array so the canvas stays sharp on retina displays and re-seeds when the viewport size changes.
- Generate a set of nodes whose count scales with viewport width (for example, roughly one node per 12px of width up to a cap), each with a random starting position and a small random velocity.
- Every animation frame, move each node by its velocity and reverse the relevant velocity component whenever the node's x or y crosses the canvas bounds, so nodes drift and bounce indefinitely.
- Each frame, check every pair of nodes and draw a line between any two within a fixed link radius, with the line's opacity computed as 1 minus their distance divided by that radius, so nearby pairs glow brighter and the connection fades out smoothly as nodes separate.
- Track the pointer position over the canvas's container and, for each node within a slightly larger radius of the pointer, draw a brighter line from the node to the pointer, so the nearest part of the network visibly reaches toward the cursor. When the pointer leaves, move its tracked position off-screen so those links disappear.
- Draw all nodes as small filled circles on top of the links each frame, and make sure the content layered on top of the canvas has pointer-events: none where needed so cursor interaction still reaches the canvas underneath.Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.
Step by step
How to Use
- 1Paste HTML, CSS, and JSA drifting field of dots fills the hero behind the text.
- 2Watch the linksLines form between nearby nodes and fade as they part.
- 3Move your cursorNearby nodes reach out and link to the pointer.
- 4Resize the windowThe node count and canvas re-fit and stay crisp.
- 5Recolor the webChange the COLOR RGB string.
- 6Tune the densityAdjust LINK, node count, and velocity.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
A double loop checks every pair of nodes each frame, and when two are within the link radius it draws a line whose opacity is 1 minus distance over the radius. So links are brightest when nodes are close and fade out as they drift apart, producing a web that continually forms and dissolves as the nodes move.
For each node the loop also measures the distance to the mouse and, within a larger radius, draws a brighter line from the node to the cursor. As you move, the nearby part of the web stretches toward the pointer. When the pointer leaves, the mouse position is moved far off-screen so those links vanish.
Yes. The resize function reads devicePixelRatio (capped at 2), sizes the canvas backing buffer to the CSS size times that ratio, and applies a setTransform so drawing happens in CSS pixels but renders at device resolution. Without this, the dots and thin lines would look blurry on HiDPI displays.
It is O(n squared), since every pair of nodes is checked each frame, so the node count is kept modest and scales with viewport width. That is smooth for typical hero fields. For much larger networks you would replace the brute-force check with a spatial grid or quadtree to only compare nearby nodes.
Put the canvas behind your content with a ref and run resize plus the rAF draw loop in a mount effect, cancelling the frame and removing listeners on unmount. Keep nodes and the mouse position in refs, not state, so the loop does not trigger re-renders. The component is framework-agnostic; in Tailwind just position the canvas absolute inset-0 behind a relative content layer.