| name | gsap-performance |
| description | Use when optimizing GSAP animations, performance tuning, ticker configuration, stagger optimization, or improving animation frame rates. |
GSAP Performance Optimization
GSAP is highly performant by design, but proper optimization ensures smooth 60fps animations even with complex effects. Master ticker, lag smoothing, staggers, and utilities for best results.
Ticker and Lag Smoothing
Basic Ticker Control
console.log(gsap.ticker.fps())
console.log(gsap.ticker.time())
console.log(gsap.ticker.delta)
Lag Smoothing
gsap.ticker.lagSmoothing(1000, 16)
gsap.ticker.lagSmoothing(0)
gsap.ticker.lagSmoothing(500, 33)
Add Ticker Callback
const callback = (time, deltaTime, frame) => {
console.log('Tick:', time, deltaTime)
}
gsap.ticker.add(callback)
gsap.ticker.remove(callback)
gsap.ticker.add(callback, false, 1)
Global Ticker Use
function syncWithTicker() {
gsap.ticker.add(() => {
updateUI()
})
}
function stopSync() {
gsap.ticker.remove(updateUI)
}
Stagger Optimization
Basic Stagger
gsap.to('.box', {
x: 200,
duration: 1,
stagger: 0.1
})
Stagger with Ease
gsap.from('.item', {
opacity: 0,
y: 20,
stagger: {
each: 0.05,
ease: 'power2.out',
from: 'center'
},
duration: 0.8
})
Grid Stagger
gsap.to('.grid-item', {
scale: 1,
opacity: 1,
stagger: {
each: 0.05,
from: 'center',
grid: [10, 5],
ease: 'power2.inOut'
},
duration: 0.6
})
Avoid Tiny Staggers
gsap.to('.many-boxes', {
x: 100,
stagger: 0.006
})
gsap.to('.many-boxes', {
x: 100,
stagger: 0.05
})
Snapping
Basic Snap
gsap.to('.box', {
x: 500,
snap: 20
})
Snap to Array
gsap.to('.box', {
x: 500,
snap: [0, 100, 200, 300, 400, 500]
})
Snap with Radius
gsap.to('.box', {
x: 500,
snap: {
x: {
points: [0, 100, 200, 300, 400, 500],
radius: 10,
velocity: 0
}
}
})
Live Snap Performance
gsap.to('.box', {
x: 500,
snap: {
x: 20,
delay: 0.01
}
})
Modifiers
Basic Modifier
gsap.to('.box', {
x: 200,
modifiers: {
x: gsap.utils.unitize(x => Math.round(x / 10) * 10)
}
})
Clamping Modifier
gsap.to('.box', {
x: 500,
modifiers: {
x: gsap.utils.pipe(
gsap.utils.clamp(0, 400)
)
}
})
Complex Modifier
gsap.to('.box', {
x: 500,
y: 500,
modifiers: {
x: value => Math.round(value),
y: value => Math.max(0, value)
}
})
Keyframes
Condense Tweens
gsap.to('.box', { x: 100, duration: 1 })
gsap.to('.box', { y: 100, duration: 1 })
gsap.to('.box', { rotation: 90, duration: 1 })
gsap.to('.box', {
keyframes: [
{ x: 100, duration: 1 },
{ y: 100, duration: 1 },
{ rotation: 90, duration: 1 }
]
})
Keyframes with Eases
gsap.to('.box', {
keyframes: [
{ x: 100, duration: 1, ease: 'power2.out' },
{ y: 100, duration: 1, ease: 'back.out' },
{ rotation: 90, duration: 1, ease: 'elastic.out' }
]
})
Keyframe Duration Control
gsap.to('.box', {
duration: 3,
keyframes: [
{ x: 100, duration: 1 },
{ y: 100, duration: 1 },
{ rotation: 90, duration: 1 }
]
})
Ease Each
gsap.to('.box', {
keyframes: [
{ x: 100 },
{ y: 100 },
{ rotation: 90 }
],
easeEach: 'power1.out'
})
Performance Utilities
Throttle
gsap.to('.box', {
onUpdate: gsap.utils.throttle(() => {
updateLayout()
}, 100)
})
Debounce
ScrollTrigger.create({
trigger: '.section',
onUpdate: gsap.utils.debounce((self) => {
console.log('Scroll position stabilized:', self.progress)
}, 100)
})
Clamp
const clamped = gsap.utils.clamp(0, 100, 150)
Pipe
const process = gsap.utils.pipe(
gsap.utils.clamp(0, 100),
gsap.utils.mapRange(0, 100, 0, 500)
)
console.log(process(150))
Optimization Strategies
Combine Animations
gsap.to('.box', { x: 100, duration: 1 })
gsap.to('.box', { y: 100, duration: 1 })
gsap.to('.box', { opacity: 0.5, duration: 1 })
gsap.to('.box', {
x: 100,
y: 100,
opacity: 0.5,
duration: 1
})
Limit Active Tweens
gsap.killTweensOf('.box')
gsap.to('.box', { x: 200, duration: 1 })
Use Timelines
const tl = gsap.timeline()
tl.to('.box1', { x: 100, duration: 1 })
.to('.box2', { x: 100, duration: 1 })
.to('.box3', { x: 100, duration: 1 })
tl.kill()
Batch Operations
const elements = document.querySelectorAll('.element')
ScrollTrigger.batch(elements, {
onEnter: batch => gsap.from(batch, {
opacity: 0,
y: 20,
stagger: 0.1
})
})
Animation Performance Tips
Use Transform Over Properties
gsap.to('.box', {
left: 200,
top: 100,
width: 100,
height: 100
})
gsap.to('.box', {
x: 200,
y: 100,
scale: 1.5
})
Avoid Filters on Many Elements
gsap.to('.many-boxes', {
filter: 'blur(10px)',
duration: 1
})
gsap.to('.few-boxes', {
filter: 'blur(10px)',
duration: 1
})
Use Will Change
gsap.set('.box', { willChange: 'transform, opacity' })
gsap.to('.box', {
x: 200,
opacity: 0.5,
duration: 1,
onComplete: () => {
gsap.set('.box', { willChange: 'auto' })
}
})
Reduce Change Area
gsap.to('.small-indicator', {
scale: 2,
duration: 0.3
})
gsap.to('.large-container', {
scale: 1.1,
duration: 0.3
})
ScrollTrigger Performance
Use Toggle Actions
gsap.to('.box', {
x: 200,
scrollTrigger: {
trigger: '.box',
start: 'top 80%',
toggleActions: 'play none none reverse'
}
})
Throttle Scroll Events
ScrollTrigger.config({
ignoreMobileResize: true,
autoRefreshEvents: 'visibilitychange,DOMContentLoaded'
})
Batch ScrollTriggers
const elements = document.querySelectorAll('.element')
elements.forEach(el => {
ScrollTrigger.create({
trigger: el,
start: 'top 80%',
onEnter: () => gsap.to(el, { opacity: 1 })
})
})
ScrollTrigger.refresh()
Memory Management
Kill When Done
const tl = gsap.timeline()
tl.to('.box', { x: 200, duration: 1 })
tl.eventCallback('onComplete', () => {
tl.kill()
})
Clean Up References
function animateAndCleanup() {
const tl = gsap.timeline()
tl.to('.box', { x: 200, duration: 1 })
tl.play()
setTimeout(() => {
tl.kill()
}, 2000)
}
Revert SplitText
const split = new SplitText('#text', { type: 'chars' })
gsap.from(split.chars, {
opacity: 0,
y: 20,
onComplete: () => {
split.revert()
}
})
Performance Monitoring
Measure FPS
let frameCount = 0
let lastTime = performance.now()
gsap.ticker.add(() => {
frameCount++
const now = performance.now()
if (now - lastTime >= 1000) {
console.log('FPS:', frameCount)
frameCount = 0
lastTime = now
}
})
Profile Animation
const start = performance.now()
gsap.to('.box', {
x: 200,
duration: 1,
onComplete: () => {
const duration = performance.now() - start
console.log('Animation time:', duration)
}
})
Common Mistakes
1. Over-scrubbing
gsap.to('.many-boxes', {
x: 200,
scrollTrigger: { scrub: true }
})
gsap.to('.many-boxes', {
x: 200,
scrollTrigger: { toggleActions: 'play none none reverse' }
})
2. Animating Wrong Properties
gsap.to('.box', {
left: 200,
top: 100
})
gsap.to('.box', {
x: 200,
y: 100
})
3. Not Killing Tweens
function animateManyTimes() {
gsap.to('.box', { x: 200, duration: 1 })
}
function animateManyTimes() {
gsap.killTweensOf('.box')
gsap.to('.box', { x: 200, duration: 1 })
}
Best Practices
- Use transform properties - GPU-accelerated, no layout thrashing
- Stagger animations - Distributes load, smoother experience
- Combine properties - Fewer tweens = better performance
- Use timelines - Easier control and cleanup
- Kill when done - Free memory, prevent conflicts
- Test on low-end devices - Ensure smooth performance
- Profile bottlenecks - Identify expensive animations
- Use appropriate eases - Complex eases cost more
Quick Reference
| Feature | Method |
|---|
| Lag smoothing | gsap.ticker.lagSmoothing(1000, 16) |
| Add callback | gsap.ticker.add(callback) |
| Remove callback | gsap.ticker.remove(callback) |
| Stagger | stagger: 0.1 |
| Snap | snap: 20 or snap: [0, 100, 200] |
| Modifier | modifiers: { x: fn } |
| Keyframes | keyframes: [...] |
| Throttle | gsap.utils.throttle(fn, 100) |
| Debounce | gsap.utils.debounce(fn, 100) |
| Kill tweens | gsap.killTweensOf(target) |
| Will change | gsap.set(target, { willChange: 'transform' }) |