Skip to content

Friction Effects

Friction controls how quickly particles slow down and return to their original positions after being displaced by cursor interaction. It’s the key parameter that determines whether your particle animation feels smooth and calm, or energetic and bouncy. Understanding friction is essential for creating the exact feel you want for your interactive text.

Friction in ParticleText.js works like real-world friction - it’s a velocity dampening factor applied to each particle every frame. The friction value is a number between 0 and 1:

  • Higher friction (0.95-0.99): Particles slow down quickly → smooth, damped movement
  • Medium friction (0.85-0.93): Balanced movement → natural feel
  • Lower friction (0.70-0.85): Particles slow down slowly → bouncy, energetic movement

Each animation frame, a particle’s velocity is multiplied by the friction value:

New Velocity = Current Velocity × Friction
Example with friction 0.9:
Frame 1: velocity = 10 → 10 × 0.9 = 9
Frame 2: velocity = 9 → 9 × 0.9 = 8.1
Frame 3: velocity = 8.1 → 8.1 × 0.9 = 7.29
...and so on until particle stops

High friction (closer to 1) means velocity decreases slowly - particles overshoot and bounce back.

Low friction (closer to 0) means velocity decreases quickly - particles stop without bouncing.

friction: {
base: 0.9, // Base friction value (0-1)
rand: 0.05 // Random variation per particle
}
// Final friction per particle: base to (base + rand)
// Example: 0.9 to 0.95
  • base: The minimum friction value
  • rand: Random amount added to each particle (creates organic variation)

Very high friction (0.95) creates slow, damped movement with minimal overshoot.

High Friction (0.95) - Slow & Smooth

initParticleJS('#canvas', {
text: 'SMOOTH',
friction: {
base: 0.95, // Very high friction
rand: 0.03 // Small variation
},
colors: ['#3498DB', '#2980B9']
});

Try it: Move your cursor over the text. Notice how particles move slowly and return to position without bouncing.

Best for: Professional sites, calm aesthetics, subtle interactions, corporate brands.

Medium friction (0.9) creates natural, balanced movement - the recommended default.

Medium Friction (0.9) - Balanced

initParticleJS('#canvas', {
text: 'BALANCED',
friction: {
base: 0.9, // Medium friction (default)
rand: 0.05 // Moderate variation
},
colors: ['#95A5A6', '#7F8C8D']
});

Try it: The movement feels natural and responsive without being sluggish or chaotic.

Best for: General purpose, most applications, landing pages, portfolios.

Low friction (0.8) creates fast, energetic movement with visible overshoot and oscillation.

Low Friction (0.8) - Fast & Bouncy

initParticleJS('#canvas', {
text: 'BOUNCY',
friction: {
base: 0.8, // Low friction
rand: 0.1 // High variation
},
colors: ['#E74C3C', '#C0392B']
});

Try it: Particles spring back quickly and overshoot their target, creating a bouncy, playful effect.

Best for: Playful brands, entertainment sites, gaming, creative portfolios, energetic content.

Friction RangeSpeedBehaviorOvershootBest For
0.95-0.99Very SlowHeavily damped, smooth glideNoneProfessional, minimal, calm
0.90-0.95SlowSoft landing, gentleMinimalCorporate, elegant, refined
0.85-0.90MediumBalanced, naturalSlightGeneral purpose, versatile
0.80-0.85FastSpringy, responsiveNoticeableEnergetic, modern, dynamic
0.70-0.80Very FastHighly bouncy, chaoticStrongPlayful, experimental, artistic
< 0.70ExtremeUnstable, wildExcessiveRarely recommended

For advanced control, use a function to calculate friction dynamically for each particle.

// Random friction per particle
friction: function() {
return Math.random() * 0.15 + 0.8; // 0.8-0.95
}
// Weighted towards high friction
friction: function() {
const values = [0.9, 0.9, 0.9, 0.85, 0.95];
return values[Math.floor(Math.random() * values.length)];
}
// Friction based on particle radius
friction: function() {
const radius = this.r || 2; // Access particle radius
return radius < 2 ? 0.95 : 0.85; // Small = smooth, large = bouncy
}
// Screen width-based
friction: function() {
return window.innerWidth < 768 ?
0.92 : // Higher friction on mobile (smoother)
0.88; // Lower friction on desktop (more dynamic)
}

