Atmosphere✨ Premium

Trail Effect

Colored particle trail following the cursor — each particle spins, shrinks and fades out progressively on a 2D canvas.

JSCanvasMouse

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

Hero / Landing — Homepage of a creative tool — Trail Effect example 1

① Hero / Landing — Homepage of a creative tool

WhenThe user lands on the homepage of a design or code tool — before any click, they explore the page with their mouse.
WhyThe colored trail instantly rewards movement and creates a strong interactive first impression without blocking the overlay text message.
SettingsPalette reduced to brand violet/indigo (o=['#6366f1','#8b5cf6','#a78bfa']), 5 particles per event (change 3 to 5), fade alpha 0.10 for a slightly longer trail.
Product component — Interactive preview zone in a dashboard — Trail Effect example 2

② Product component — Interactive preview zone in a dashboard

WhenAn editor or dashboard exposes a live preview zone that the user hovers over with the mouse.
WhyFine, low-speed particles signal the zone's reactivity without visually overloading a data-dense context.
SettingsCyan/blue palette (o=['#06b6d4','#0ea5e9','#38bdf8']), 2 particles per event, speed 1.2*(Math.random()-.5), size 1.5–3 px (2*Math.random()+1.5).
Drawing app — Full-screen creative annotation canvas — Trail Effect example 3

③ Drawing app — Full-screen creative annotation canvas

WhenThe user opens a drawing or annotation tool and traces freehand shapes on a blank surface.
WhyThe multicolor trail becomes the main visual feedback of the gesture: each mouse pass leaves a persistent glowing tail that instantly turns the stroke into an artistic experience, with no additional rendering logic required.
SettingsVivid rainbow palette (o=['#8b5cf6','#ec4899','#f59e0b','#10b981','#06b6d4']), 5 particles per event, fade alpha 0.06 for a very persistent trail (the accumulation simulates a brush), decay .015*Math.random()+.010.

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 first requestAnimationFrame call to cut the effect.
  • The <canvas> does not carry aria-hidden="true" in the delivered code — add it to hide this semantics-free node from screen readers.
  • The .ps-hint text ('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: no touchmove or keyboard event — mobile and keyboard users cannot trigger the particles.
  • The .ps-hint text renders at rgba(255,255,255,0.3) on a #0a0a0f background — 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.

Chrome 88+✓ Full
Firefox 87+✓ Full
Safari 15+✓ Full
Edge 88+✓ Full
Mobile iOS✓ Partial — touch not handled
Android Chrome✓ Partial — touch not handled

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):

index.html — structure
<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>
🔒 Unlock the full code — from €2.99 the first month

Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
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

Not directly: the IIFE targets the fixed id 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.
The trail comes from partial canvas erasure: 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.
No in the delivered code: only the 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.