GSAP Animation Library Migration Plan
Executive Summary
Goal: Replace Framer Motion with GSAP for all visualization components in the DSA Panicle project, including the core library and all 22 linked list playgrounds.
Why GSAP:
- True timeline control with labels and markers
- Precise timing for educational animations
- Timeline scrubbing capability (users can drag through animations)
- Industry standard for professional animations
- Better for complex multi-element choreography
Scope:
- Core library: PlaygroundContainer, ControlPanel, StatsPanel
- LinkedList library: All components (Container, Node, Arrow, Pointer)
- All 22 linked list playgrounds
- 4 Flatten Tree playgrounds (tree + linked list hybrid)
- 2 Design playgrounds (interactive)
Current Architecture
File Structure
src/lib/viz/
├── components/
│ └── LinkedList/
│ ├── useLinkedListViz.js # Config hook (KEEP - no animations)
│ ├── LinkedListContainer.jsx # MIGRATE
│ ├── LinkedListNode.jsx # MIGRATE
│ ├── LinkedListArrow.jsx # MIGRATE
│ ├── LinkedListPointer.jsx # MIGRATE
│ └── index.js
├── core/
│ ├── PlaygroundContainer.jsx # MIGRATE
│ ├── ControlPanel.jsx # MIGRATE (add scrubbing)
│ └── StatsPanel.jsx # MIGRATE
├── hooks/
│ ├── useAlgorithmState.js # MIGRATE (add GSAP timeline)
│ └── useTheme.js # KEEP (no animations)
├── themes/
│ └── defaultTheme.js # KEEP
└── index.js
Current Animation Library: Framer Motion
motion.div,motion.button,motion.pathAnimatePresencefor enter/exit- Spring physics (
stiffness,damping) whileHover,whileTapfor interactions
New Architecture Design
GSAP Integration Strategy
1. Core Timeline Hook: useGSAPTimeline
// New hook to replace useAlgorithmState animation logic
const useGSAPTimeline = (steps, options) => {
const timelineRef = useRef(null);
const containerRef = useRef(null);
// Create master timeline with all steps
// Each step is a label in the timeline
// Supports: play, pause, seek, scrub, reverse
return {
timeline: timelineRef.current,
containerRef,
controls: {
play, pause, togglePlay,
seekToStep, seekToProgress,
nextStep, previousStep, reset
},
state: {
currentStep, progress, isPlaying, isComplete
}
};
};
2. Animation Components Pattern
// Each component uses useGSAP hook for cleanup
import { useGSAP } from '@gsap/react';
import gsap from 'gsap';
const LinkedListNode = ({ nodeRef, state, ... }) => {
useGSAP(() => {
// Animations with automatic cleanup
gsap.to(nodeRef.current, { ... });
}, { scope: containerRef, dependencies: [state] });
};
3. Timeline Scrubbing in ControlPanel
// New scrubber component
<TimelineScrubber
timeline={timeline}
progress={progress}
onScrub={(progress) => timeline.progress(progress)}
labels={stepLabels} // Show step markers on scrubber
/>
Detailed Task Breakdown
Phase 1: Core Infrastructure (Priority: CRITICAL)
Task 1.1: Create useGSAPTimeline Hook
File: src/lib/viz/hooks/useGSAPTimeline.js
Dependencies: gsap, @gsap/react
Estimated Complexity: High
Requirements:
- Replace
useAlgorithmStateanimation logic - Create master timeline from steps array
- Each step = labeled position in timeline
- Support methods:
play()/pause()/togglePlay()nextStep()/previousStep()goToStep(index)/reset()seekToProgress(0-1)- for scrubbinggetProgress()- current progress 0-1
- Auto-advance between steps based on speed
- Emit events:
onStepChange,onComplete - Handle step duration (default 800ms, configurable)
Interface:
const {
containerRef, // Ref to attach to container
timeline, // GSAP timeline instance
currentStep, // Current step object
currentStepIndex, // Current step index
totalSteps, // Total steps count
progress, // 0-1 progress
isPlaying, // Is playing
isComplete, // Reached end
controls: {
play, pause, togglePlay,
nextStep, previousStep,
goToStep, reset,
seekToProgress,
setSpeed
}
} = useGSAPTimeline(steps, {
initialSpeed: 800,
onStepChange: (index, step) => {},
onComplete: () => {}
});
Task 1.2: Create TimelineScrubber Component
File: src/lib/viz/core/TimelineScrubber.jsx
Dependencies: gsap, useTheme
Estimated Complexity: Medium
Requirements:
- Horizontal scrubber bar
- Draggable thumb for seeking
- Step markers/labels on the bar
- Current position indicator
- Click anywhere to seek
- Keyboard support (arrow keys)
- Touch support for mobile
- Visual feedback during drag
Props:
{
progress, // 0-1 current progress
onSeek, // (progress) => void
stepCount, // Total steps
stepLabels, // Optional labels for markers
showMarkers, // Show step markers (default: true)
disabled, // Disable interaction
theme // Theme object
}
Task 1.3: Migrate PlaygroundContainer
File: src/lib/viz/core/PlaygroundContainer.jsx
Current: Framer Motion motion.div for fade-in
New: GSAP useGSAP for entrance animations
Changes:
- Replace
motion.divwith regulardiv+ GSAP - Add
containerReffor GSAP scope - Integrate TimelineScrubber
- Update layout to accommodate scrubber
Task 1.4: Migrate ControlPanel
File: src/lib/viz/core/ControlPanel.jsx
Current: Framer Motion whileHover, whileTap
New: GSAP or CSS for button interactions
Changes:
- Replace motion buttons with GSAP hover/tap
- Add TimelineScrubber integration
- Update progress bar to use GSAP
- Keep existing button layout
Task 1.5: Migrate StatsPanel
File: src/lib/viz/core/StatsPanel.jsx
Current: Framer Motion staggered fade-in
New: GSAP stagger animation
Changes:
- Replace motion.div with GSAP timeline
- Use
gsap.from()with stagger for stats items - Keep completion badge animation
Phase 2: LinkedList Components (Priority: HIGH)
Task 2.1: Migrate LinkedListNode
File: src/lib/viz/components/LinkedList/LinkedListNode.jsx
Current Animations:
- Entry: scale 0→1, opacity 0→1 (spring)
- Exit: scale 1→0, opacity 1→0
- Hover: scale 1→1.08
- Removal: scale/opacity to 0
- Tooltip: fade in/out
New GSAP Implementation:
useGSAP(() => {
// Entry animation
gsap.from(nodeRef.current, {
scale: 0,
opacity: 0,
duration: 0.4,
ease: 'back.out(1.7)'
});
}, { scope: containerRef });
// State change animations
useGSAP(() => {
gsap.to(nodeRef.current, {
backgroundColor: getColorForState(state),
scale: isHighlighted ? 1.1 : 1,
duration: 0.3,
ease: 'power2.out'
});
}, [state, isHighlighted]);
Task 2.2: Migrate LinkedListArrow
File: src/lib/viz/components/LinkedList/LinkedListArrow.jsx
Current Animations:
- Normal: fade + slide in
- Broken: infinite pulse (opacity/scale)
- Reversed: spring entrance
New GSAP Implementation:
// Broken arrow pulse
useGSAP(() => {
if (isBroken) {
gsap.to(arrowRef.current, {
opacity: 0.3,
scale: 0.8,
duration: 0.5,
repeat: -1,
yoyo: true,
ease: 'sine.inOut'
});
}
}, [isBroken]);
Task 2.3: Migrate LinkedListPointer
File: src/lib/viz/components/LinkedList/LinkedListPointer.jsx
Current Animations:
- Entry: fade + slide (spring)
- Position change: animated movement
New GSAP Implementation:
useGSAP(() => {
gsap.from(pointerRef.current, {
y: position === 'top' ? -20 : 20,
opacity: 0,
duration: 0.4,
ease: 'back.out(1.7)'
});
}, { scope: containerRef });
Task 2.4: Migrate LinkedListContainer
File: src/lib/viz/components/LinkedList/LinkedListContainer.jsx
Current Animations:
- Cycle SVG path: pathLength 0→1
- AnimatePresence for node enter/exit
New GSAP Implementation:
// Cycle path drawing
useGSAP(() => {
if (hasCycle) {
gsap.fromTo(cyclePathRef.current,
{ strokeDashoffset: pathLength },
{ strokeDashoffset: 0, duration: 1, delay: 0.3, ease: 'power2.inOut' }
);
}
}, [hasCycle, cycleStartIndex, cycleEndIndex]);
Note: Need custom enter/exit handling since GSAP doesn't have AnimatePresence equivalent. Use:
- Track previous nodes vs current nodes
- Animate out removed nodes before removing from DOM
- Animate in new nodes after adding to DOM
Task 2.5: Update useLinkedListViz Hook
File: src/lib/viz/components/LinkedList/useLinkedListViz.js
Status: Mostly keep as-is (no animations in hook)
Minor Updates:
- Add ref generation for each node
- Add helpers for GSAP animation targets
Phase 3: Playground Migrations (Priority: MEDIUM)
Each playground needs to be updated to use the new useGSAPTimeline hook instead of useAlgorithmState.
Two-Pointers Category (9 files)
Task 3.1: Migrate cycle-1.js (Floyd's Cycle Detection Phase 1)
File: docs/20. linked-list/playground/two-pointers/cycle-1.js
Special Features:
- Slow/fast pointers
- Cycle arc visualization
- Meeting point detection
Changes:
- Replace
useAlgorithmStatewithuseGSAPTimeline - Update component to use new API
- Test cycle visualization animation
Task 3.2: Migrate cycle-2.js (Floyd's Cycle Detection Phase 2)
File: docs/20. linked-list/playground/two-pointers/cycle-2.js
Special Features:
- Two phases (detection → start finding)
- Phase transition animation
- Head + slow pointer movement
Task 3.3: Migrate middle-node.js
File: docs/20. linked-list/playground/two-pointers/middle-node.js
Special Features:
- 2:1 speed ratio visualization
- Slow/fast pointer sync
Task 3.4: Migrate remove-nth-end-node.js
File: docs/20. linked-list/playground/two-pointers/remove-nth-end-node.js
Special Features:
- Gap creation between pointers
- Sentinel node
- Node removal animation
Task 3.5: Migrate rotate-ll.js
File: docs/20. linked-list/playground/two-pointers/rotate-ll.js
Special Features:
- Circular connection formation
- Circle breaking animation
- Broken edges visualization
Task 3.6: Migrate remove-duplicates-2.js
File: docs/20. linked-list/playground/two-pointers/remove-duplicates-2.js
Special Features:
- Sentinel node
- Group removal (all duplicates together)
- removedIndices animation
Task 3.7: Migrate intersection.js
File: docs/20. linked-list/playground/two-pointers/intersection.js
Special Features:
- Multi-row layout (two separate lists)
- Pointer switching at end
- Intersection point detection
Task 3.8: Migrate plus-one.js
File: docs/20. linked-list/playground/two-pointers/plus-one.js
Special Features:
- Carry propagation visualization
- Node value transformation
- New head creation (all-9s case)
Task 3.9: Migrate insert-in-circular-ll.js
File: docs/20. linked-list/playground/two-pointers/insert-in-circular-ll.js
Special Features:
- Circular list visualization
- Max-min boundary detection
- Node insertion animation
Three-Pointers Category (3 files)
Task 3.10: Migrate reverse-ll.js
File: docs/20. linked-list/playground/three-pointers/reverse-ll.js
Special Features:
- Three pointers (front/mid/back)
- reversedEdges visualization
- Arrow direction change animation
Task 3.11: Migrate swap-pairs.js
File: docs/20. linked-list/playground/three-pointers/swap-pairs.js
Special Features:
- Sentinel node
- Pair swapping animation
- brokenEdges during restructuring
Task 3.12: Migrate reverse-both-parts.js
File: docs/20. linked-list/playground/three-pointers/reverse-both-parts.js
Special Features:
- Three phases visualization
- Phase-specific pointer colors
- Multiple reversal processes
One-Pass Category (4 files)
Task 3.13: Migrate convert-binary-number.js
File: docs/20. linked-list/playground/one-pass/convert-binary-number.js
Special Features:
- Bit-by-bit processing
- Formula display
- Decimal accumulation visualization
Task 3.14: Migrate delete-n-after-m.js
File: docs/20. linked-list/playground/one-pass/delete-n-after-m.js
Special Features:
- Pattern cycling (keep M, delete N)
- removedIndices animation
- Cycle counter
Task 3.15: Migrate remove-ll-elements.js
File: docs/20. linked-list/playground/one-pass/remove-ll-elements.js
Special Features:
- Two implementations (iterative + recursive)
- Recursion call stack visualization
- Target value matching
Task 3.16: Migrate sort-already-sorted-abs.js
File: docs/20. linked-list/playground/one-pass/sort-already-sorted-abs.js
Special Features:
- Classification scan
- Negatives reversal
- List concatenation
Flatten Tree Category (4 files) - COMPLEX
Task 3.17: Migrate FlattenTreeSentinel.js
File: docs/20. linked-list/playground/misc/FlattenTreeSentinel.js
Special Features:
- Dual visualization: tree + linked list
- Sentinel in flattened list
- Call stack display
- Custom color scheme (6+ colors)
Complexity: HIGH - Requires tree rendering support
Task 3.18: Migrate FlattenTreeRecursive.js
File: docs/20. linked-list/playground/misc/FlattenTreeRecursive.js
Special Features:
- Tail returns tracking
- Intermediate connections (dashed)
- Call stack building/unwinding
- Leaf vs non-leaf differentiation
Complexity: HIGH
Task 3.19: Migrate FlattenTreeMorris.js
File: docs/20. linked-list/playground/misc/FlattenTreeMorris.js
Special Features:
- Predecessor node tracking
- Connection state (original/new/nullified/final)
- Different line styles
- Structure transformation
Complexity: HIGH
Task 3.20: Migrate FlattenTreeIterative.js
File: docs/20. linked-list/playground/misc/FlattenTreeIterative.js
Special Features:
- Stack visualization (LIFO display)
- Tuple representation: (node, visited)
- Stack operations animation
- Top-of-stack indicator
Complexity: HIGH
Basics Category (2 files) - INTERACTIVE
Task 3.21: Migrate design-singly-ll.js
File: docs/20. linked-list/playground/basics/design-singly-ll.js
Special Features:
- Interactive (user operations)
- Operation log display
- Node highlighting on operation
- Gradient backgrounds
- Input validation
Complexity: MEDIUM - Different interaction pattern
Task 3.22: Migrate design-doubly-ll.js
File: docs/20. linked-list/playground/basics/design-doubly-ll.js
Special Features:
- Bidirectional arrows
- Two sentinels (head + tail)
- Prev/next pointer visualization
- Interactive operations
Complexity: MEDIUM
Phase 4: Testing & Polish (Priority: HIGH)
Task 4.1: Create Test Page
- Create a test MDX page that imports all 22 playgrounds
- Verify each playground works correctly
- Test dark/light mode switching
- Test timeline scrubbing
- Test all controls
Task 4.2: Performance Testing
- Test with rapid scrubbing
- Test with fast playback speeds
- Check for memory leaks
- Verify GSAP cleanup on unmount
Task 4.3: Accessibility
- Keyboard navigation for scrubber
- ARIA labels for controls
- Focus management
Task 4.4: Documentation
- Update CLAUDE.md with new library usage
- Add JSDoc comments to new hooks
- Create migration guide for future playgrounds
Implementation Order (Recommended)
Week 1: Core Infrastructure
├── Day 1-2: useGSAPTimeline hook
├── Day 3: TimelineScrubber component
├── Day 4: PlaygroundContainer migration
└── Day 5: ControlPanel + StatsPanel migration
Week 2: LinkedList Components
├── Day 1: LinkedListNode migration
├── Day 2: LinkedListArrow migration
├── Day 3: LinkedListPointer migration
└── Day 4-5: LinkedListContainer migration (complex)
Week 3: Simple Playgrounds (14 files)
├── Day 1-2: Two-Pointers (cycle-1, cycle-2, middle-node, remove-nth)
├── Day 3: Two-Pointers (rotate-ll, remove-duplicates-2, plus-one)
├── Day 4: Two-Pointers (intersection, insert-circular)
└── Day 5: Three-Pointers (all 3 files)
Week 4: Complex Playgrounds (8 files)
├── Day 1: One-Pass (all 4 files)
├── Day 2-3: Flatten Tree (Sentinel, Recursive)
├── Day 4: Flatten Tree (Morris, Iterative)
└── Day 5: Design playgrounds (Singly, Doubly)
Week 5: Testing & Polish
├── Day 1-2: Testing all playgrounds
├── Day 3: Performance optimization
├── Day 4: Accessibility
└── Day 5: Documentation
Key Technical Decisions
1. Enter/Exit Animations Without AnimatePresence
GSAP doesn't have AnimatePresence. Solution:
// Track nodes for enter/exit
const [displayNodes, setDisplayNodes] = useState([]);
useEffect(() => {
const currentIds = new Set(nodes.map(n => n.id));
const prevIds = new Set(displayNodes.map(n => n.id));
// Find removed nodes
const removed = displayNodes.filter(n => !currentIds.has(n.id));
// Animate out removed nodes
removed.forEach(node => {
gsap.to(`#node-${node.id}`, {
scale: 0, opacity: 0, duration: 0.3,
onComplete: () => {
setDisplayNodes(prev => prev.filter(n => n.id !== node.id));
}
});
});
// Add new nodes (they animate in via useGSAP)
const added = nodes.filter(n => !prevIds.has(n.id));
setDisplayNodes(prev => [...prev, ...added]);
}, [nodes]);
2. Timeline Structure
// Master timeline with labeled steps
const masterTimeline = gsap.timeline({ paused: true });
steps.forEach((step, index) => {
masterTimeline.addLabel(`step-${index}`);
masterTimeline.to({}, { duration: stepDuration });
});
// Seek to step
masterTimeline.seek(`step-${stepIndex}`);
// Scrub to progress
masterTimeline.progress(progressValue);
3. Speed Control
// Change playback speed
masterTimeline.timeScale(1000 / speed); // speed in ms
4. Step Detection During Scrub
// Get current step from progress
const getCurrentStep = (progress) => {
const totalDuration = masterTimeline.duration();
const currentTime = progress * totalDuration;
const stepIndex = Math.floor(currentTime / stepDuration);
return Math.min(stepIndex, steps.length - 1);
};
Props/API Reference
useGSAPTimeline
const timeline = useGSAPTimeline(steps, {
initialSpeed: 800, // ms per step
autoPlay: false, // Start playing immediately
loop: false, // Loop when complete
onStepChange: (index, step) => {},
onComplete: () => {},
onProgress: (progress) => {},
});
TimelineScrubber
<TimelineScrubber
progress={0.5}
onSeek={(progress) => {}}
totalSteps={10}
currentStep={5}
stepLabels={['Init', 'Step 1', ...]}
showLabels={true}
showMarkers={true}
disabled={false}
/>
Updated LinkedListContainer
<LinkedListContainer
// Existing props (unchanged)
nodes={[...]}
pointers={{...}}
type="singly"
// ... all existing props
// New GSAP-related props
containerRef={containerRef} // For GSAP scope
animationTimeline={timeline} // Optional: external timeline
/>
Files to Create
src/lib/viz/hooks/useGSAPTimeline.js- NEWsrc/lib/viz/core/TimelineScrubber.jsx- NEWsrc/lib/viz/utils/gsapHelpers.js- NEW (optional utilities)
Files to Modify
src/lib/viz/core/PlaygroundContainer.jsxsrc/lib/viz/core/ControlPanel.jsxsrc/lib/viz/core/StatsPanel.jsxsrc/lib/viz/components/LinkedList/LinkedListContainer.jsxsrc/lib/viz/components/LinkedList/LinkedListNode.jsxsrc/lib/viz/components/LinkedList/LinkedListArrow.jsxsrc/lib/viz/components/LinkedList/LinkedListPointer.jsxsrc/lib/viz/components/LinkedList/index.js(exports)src/lib/viz/index.js(exports)- All 22 playground files
Files to Keep (No Changes)
src/lib/viz/hooks/useTheme.jssrc/lib/viz/hooks/useLinkedListViz.js(minor updates only)src/lib/viz/themes/defaultTheme.js
Risk Assessment
| Risk | Impact | Mitigation |
|---|---|---|
| Enter/exit animations complexity | HIGH | Build robust node tracking system |
| Timeline scrubbing edge cases | MEDIUM | Extensive testing with edge cases |
| Performance with many nodes | MEDIUM | Use GSAP's optimized methods |
| Breaking existing playgrounds | HIGH | Migrate one at a time, test each |
| Dark/light mode issues | LOW | Theme system already solid |
Success Criteria
- All 22 playgrounds work with new library
- Timeline scrubbing works smoothly
- Play/pause/step controls work
- Dark/light mode works
- No memory leaks
- Performance is equal or better
- Bundle size acceptable (GSAP ~23KB is fine)
- All animations feel polished and educational
Context for Future Sessions
When continuing this migration in future chat sessions, provide this context:
I'm working on migrating the DSA Panicle visualization library from Framer Motion to GSAP.
Current status: [PHASE X, TASK X.X]
Completed:
- [List completed tasks]
In Progress:
- [Current task]
Key files:
- Migration plan: docs/playground/MIGRATION_PLAN.md
- Core library: src/lib/viz/
- Playgrounds: docs/20. linked-list/playground/
GSAP is installed (v3.13.0, @gsap/react v2.1.2).
Please continue from [TASK X.X].
Appendix: Playground Feature Matrix
| # | Playground | Pointers | Cycle | Broken | Reversed | Removed | Sentinel | Multi-Row | Interactive | Tree |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 | cycle-1 | slow,fast | YES | - | - | - | - | - | - | - |
| 2 | cycle-2 | slow,fast,head | YES | - | - | - | - | - | - | - |
| 3 | middle-node | slow,fast | - | - | - | - | - | - | - | - |
| 4 | remove-nth | left,right | - | YES | - | YES | YES | - | - | - |
| 5 | rotate-ll | curr,tail | YES | YES | - | - | - | - | - | - |
| 6 | remove-dup-2 | prev,curr | - | - | - | YES | YES | - | - | - |
| 7 | intersection | ptrA,ptrB | - | - | - | - | - | YES | - | - |
| 8 | plus-one | curr | - | - | - | - | - | - | - | - |
| 9 | insert-circular | curr | YES | - | - | - | - | - | - | - |
| 10 | reverse-ll | front,mid,back | - | - | YES | - | - | - | - | - |
| 11 | swap-pairs | prev,first,second | - | YES | - | - | YES | - | - | - |
| 12 | reverse-both | varies | - | - | YES | - | - | - | - | - |
| 13 | convert-binary | curr | - | - | - | - | - | - | - | - |
| 14 | delete-n-m | curr | - | - | - | YES | - | - | - | - |
| 15 | remove-elements | prev,curr | - | - | - | YES | YES | - | - | - |
| 16 | sort-abs | curr | - | - | - | - | - | - | - | - |
| 17 | flatten-sentinel | curr | - | - | - | - | YES | - | - | YES |
| 18 | flatten-recursive | - | - | - | - | - | - | - | - | YES |
| 19 | flatten-morris | curr,pred | - | - | - | - | - | - | - | YES |
| 20 | flatten-iterative | - | - | - | - | - | YES | - | - | YES |
| 21 | design-singly | - | - | - | - | - | YES | - | YES | - |
| 22 | design-doubly | - | - | - | - | - | YES | - | YES | - |
Last Updated: 2025-11-21 Status: Planning Complete, Ready for Implementation