webdevpuneet.com

Three.js Magnetic Particles — Cursor-Repelled WebGL Particle Field

Three.js Magnetic Particles · Animations · Plain HTML, CSS & JS · Live preview

CategoryAnimations

Copy & paste

Source Code

Requires
<canvas id="magCanvas"></canvas>
<div class="mp-caption">Move your cursor to repel the field</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

Invisible pick plane: an unrendered mesh gives the raycaster a real 3D surface to intersect for cursor position
Per-particle spring-and-repel physics: two independent forces combine into "dent and heal" with no special-case code
Distance-scaled repulsion: particles near the cursor push harder than particles near the edge of the affected radius
Continuous damping: velocity decays every frame regardless of active forces, letting particles genuinely settle
Velocity-driven color: brightness reflects how fast a particle is currently moving, not just its displaced position
Fully recoverable field: removing the cursor lets every particle return exactly to its original grid slot
GPU point cloud: thousands of particles rendered in a single THREE.Points draw call
Loaded entirely from a CDN: no npm install, bundler, or build step required

About this UI Snippet

How to Build a Cursor-Repelled Magnetic Particle Field in Three.js

Screenshot of the Three.js Magnetic Particles snippet rendered live

The Three.js Magnetic Particles snippet fills the screen with a grid of thousands of points that visibly repel away from the cursor as it approaches, then spring smoothly back to their original resting positions once it moves away — like iron filings reacting to a moving magnet — using THREE.Raycaster against an invisible plane, all with core Three.js loaded from a CDN.

An invisible plane turns a 2D cursor into a 3D field position

The particle grid lives entirely at z = 0, but the cursor is just two screen coordinates. To get a real 3D point that matches the particle grid's own plane, the snippet raycasts against a completely invisible THREE.Mesh — a flat plane, sized generously, given a MeshBasicMaterial with visible: false. It never renders, but it's still a real object the raycaster can intersect, so raycaster.intersectObject(pickPlane) reliably returns exactly where the cursor "touches" the Z=0 plane the particles occupy.

Two forces per particle, every frame: spring home, repel from cursor

Each particle carries its own stored home position (its resting grid slot) and its own velocity. Every frame, two forces are added into that velocity: a spring force pulling gently back toward home — proportional to how far the particle currently is from it — and, if the cursor is nearby, a repulsion force pushing directly away from the cursor's 3D position, scaled by proximity. These two forces run simultaneously and independently for every particle; the *visible* behavior — a "dent" pushed away from the cursor that heals back to a flat grid — falls entirely out of that combination, with no special-case logic for "restore the grid" anywhere in the code.

Repulsion strength falls off with distance, not a fixed push

A particle's repulsion force is scaled by (1 - distance/REPEL_RADIUS), so particles right next to the cursor get pushed hard while particles near the edge of the affected radius barely feel it — producing a smooth, rounded dent in the field rather than a sharp-edged crater with a uniform push inside a hard boundary.

Damping keeps the system stable, not just decorative

Every particle's velocity is multiplied by a DAMPING factor just under 1, every single frame, regardless of whether any force is currently acting on it. Without this, the spring force alone would cause every particle to oscillate back and forth around its home position forever, like a frictionless pendulum; damping is what lets particles actually *settle* back into place rather than perpetually overshooting.

Color intensity is driven by velocity, not position

Rather than tying color to a particle's distance from home (which is what most similar effects do), this snippet ties each particle's brightness to how fast it's currently *moving* — its velocity magnitude. This means particles caught in the middle of being pushed away glow visibly brighter than particles that have already reached a far, but now-stationary, displaced position, giving the "active disturbance" a clearer visual read than a static-position-based color would.

Where this spring-plus-repulsion pattern is used

This exact spring-toward-home-plus-repel-from-cursor combination is the foundation of interactive particle logos, magnetic hover effects, and "iron filings" style physics toys across the web. Compare its flat, mouse-driven field against the raycasted surface deformation in interactive mesh distortion, or pair it with a custom cursor snippet for a fully cursor-reactive page.

Step by step

How to Use

  1. 1
    Load the Three.js CDNAdd three.min.js from the CDN panel — Raycaster is part of core Three.js.
  2. 2
    Paste HTML, CSS, and JSA flat grid of particles appears; move your cursor over it to see the effect.
  3. 3
    Move your cursor aroundNearby particles are pushed away and glow brighter while displaced.
  4. 4
    Move away and watch it healParticles spring smoothly back to their resting grid positions once undisturbed.
  5. 5
    Tune the repulsionAdjust REPEL_RADIUS and REPEL_STRENGTH for a wider, gentler, or more localized, forceful push.
  6. 6
    Tune the spring feelRaise SPRING for a snappier return, or raise DAMPING (closer to 1) for a slower, floatier settle.

Real-world uses

Common Use Cases

Interactive hero backgrounds
A cursor-reactive particle field invites exploration and increases time-on-page for a hero section.
Digital art and experimental portfolios
A physics-feeling, cursor-controlled field suits generative art and experience-driven creative sites.
Teaching raycasting and spring physics
A complete, focused example of converting a 2D cursor into a 3D interaction and combining two independent forces.
Brand and agency showpieces
Demonstrates genuine interactive WebGL competence, standing out from static or purely time-driven backgrounds.
Loading and menu backgrounds
A responsive, playable particle field gives visitors something satisfying to interact with while a page loads.
Sci-fi and tech product visuals
Pair with a particle network or particle wave for a broader particle-themed visual language.

Got questions?

Frequently Asked Questions

A completely invisible plane mesh sits at the same Z position as the particle grid. Every frame, a Raycaster is cast from the camera through the cursor's normalized screen coordinates, and its intersection with that invisible plane gives an exact 3D point on the particle grid's own plane — even though the plane itself never renders anything.

Every particle stores its own resting "home" position from when the grid was first built. Each frame, a spring force proportional to the distance between a particle's current position and its home position is added to its velocity, constantly pulling it back — this force runs regardless of whether the cursor is nearby, so once repulsion stops, the spring force alone eventually returns the particle home.

Without constant damping, the spring force alone would cause every particle to oscillate back and forth around its home position indefinitely, like a frictionless pendulum. Multiplying velocity by a factor just under 1 every single frame bleeds off a small amount of energy continuously, which is what allows particles to actually settle into place rather than overshoot forever.

Tying brightness to how fast a particle is currently moving highlights the active, in-motion disturbance more clearly than tying it to distance from home would. A particle that has already been pushed far away but is now stationary reads as "settled," while a particle currently accelerating away from the cursor reads as visibly brighter and more active.

Yes. Raise REPEL_RADIUS to affect particles further from the cursor, and raise REPEL_STRENGTH for a more forceful push within that radius. Adjust SPRING and DAMPING alongside them to keep the return-to-home motion feeling proportionate to the new repulsion strength.

Yes. Click JSX for a React component, Vue for a Vue 3 SFC, Angular for a standalone component, or Tailwind for a React + Tailwind CSS utility-class version. Attach the pointermove and pointerleave listeners to the canvas ref inside a mount effect, keep the velocity and home arrays in refs, and call renderer.dispose() plus remove the listeners on cleanup.