Friction and explosion radius work together to create distinct interaction feels:

{
friction: { base: 0.93, rand: 0.03 }, // High friction
explosionRadius: { xs: 80, lg: 120 } // Medium radius
}
// Result: Soft, wave-like movement
{
friction: { base: 0.87, rand: 0.05 }, // Medium-low friction
explosionRadius: { xs: 50, lg: 70 } // Small radius
}
// Result: Quick, precise reactions
{
friction: { base: 0.82, rand: 0.08 }, // Low friction
explosionRadius: { xs: 150, lg: 250 } // Large radius
}
// Result: Wide-ranging, bouncy movement
{
friction: { base: 0.96, rand: 0.02 }, // Very high friction
explosionRadius: { xs: 40, lg: 60 } // Small radius
}
// Result: Subtle, calm, professional

Calm, controlled, minimal bounce.

{
friction: { base: 0.94, rand: 0.03 },
explosionRadius: { xs: 50, lg: 80 },
colors: ['#2C3E50'],
particleRadius: { xs: { base: 1, rand: 1 } }
}
// Smooth, professional interaction

Engaging, noticeable movement with character.

{
friction: { base: 0.87, rand: 0.06 },
explosionRadius: { xs: 100, lg: 180 },
colors: [
{ color: '#E74C3C', weight: 5 },
{ color: '#9B59B6', weight: 2 }
],
particleRadius: { xs: { base: 2, rand: 2 } }
}
// Dynamic, memorable interaction

Energetic, bouncy, playful.

{
friction: { base: 0.78, rand: 0.12 },
explosionRadius: { xs: 120, lg: 200 },
colors: ['#FF6B6B', '#4ECDC4', '#FFD93D'],
particleRadius: { xs: { base: 3, rand: 2 } }
}
// Playful, energetic, fun

Bold, attention-grabbing with smooth recovery.

{
friction: { base: 0.89, rand: 0.05 },
explosionRadius: { xs: 150, lg: 300 },
colors: ['#695aa6', '#8b7bb8'],
fontSize: 150
}
// Impactful yet controlled

Quick to position, minimal oscillation.

{
friction: { base: 0.96, rand: 0.02 },
explosionRadius: { xs: 30, lg: 50 },
particleRadius: { xs: { base: 1, rand: 0.5 } }
}
// Precise, minimal overshoot

The rand parameter adds per-particle variation, creating organic, natural movement:

friction: { base: 0.9, rand: 0 }
// All particles have exactly 0.9 friction
// Movement looks mechanical, synchronized
friction: { base: 0.9, rand: 0.03 }
// Particles: 0.9 to 0.93
// Subtle organic feel, particles slightly out of sync
friction: { base: 0.9, rand: 0.05 }
// Particles: 0.9 to 0.95
// Natural variation, recommended for most cases
friction: { base: 0.85, rand: 0.15 }
// Particles: 0.85 to 1.0
// Wide range of behaviors, creates depth and interest

Recommended: Use rand values between 0.03-0.08 for natural-looking variation without chaos.

Friction has zero performance impact beyond the base animation loop. It’s a simple multiplication operation:

velocity *= friction;

However, friction affects the perceived performance:

  • High friction → Slower movement → May feel laggy on slow devices
  • Low friction → Faster movement → May feel choppy at low frame rates
{
friction: { base: 0.88, rand: 0.05 }, // Medium friction
supportSlowBrowsers: true,
renderTimeThreshold: 15,
maxParticles: 3000
}
// On slow browsers, friction is automatically adjusted:
// friction /= 1.1 (becomes ~0.8 for faster movement)

The library automatically reduces friction on slow browsers to compensate for lower frame rates.

Smoothly change friction over time (requires manual implementation):

let targetFriction = 0.9;
let currentFriction = 0.9;
friction: function() {
// Smoothly interpolate to target
currentFriction += (targetFriction - currentFriction) * 0.1;
return currentFriction;
}
// Later, change friction dynamically:
// targetFriction = 0.75; // Becomes bouncier

Different friction in different areas (experimental):

