- Installs
- 254
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://smithery.ai --skill gsap- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads gsap automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
GSAP Animation Skill
File Organization: This skill uses split structure. See
references/for advanced patterns.
1. Overview
This skill provides GSAP (GreenSock Animation Platform) expertise for creating smooth, professional animations in the JARVIS AI Assistant HUD.
Risk Level: LOW - Animation library with minimal security surface
Primary Use Cases:
- HUD panel entrance/exit animations
- Status indicator transitions
- Data visualization animations
- Scroll-triggered effects
- Complex timeline sequences
2. Core Responsibilities
2.1 Fundamental Principles
- TDD First: Write animation tests before implementation
- Performance Aware: Use transforms/opacity for GPU acceleration, avoid layout thrashing
- Cleanup Required: Always kill animations on component unmount
- Timeline Organization: Use timelines for complex sequences
- Easing Selection: Choose appropriate easing for HUD feel
- Accessibility: Respect reduced motion preferences
- Memory Management: Avoid memory leaks with proper cleanup
2.5 Implementation Workflow (TDD)
Step 1: Write Failing Test First
// tests/animations/panel-animation.test.ts
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { mount } from '@vue/test-utils'
import { gsap } from 'gsap'
import HUDPanel from '~/components/HUDPanel.vue'
describe('HUDPanel Animation', () => {
beforeEach(() => {
// Mock reduced motion
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: vi.fn().mockImplementation(query => ({
matches: false,
media: query
}))
})
})
afterEach(() => {
// Verify cleanup
gsap.globalTimeline.clear()
})
it('animates panel entrance with correct properties', async () => {
const wrapper = mount(HUDPanel)
// Wait for animation to complete
await new Promise(resolve => setTimeout(resolve, 600))
const panel = wrapper.find('.hud-panel')
expect(panel.exists()).toBe(true)
})
it('cleans up animations on unmount', async () => {
const wrapper = mount(HUDPanel)
const childCount = gsap.globalTimeline.getChildren().length
await wrapper.unmount()
// All animations should be killed
expect(gsap.globalTimeline.getChildren().length).toBeLessThan(childCount)
})
it('respects reduced motion preference', async () => {
// Mock reduced motion enabled
window.matchMedia = vi.fn().mockImplementation(() => ({
matches: true
}))
const wrapper = mount(HUDPanel)
const panel = wrapper.find('.hud-panel').element
// Should set final state immediately without animation
expect(gsap.getProperty(panel, 'opacity')).toBe(1)
})
})
Step 2: Implement Minimum to Pass
// components/HUDPanel.vue - implement animation logic
const animation = ref<gsap.core.Tween | null>(null)
onMounted(() => {
if (!panelRef.value) return
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
gsap.set(panelRef.value, { opacity: 1 })
return
}
animation.value = gsap.from(panelRef.value, {
opacity: 0,
y: 20,
duration: 0.5
})
})
onUnmounted(() => {
animation.value?.kill()
})
Step 3: Refactor Following Patterns
// Extract to composable for reusability
export function usePanelAnimation(elementRef: Ref<HTMLElement | null>) {
const animation = ref<gsap.core.Tween | null>(null)
const animate = () => {
if (!elementRef.value) return
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
gsap.set(elementRef.value, { opacity: 1 })
return
}
animation.value = gsap.from(elementRef.value, {
opacity: 0,
y: 20,
duration: 0.5,
ease: 'power2.out'
})
}
onMounted(animate)
onUnmounted(() => animation.value?.kill())
return { animation }
}
Step 4: Run Full Verification
# Run animation tests
npm test -- --grep "Animation"
# Check for memory leaks
npm run test:memory
# Verify 60fps performance
npm run test:performance
3. Technology Stack & Versions
3.1 Recommended Versions
| Package | Version | Notes |
|---|---|---|
| gsap | ^3.12.0 | Core library |
| @gsap/vue | ^3.12.0 | Vue integration |
| ScrollTrigger | included | Scroll effects |
3.2 Vue Integration
// plugins/gsap.ts
import { gsap } from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
export default defineNuxtPlugin(() => {
gsap.registerPlugin(ScrollTrigger)
return {
provide: {
gsap,
ScrollTrigger
}
}
})
4. Implementation Patterns
4.1 Panel Entrance Animation
<script setup lang="ts">
import { gsap } from 'gsap'
import { onMounted, onUnmounted, ref } from 'vue'
const panelRef = ref<HTMLElement | null>(null)
let animation: gsap.core.Tween | null = null
onMounted(() => {
if (!panelRef.value) return
// ✅ Check reduced motion preference
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
gsap.set(panelRef.value, { opacity: 1 })
return
}
animation = gsap.from(panelRef.value, {
opacity: 0,
y: 20,
scale: 0.95,
duration: 0.5,
ease: 'power2.out'
})
})
// ✅ Cleanup on unmount
onUnmounted(() => {
animation?.kill()
})
</script>
<template>
<div ref="panelRef" class="hud-panel">
<slot />
</div>
</template>
4.2 Status Indicator Animation
// composables/useStatusAnimation.ts
import { gsap } from 'gsap'
export function useStatusAnimation(element: Ref<HTMLElement | null>) {
const timeline = ref<gsap.core.Timeline | null>(null)
const animateStatus = (status: string) => {
if (!element.value) return
timeline.value?.kill()
timeline.value = gsap.timeline()
switch (status) {
case 'active':
timeline.value
.to(element.value, {
scale: 1.2,
duration: 0.2,
ease: 'power2.out'
})
.to(element.value, {
scale: 1,
duration: 0.3,
ease: 'elastic.out(1, 0.3)'
})
break
case 'warning':
timeline.value.to(element.value, {
backgroundColor: '#f59e0b',
boxShadow: '0 0 10px #f59e0b',
duration: 0.3,
repeat: 2,
yoyo: true
})
break
case 'error':
timeline.value.to(element.value, {
x: -5,
duration: 0.05,
repeat: 5,
yoyo: true
})
break
}
}
onUnmounted(() => {
timeline.value?.kill()
})
return { animateStatus }
}
4.3 Data Visualization Animation
<script setup lang="ts">
import { gsap } from 'gsap'
const props = defineProps<{
data: number[]
}>()
const barsRef = ref<HTMLElement[]>([])
let animations: gsap.core.Tween[] = []
watch(() => props.data, (newData) => {
// Kill previous animations
animations.forEach(a => a.kill())
animations = []
// Animate each bar
newData.forEach((value, index) => {
const bar = barsRef.value[index]
if (!bar) return
const tween = gsap.to(bar, {
height: `${value}%`,
duration: 0.5,
delay: index * 0.05,
ease: 'power2.out'
})
animations.push(tween)
})
}, { immediate: true })
onUnmounted(() => {
animations.forEach(a => a.kill())
})
</script>
<template>
<div class="flex items-end h-40 gap-1">
<div
v-for="(_, index) in data"
:key="index"
ref="barsRef"
class="w-4 bg-jarvis-primary"
/>
</div>
</template>
4.4 Timeline Sequence
// Create complex HUD startup sequence
export function createStartupSequence(elements: {
logo: HTMLElement
panels: HTMLElement[]
status: HTMLElement
}): gsap.core.Timeline {
const tl = gsap.timeline({
defaults: { ease: 'power2.out' }
})
// Logo reveal
tl.from(elements.logo, {
opacity: 0,
scale: 0,
duration: 0.8,
ease: 'back.out(1.7)'
})
// Panels stagger in
tl.from(elements.panels, {
opacity: 0,
x: -30,
stagger: 0.1,
duration: 0.5
}, '-=0.3')
// Status indicator
tl.from(elements.status, {
opacity: 0,
y: 10,
duration: 0.3
}, '-=0.2')
return tl
}
4.5 Scroll-Triggered Animation
<script setup lang="ts">
import { gsap } from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
const sectionRef = ref<HTMLElement | null>(null)
onMounted(() => {
if (!sectionRef.value) return
gsap.from(sectionRef.value.querySelectorAll('.animate-item'), {
scrollTrigger: {
trigger: sectionRef.value,
start: 'top 80%',
end: 'bottom 20%',
toggleActions: 'play none none reverse'
},
opacity: 0,
y: 30,
stagger: 0.1,
duration: 0.5
})
})
onUnmounted(() => {
ScrollTrigger.getAll().forEach(trigger => trigger.kill())
})
</script>
5. Quality Standards
5.1 Performance
// ✅ GOOD - Use transforms for GPU acceleration
gsap.to(element, {
x: 100,
y: 50,
rotation: 45,
scale: 1.2
})
// ❌ BAD - Triggers layout recalculation
gsap.to(element, {
left: 100,
top: 50,
width: '120%'
})
5.2 Accessibility
// ✅ Respect reduced motion
const prefersReducedMotion = window.matchMedia(
'(prefers-reduced-motion: reduce)'
).matches
if (prefersReducedMotion) {
gsap.set(element, { opacity: 1 })
} else {
gsap.from(element, { opacity: 0, duration: 0.5 })
}
6. Performance Patterns
6.1 will-change Property Usage
// Good: Apply will-change before animation
const animatePanel = (element: HTMLElement) => {
element.style.willChange = 'transform, opacity'
gsap.to(element, {
x: 100,
opacity: 0.8,
duration: 0.5,
onComplete: () => {
element.style.willChange = 'auto'
}
})
}
// Bad: Never removing will-change
const animatePanelBad = (element: HTMLElement) => {
element.style.willChange = 'transform, opacity' // Memory leak!
gsap.to(element, { x: 100, opacity: 0.8 })
}
6.2 Transform vs Layout Properties
// Good: Use transforms (GPU accelerated)
gsap.to(element, {
x: 100, // translateX
y: 50, // translateY
scale: 1.2, // scale
rotation: 45, // rotate
opacity: 0.5 // opacity
})
// Bad: Layout-triggering properties (CPU, causes reflow)
gsap.to(element, {
left: 100, // Triggers layout
top: 50, // Triggers layout
width: '120%', // Triggers layout
height: 200, // Triggers layout
margin: 10 // Triggers layout
})
6.3 Timeline Reuse
// Good: Reuse timeline instance
const timeline = gsap.timeline({ paused: true })
timeline
.to(element, { opacity: 1, duration: 0.3 })
.to(element, { y: -20, duration: 0.5 })
// Play/reverse as needed
const show = () => timeline.play()
const hide = () => timeline.reverse()
// Bad: Creating new timeline each time
const showBad = () => {
gsap.timeline()
.to(element, { opacity: 1, duration: 0.3 })
.to(element, { y: -20, duration: 0.5 })
}
6.4 ScrollTrigger Batching
// Good: Batch ScrollTrigger animations
ScrollTrigger.batch('.animate-item', {
onEnter: (elements) => {
gsap.to(elements, {
opacity: 1,
y: 0,
stagger: 0.1,
overwrite: true
})
},
onLeave: (elements) => {
gsap.to(elements, {
opacity: 0,
y: -20,
overwrite: true
})
}
})
// Bad: Individual ScrollTrigger per element
document.querySelectorAll('.animate-item').forEach(item => {
gsap.to(item, {
scrollTrigger: {
trigger: item,
start: 'top 80%'
},
opacity: 1,
y: 0
})
})
6.5 Lazy Initialization
// Good: Initialize animations only when needed
let panelAnimation: gsap.core.Timeline | null = null
const getPanelAnimation = () => {
if (!panelAnimation) {
panelAnimation = gsap.timeline({ paused: true })
.from('.panel', { opacity: 0, y: 20 })
.from('.panel-content', { opacity: 0, stagger: 0.1 })
}
return panelAnimation
}
const showPanel = () => getPanelAnimation().play()
const hidePanel = () => getPanelAnimation().reverse()
// Bad: Initialize all animations on mount
onMounted(() => {
// Creates timeline even if never used
const animation1 = gsap.timeline().to('.panel1', { x: 100 })
const animation2 = gsap.timeline().to('.panel2', { y: 100 })
const animation3 = gsap.timeline().to('.panel3', { scale: 1.2 })
})
7. Testing & Quality
7.1 Animation Testing
describe('Panel Animation', () => {
it('cleans up on unmount', async () => {
const wrapper = mount(HUDPanel)
await wrapper.unmount()
// No active GSAP animations should remain
expect(gsap.globalTimeline.getChildren().length).toBe(0)
})
})
8. Common Mistakes & Anti-Patterns
8.1 Critical Anti-Patterns
Never: Skip Cleanup
// ❌ MEMORY LEAK
onMounted(() => {
gsap.to(element, { x: 100, duration: 1 })
})
// ✅ PROPER CLEANUP
let tween: gsap.core.Tween
onMounted(() => {
tween = gsap.to(element, { x: 100, duration: 1 })
})
onUnmounted(() => {
tween?.kill()
})
Never: Animate Layout Properties
// ❌ BAD - Causes layout thrashing
gsap.to(element, { width: 200, height: 100 })
// ✅ GOOD - Use transforms
gsap.to(element, { scaleX: 2, scaleY: 1 })
13. Pre-Implementation Checklist
Phase 1: Before Writing Code
- Write failing tests for animation behavior
- Define animation timing and easing requirements
- Identify elements that need will-change hints
- Plan cleanup strategy for all animations
- Check if reduced motion support is needed
Phase 2: During Implementation
- Use transforms/opacity only (no layout properties)
- Store animation references for cleanup
- Apply will-change before, remove after animation
- Use timelines for sequences
- Batch ScrollTrigger animations
- Implement lazy initialization for complex animations
Phase 3: Before Committing
- All tests pass (npm test -- --grep "Animation")
- All animations cleaned up on unmount
- Reduced motion preference respected
- No memory leaks (check with DevTools)
- 60fps maintained (test with performance monitor)
- ScrollTrigger instances properly killed
14. Summary
GSAP provides professional animations for JARVIS HUD:
- Cleanup: Always kill animations on unmount
- Performance: Use transforms and opacity only
- Accessibility: Respect reduced motion preference
- Organization: Use timelines for sequences
Remember: Every animation must be cleaned up to prevent memory leaks.
References:
references/advanced-patterns.md- Complex animation patterns
FAQ
What does the gsap skill do?
GSAP animations for JARVIS HUD transitions and effects
How do I install gsap?
Run npx skills add https://smithery.ai --skill gsap in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/gsap/ (personal) or .claude/skills/gsap/ (project).
Which AI agents can use gsap?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is gsap safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains gsap?
It is published by smithery.ai in smithery.ai. It has 254 recorded installs.
More from smithery.ai
smithery-ai-cli
smithery.ai
Find, connect, and use MCP tools and skills via the Smithery CLI. Use when the user searches for new tools or skills, wants to discover integrations, connect to an MCP, install a skill, or wants to interact with an external service (email, Slack, Discord, GitHub, Jira, Notion, databases, cloud APIs, monitoring, etc.).
frontend-design
smithery.ai
Guidance for distinctive, intentional visual design when building new UI or reshaping an existing one. Helps with aesthetic direction, typography, and making choices that don't read as templated defaults.
skill-creator
smithery.ai
Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that extends Gemini CLI's capabilities with specialized knowledge, workflows, or tool integrations.
ui-ux-pro-max
smithery.ai
UI/UX design intelligence for web, mobile, and desktop. This skill should be used when designing, building, reviewing, or fixing interfaces, including pages, components, design systems, accessibility, interaction, responsive layout, typography, color, charts, and stack-specific UI implementation. Searchable local data: 79 searchable styles (50 active), 192 product palettes and reasoning profiles, 74 font pairings, 119 UX guidelines, 105 icons, 17 GSAP presets, 25 chart types, and 22 stacks.
smithery.ai
Use this skill whenever the user wants to do anything with PDF files. This includes reading or extracting text/tables from PDFs, combining or merging multiple PDFs into one, splitting PDFs apart, rotating pages, adding watermarks, creating new PDFs, filling PDF forms, encrypting/decrypting PDFs, extracting images, and OCR on scanned PDFs to make them searchable. If the user mentions a .pdf file or asks to produce one, use this skill.
xlsx
smithery.ai
Use this skill any time a spreadsheet file is the primary input or output. This means any task where the user wants to: open, read, edit, or fix an existing .xlsx, .xlsm, .xltx, .csv, or .tsv file (e.g., adding columns, computing formulas, formatting, charting, cleaning messy data); create a new spreadsheet from scratch or from other data sources; or convert between tabular file formats. Trigger especially when the user references a spreadsheet file by name or path — even casually (like "the xlsx in my downloads") — and wants something done to it or produced from it. Also trigger for cleaning or restructuring messy tabular data files (malformed rows, misplaced headers, junk data) into proper spreadsheets. The deliverable must be a spreadsheet file. Do NOT trigger when the primary deliverable is a Word document, HTML report, standalone Python script, database pipeline, or Google Sheets API integration, even if tabular data is involved.
Popular other skills
hyperframes-cli
heygen-com/hyperframes
The HyperFrames CLI by job - scaffold, check, look at frames, edit the timeline, preview, render locally or in the cloud, publish, project history - plus reading its JSON and recovering from build or render failures.
triage
mattpocock/skills
Move issues and external PRs through a state machine of triage roles, categorise, verify, grill if needed, and write agent-ready briefs.
ask-matt
mattpocock/skills
Ask which skill or flow fits your situation. A router over the skills in this repo.
to-questionnaire
mattpocock/skills
Turn a decision you can't fully answer into a questionnaire for someone else to fill in.
loop-me
mattpocock/skills
Grill me about specs for the workflows I want to build, within this workspace.