Skip to main content

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.path
  • AnimatePresence for enter/exit
  • Spring physics (stiffness, damping)
  • whileHover, whileTap for 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 useAlgorithmState animation 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 scrubbing
    • getProgress() - 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.div with regular div + GSAP
  • Add containerRef for 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 useAlgorithmState with useGSAPTimeline
  • 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

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

  1. src/lib/viz/hooks/useGSAPTimeline.js - NEW
  2. src/lib/viz/core/TimelineScrubber.jsx - NEW
  3. src/lib/viz/utils/gsapHelpers.js - NEW (optional utilities)

Files to Modify

  1. src/lib/viz/core/PlaygroundContainer.jsx
  2. src/lib/viz/core/ControlPanel.jsx
  3. src/lib/viz/core/StatsPanel.jsx
  4. src/lib/viz/components/LinkedList/LinkedListContainer.jsx
  5. src/lib/viz/components/LinkedList/LinkedListNode.jsx
  6. src/lib/viz/components/LinkedList/LinkedListArrow.jsx
  7. src/lib/viz/components/LinkedList/LinkedListPointer.jsx
  8. src/lib/viz/components/LinkedList/index.js (exports)
  9. src/lib/viz/index.js (exports)
  10. All 22 playground files

Files to Keep (No Changes)

  1. src/lib/viz/hooks/useTheme.js
  2. src/lib/viz/hooks/useLinkedListViz.js (minor updates only)
  3. src/lib/viz/themes/defaultTheme.js

Risk Assessment

RiskImpactMitigation
Enter/exit animations complexityHIGHBuild robust node tracking system
Timeline scrubbing edge casesMEDIUMExtensive testing with edge cases
Performance with many nodesMEDIUMUse GSAP's optimized methods
Breaking existing playgroundsHIGHMigrate one at a time, test each
Dark/light mode issuesLOWTheme system already solid

Success Criteria

  1. All 22 playgrounds work with new library
  2. Timeline scrubbing works smoothly
  3. Play/pause/step controls work
  4. Dark/light mode works
  5. No memory leaks
  6. Performance is equal or better
  7. Bundle size acceptable (GSAP ~23KB is fine)
  8. 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

#PlaygroundPointersCycleBrokenReversedRemovedSentinelMulti-RowInteractiveTree
1cycle-1slow,fastYES-------
2cycle-2slow,fast,headYES-------
3middle-nodeslow,fast--------
4remove-nthleft,right-YES-YESYES---
5rotate-llcurr,tailYESYES------
6remove-dup-2prev,curr---YESYES---
7intersectionptrA,ptrB-----YES--
8plus-onecurr--------
9insert-circularcurrYES-------
10reverse-llfront,mid,back--YES-----
11swap-pairsprev,first,second-YES--YES---
12reverse-bothvaries--YES-----
13convert-binarycurr--------
14delete-n-mcurr---YES----
15remove-elementsprev,curr---YESYES---
16sort-abscurr--------
17flatten-sentinelcurr----YES--YES
18flatten-recursive--------YES
19flatten-morriscurr,pred-------YES
20flatten-iterative-----YES--YES
21design-singly-----YES-YES-
22design-doubly-----YES-YES-

Last Updated: 2025-11-21 Status: Planning Complete, Ready for Implementation