Trail Effect
Colored particle trail following the cursor — each particle spins, shrinks and fades out progressively on a 2D canvas.
You'll get access to the interactive demo with a free account.
This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Atmosphere has 57 effects, including 10 free. Explore the category →
3 usage examples



How it works
On each mousemove event, the IIFE spawns 3 particles at the cursor position: random velocity within ±2 px/frame, initial size 2–6 px, a random color from a 5-color palette, and a normalized lifetime of 1. The requestAnimationFrame loop continuously updates and redraws all active particles.
The trail effect relies on partial canvas erasure: instead of a full clearRect, each frame starts with ctx.fillStyle = 'rgba(10,10,15,0.12)'. The 88% residual opacity accumulates previous frames and naturally forms the glowing tail without storing historical positions.
Each particle loses decay life (0.015–0.035 per frame, randomly drawn), shrinks by a factor of 0.98 per frame, and spins at a random angle (rotSpeed within ±4°/frame). The globalAlpha is set to the current life value — the fade-out is progressive, with no external interpolation table.
The canvas is sized once at initialization via getBoundingClientRect() on the parentElement. No IntersectionObserver is present — the RAF runs continuously even off-screen. The effect is bound to the fixed id psTrail: one instance per page in the delivered code.
Accessibility
- No prefers-reduced-motion: the code does not check
window.matchMedia('(prefers-reduced-motion:reduce)')— the animation runs even if the OS disables it. Add the check before the firstrequestAnimationFramecall to cut the effect. - The
<canvas>does not carryaria-hidden="true"in the delivered code — add it to hide this semantics-free node from screen readers. - The
.ps-hinttext ('Move your mouse') is a real text node, accessible to screen readers — but its content is only relevant to mouse users. - Interaction is exclusively
mousemove: notouchmoveor keyboard event — mobile and keyboard users cannot trigger the particles. - The
.ps-hinttext renders atrgba(255,255,255,0.3)on a#0a0a0fbackground — a contrast ratio of ≈1.8:1, well below the WCAG AA threshold (4.5:1). Increase opacity if the text is functional.
Browser compatibility
Requires Canvas 2D Context and requestAnimationFrame — available in all modern browsers since 2013. Zero external dependencies.
If <code>canvas</code> is not supported, <code>setupCanvas()</code> returns <code>null</code> and the IIFE exits immediately (<code>if (!t) return</code>) — no JS errors, the page stays functional.
The code
HTML structure to paste into your page (CSS + JS available with a premium account):
<div class="demo-preview">
<div class="ps-canvas-wrap">
<canvas class="ps-canvas" id="psTrail"
aria-hidden="true"
width="392" height="280"></canvas>
<span class="ps-hint">Move your mouse</span>
</div>
</div>
Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
| o = [...] | ['#8b5cf6','#ec4899','#06b6d4','#f59e0b','#10b981'] | Particle color palette. Array of hex or rgba strings — swap in your brand colors. |
| for (let t=0;t<3;t++) | 3 particles/event | Trail density: number of particles spawned per mouse move. Increase for a dense trail, reduce for a subtle effect. |
| vx: 2*(Math.random()-.5) | ±2 px/frame | Initial velocity multiplier (same value for vx and vy). 1 = particles tight around cursor, 4 = wide dispersion. |
| size: 4*Math.random()+2 | 2–6 px | Initial size range. Change the amplitude (4) and minimum (2) — e.g. 8*Math.random()+4 for larger particles. |
| decay: .02*Math.random()+.015 | 0.015–0.035/frame | Fade speed. Raise toward 0.04 for ephemeral particles, lower to 0.008 for a long-lasting trail. |
| n.size *= .98 | 0.98× per frame | Shrink factor. 0.99 = gentle reduction (particle visible longer), 0.94 = fast disappearance. |
| 'rgba(10,10,15,0.12)' | alpha 0.12 | Opacity of the partial background fill — controls trail length. 0.06 = very long tail, 0.25 = short, crisp particles. |
FAQ
psTrail via document.getElementById. For multiple zones, copy the JS block and replace "psTrail" with a different id in each copy — the setupCanvas() function is global and reusable.ctx.fillStyle = 'rgba(10,10,15,0.12)' replaces the usual clearRect. Lowering the alpha coefficient (e.g. 0.06) lengthens the trail by retaining previous frames longer; raising it (e.g. 0.25) shortens it for sharper particles.mousemove event is listened to. For touch support, add a touchmove listener that reads e.touches[0].clientX/Y and calls the same particle emission logic.