friction: function() {
const x = this.dest.x; // Particle's rest position
const centerX = canvas.width / 2;
const distanceFromCenter = Math.abs(x - centerX);
const maxDistance = centerX;
// Higher friction towards edges
return 0.85 + (distanceFromCenter / maxDistance) * 0.1;
// Center particles: 0.85 (bouncy)
// Edge particles: 0.95 (smooth)
}

Pulsing friction for musical/rhythmic effects:

let time = 0;
friction: function() {
time += 0.05;
const pulse = Math.sin(time) * 0.05; // -0.05 to +0.05
return 0.9 + pulse; // Oscillates 0.85-0.95
}
// Creates rhythmic variation in particle behavior

Air resistance effect (higher friction at higher speeds):

friction: function() {
const speed = Math.sqrt(this.vx * this.vx + this.vy * this.vy);
if (speed > 5) {
return 0.85; // High friction for fast particles
} else if (speed > 2) {
return 0.90; // Medium friction
} else {
return 0.95; // Low friction for slow particles
}
// Fast particles slow down quickly, slow particles glide
}
// ❌ Too chaotic
friction: { base: 0.65, rand: 0.1 }
// Particles never settle, constantly oscillating
// ✅ Better
friction: { base: 0.8, rand: 0.08 }
// Bouncy but stable
// ❌ Too slow, feels laggy
friction: { base: 0.98, rand: 0.01 }
// Particles barely move
// ✅ Better
friction: { base: 0.94, rand: 0.03 }
// Smooth but responsive
// ❌ Awkward combination
{
friction: { base: 0.95, rand: 0.02 }, // Very smooth
explosionRadius: { lg: 300 } // Very large
}
// Large explosion but slow recovery = feels disconnected
// ✅ Better pairing
{
friction: { base: 0.88, rand: 0.05 }, // Medium
explosionRadius: { lg: 300 } // Very large
}
// Fast enough to follow large movements
// ❌ Mechanical, synchronized
friction: { base: 0.9, rand: 0 }
// ✅ Natural, organic
friction: { base: 0.9, rand: 0.05 }
  1. Start with defaults: { base: 0.9, rand: 0.05 } works for 90% of use cases
  2. Test by interacting: Don’t just look - move your mouse and feel the response
  3. Match your brand: Playful brand = lower friction; professional = higher friction
  4. Pair with explosion radius: Large radius needs lower friction for responsiveness
  5. Use variation: rand of 0.03-0.08 creates natural movement
  6. Consider device: Mobile may need higher friction (smoother) than desktop
  7. Avoid extremes: Stay within 0.75-0.96 for stable behavior
  8. Test slow browsers: Enable supportSlowBrowsers to auto-adjust

Particles move too slowly or feel sluggish?

  • Decrease friction value
  • Try: { base: 0.87, rand: 0.05 }

Particles bounce too much or never settle?

  • Increase friction value
  • Try: { base: 0.92, rand: 0.04 }

Movement looks robotic or synchronized?

  • Increase rand value
  • Try: { base: 0.9, rand: 0.06 }

Particles overshoot and oscillate wildly?

  • Increase base to 0.90+
  • Decrease rand to < 0.05

Different behavior on mobile vs desktop?

  • Set explicit friction values
  • Consider using a function to detect device type

Particles don’t react to cursor at all?

For those interested in the math:

velocity = constant (never slows down)
v(n+1) = v(n) × friction
After k frames:
v(k) = v(0) × friction^k

How many frames until velocity is reduced to 5% of original:

  • Friction 0.95: ~60 frames (~1 second at 60fps)
  • Friction 0.90: ~29 frames (~0.5 seconds)
  • Friction 0.85: ~19 frames (~0.3 seconds)
  • Friction 0.80: ~14 frames (~0.23 seconds)

Lower friction = faster settling time.

Overshoot occurs when friction is low enough that particles accelerate past their rest position before slowing down:

  • Friction > 0.92: Minimal overshoot (critically damped)
  • Friction 0.85-0.92: Slight overshoot (slightly underdamped)
  • Friction < 0.85: Noticeable overshoot (underdamped)
  • Reduced motion: The library respects prefers-reduced-motion - consider using higher friction (0.94+) for users who prefer reduced motion
  • Photosensitivity: Very low friction with rapid oscillation may trigger issues - avoid friction < 0.75
  • Predictability: Higher friction = more predictable movement = better for users with motor impairments