$npx -y skills add calesthio/OpenMontage --skill gsap-performanceOfficial GSAP skill for performance — prefer transforms, avoid layout thrashing, will-change, batching. Use when optimizing GSAP animations, reducing jank, or when the user asks about animation performance, FPS, or smooth 60fps.
| 1 | # GSAP Performance |
| 2 | |
| 3 | ## When to Use This Skill |
| 4 | |
| 5 | Apply when optimizing GSAP animations for smooth 60fps, reducing layout/paint cost, or when the user asks about performance, jank, or best practices for fast animations. |
| 6 | |
| 7 | **Related skills:** Build animations with **gsap-core** (transforms, autoAlpha) and **gsap-timeline**; for ScrollTrigger performance see **gsap-scrolltrigger**. |
| 8 | |
| 9 | ## Prefer Transform and Opacity |
| 10 | |
| 11 | Animating **transform** (`x`, `y`, `scaleX`, `scaleY`, `rotation`, `rotationX`, `rotationY`, `skewX`, `skewY`) and **opacity** keeps work on the compositor and avoids layout and most paint. Avoid animating layout-heavy properties when a transform can achieve the same effect. |
| 12 | |
| 13 | - ✅ Prefer: **x**, **y**, **scale**, **rotation**, **opacity**. |
| 14 | - ❌ Avoid when possible: **width**, **height**, **top**, **left**, **margin**, **padding** (they trigger layout and can cause jank). |
| 15 | |
| 16 | GSAP’s **x** and **y** use transforms (translate) by default; use them instead of **left**/**top** for movement. |
| 17 | |
| 18 | ## will-change |
| 19 | |
| 20 | Use **will-change** in CSS on elements that will animate. It hints the browser to promote the layer. |
| 21 | |
| 22 | ```css |
| 23 | will-change: transform; |
| 24 | ``` |
| 25 | |
| 26 | ## Batch Reads and Writes |
| 27 | |
| 28 | GSAP batches updates internally. When mixing GSAP with direct DOM reads/writes or layout-dependent code, avoid interleaving reads and writes in a way that causes repeated layout thrashing. Prefer doing all reads first, then all writes (or let GSAP handle the writes in one go). |
| 29 | |
| 30 | ## Many Elements (Stagger, Lists) |
| 31 | |
| 32 | - Use **stagger** instead of many separate tweens with manual delays when the animation is the same; it’s more efficient. |
| 33 | - For long lists, consider **virtualization** or animating only visible items; avoid creating hundreds of simultaneous tweens if it causes jank. |
| 34 | - Reuse timelines where possible; avoid creating new timelines every frame. |
| 35 | |
| 36 | ## Frequently updated properties (e.g. mouse followers) |
| 37 | |
| 38 | Prefer **gsap.quickTo()** for properties that are updated often (e.g. mouse-follower x/y). It reuses a single tween instead of creating new tweens on each update. |
| 39 | |
| 40 | ```javascript |
| 41 | let xTo = gsap.quickTo("#id", "x", { duration: 0.4, ease: "power3" }), |
| 42 | yTo = gsap.quickTo("#id", "y", { duration: 0.4, ease: "power3" }); |
| 43 | |
| 44 | document.querySelector("#container").addEventListener("mousemove", (e) => { |
| 45 | xTo(e.pageX); |
| 46 | yTo(e.pageY); |
| 47 | }); |
| 48 | ``` |
| 49 | |
| 50 | ## ScrollTrigger and Performance |
| 51 | |
| 52 | - **pin: true** promotes the pinned element; pin only what’s needed. |
| 53 | - **scrub** with a small value (e.g. `scrub: 1`) can reduce work during scroll; test on low-end devices. |
| 54 | - Call **ScrollTrigger.refresh()** only when layout actually changes (e.g. after content load), not on every resize; debounce when possible. |
| 55 | |
| 56 | ## Reduce Simultaneous Work |
| 57 | |
| 58 | - Pause or kill off-screen or inactive animations when they’re not visible (e.g. when the user navigates away). |
| 59 | - Avoid animating huge numbers of properties on many elements at once; simplify or sequence if needed. |
| 60 | |
| 61 | ## Best practices |
| 62 | |
| 63 | - ✅ Animate **transform** and **opacity**; use **will-change** in CSS only on elements that animate. |
| 64 | - ✅ Use **stagger** instead of many separate tweens with manual delays when the animation is the same. |
| 65 | - ✅ Use **gsap.quickTo()** for frequently updated properties (e.g. mouse followers). |
| 66 | - ✅ Clean up or kill off-screen animations; call **ScrollTrigger.refresh()** when layout changes, debounced when possible. |
| 67 | |
| 68 | ## Do Not |
| 69 | |
| 70 | - ❌ Animate **width**/ **height**/ **top**/ **left** for movement when **x**/ **y**/ **scale** can achieve the same look. |
| 71 | - ❌ Set **will-change** or **force3D** on every element “just in case”; use for elements that are actually animating. |
| 72 | - ❌ Create hundreds of overlapping tweens or ScrollTriggers without testing on low-end devices. |
| 73 | - ❌ Ignore cleanup; stray tweens and ScrollTriggers keep running and can hurt performance and correctness. |