GSAP Carousel Documentation

A production-ready, seamless horizontal carousel component built on GSAP's seamlessLoop helper function

Welcome

GSAP Carousel is an enhanced, production-ready implementation of GSAP's seamlessLoop helper function. It provides a high-performance, feature-rich carousel component with smooth infinite scrolling, responsive design, comprehensive accessibility features, and extensive customization options. Built on top of the powerful GSAP (GreenSock Animation Platform), it leverages hardware acceleration and advanced animation techniques to deliver exceptional performance across all modern browsers.

Based on GSAP's seamlessLoop: This carousel extends the official GSAP seamlessLoop helper function with additional features like responsive breakpoints, accessibility enhancements, advanced navigation controls, and comprehensive error handling for production use.

Key Features

  • True Seamless Loop - Uses GSAP's advanced xPercent positioning for pixel-perfect infinite scrolling without visible resets or jumps
  • Responsive Grid System - Configurable breakpoints with CSS custom properties for flexible items-per-row layouts
  • Advanced Touch & Mouse Dragging - Momentum-based scrolling with InertiaPlugin integration and customizable snap points
  • Smart Autoplay System - Pause on visibility changes, user interaction, and focus events with configurable delays
  • Comprehensive Navigation - Previous/next buttons, pagination dots, and full keyboard navigation (arrow keys, Home, End)
  • Center Mode Support - Active slide centering with precise positioning calculations
  • WCAG 2.1 Compliance - Full ARIA implementation, screen reader support, and keyboard accessibility
  • Memory Management - Automatic cleanup of event listeners, observers, and animations with comprehensive destroy methods
  • Developer Experience - Debug mode, comprehensive error handling, and TypeScript-friendly API design
Production Tested: This carousel includes battle-tested error recovery, comprehensive cleanup methods, performance monitoring, and debugging tools specifically designed for production environments with millions of users.

Installation

Quick Start

Download the carousel file and include it in your project. The carousel automatically detects available GSAP plugins and adjusts functionality accordingly.

HTML - Basic Setup
<!-- Load GSAP first -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.13.0/dist/gsap.min.js"></script>
<!-- Load carousel -->
<script src="path/to/gsap-carousel.min.js"></script>

Required Dependencies

GSAP 3.12+ is required. Additional plugins enhance functionality but are not mandatory:

Complete Dependencies
<!-- Required: GSAP Core (3.12.0 or newer) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.13.0/dist/gsap.min.js"></script>

<!-- Optional: For drag functionality -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.13.0/dist/Draggable.min.js"></script>

<!-- Optional: For momentum scrolling (Club GreenSock) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.13.0/dist/InertiaPlugin.min.js"></script>
All GSAP Plugins Now Free! GSAP has recently made all plugins, including InertiaPlugin, completely free for both personal and commercial use. This means you can enjoy the full momentum-based dragging experience with smooth inertia scrolling without any licensing restrictions. Simply include the InertiaPlugin to unlock advanced touch interactions and momentum scrolling.

Basic Usage

Minimal HTML Structure

The carousel requires a container element with child slides. No specific CSS classes are needed - the carousel handles all styling automatically:

HTML Structure
<div id="my-carousel">
  <div class="slide">
    <h3>Slide 1</h3>
    <p>Your content here</p>
  </div>
  <div class="slide">
    <h3>Slide 2</h3>
    <p>Your content here</p>
  </div>
  <div class="slide">
    <h3>Slide 3</h3>
    <p>Your content here</p>
  </div>
</div>

Basic Initialization

Simple Setup
// Minimal initialization
const carousel = horizontalLoop("#my-carousel");

// With basic options
const carousel = horizontalLoop("#my-carousel", {
  speed: 1,      // Animation speed multiplier
  gap: "20px",   // Space between slides
  paused: false  // Start playing immediately
});

Advanced Configuration

Full-Featured Setup
const carousel = horizontalLoop("#my-carousel", {
  // Responsive breakpoints
  responsive: {
    0: { items: 1 },      // Mobile: 1 slide visible
    768: { items: 2 },    // Tablet: 2 slides visible
    1024: { items: 3 },   // Desktop: 3 slides visible
  },
  
  // Animation settings
  speed: 1.5,             // Slightly faster than default
  gap: "24px",            // Consistent spacing
  
  // Interaction options
  draggable: true,        // Enable touch/mouse dragging
  snap: 1,                // Snap to nearest slide
  center: false,          // Don't center active slide
  
  // Autoplay configuration
  autoplayDelay: 3,       // 3 seconds between advances
  paused: false,          // Start immediately
  reversed: false,        // Move left to right
  
  // Navigation elements
  prevNav: "#prev-btn",   // Previous button selector
  nextNav: "#next-btn",   // Next button selector
  dots: "#pagination",    // Dots container selector
  
  // Accessibility
  ariaLabel: "Product showcase",
  accessibilityEnabled: true,
  
  // Callbacks
  onChange: ({ currentIndex, currentItem, totalItems }) => {
    console.log(`Showing slide ${currentIndex + 1} of ${totalItems}`);
    
    // Update custom UI elements
    updateSlideCounter(currentIndex + 1, totalItems);
    updateSlideDescription(currentItem);
  },
  
  onInitialized: (payload) => {
    console.log("Carousel ready!", payload);
    // Hide loading indicator, start additional animations, etc.
  },
  
  // Development
  debug: false  // Enable for development
});

Error Handling

Safe Initialization
// Always check for successful initialization
const carousel = horizontalLoop("#my-carousel", config);

if (carousel) {
  console.log("Carousel initialized successfully");
  
  // Store reference for cleanup
  window.myCarousel = carousel;
} else {
  console.error("Failed to initialize carousel");
  // Handle fallback UI or show error message
}

Configuration Options

Complete reference of all configuration options with detailed explanations and usage examples:

Option Type Default Description
responsive Object|null null Breakpoint mapping for responsive layouts. Keys are pixel widths, values are {items: number} objects
speed number 1 Animation speed multiplier. 1 ≈ 100px/second. Higher values = faster movement
gap string|number "0px" Space between slides. CSS length values ("20px", "1rem") or numbers (converted to px)
draggable boolean false Enable touch/mouse dragging. Requires GSAP Draggable plugin
repeat number 0 Number of complete loop cycles. -1 for infinite, 0 for continuous
paused boolean true Start in paused state. Set to false for immediate autoplay
autoplayDelay number 0 Seconds between automatic slide advances. 0 disables autoplay
reversed boolean false Reverse animation direction (right to left)
prevNav Element|string|Array null Previous button element, selector, or [element, options] tuple
nextNav Element|string|Array null Next button element, selector, or [element, options] tuple
dots Element|string|Array null Pagination container element, selector, or [container, options] tuple
snap number|false 1 Snap increment in slides. false disables snapping. Useful for partial slide views
center boolean false Center active slide in viewport. Useful for highlighting current item
updateOnlyOnSettle boolean false Fire onChange only after drag/scroll settles. Improves performance with expensive callbacks
onChange Function|null null Callback when active slide changes. Receives payload object with slide data
onInitialized Function|null null Callback after carousel setup completes. Useful for hiding loading states
ariaLabel string "Carousel" ARIA label for the carousel container. Important for screen readers
debug boolean false Enable detailed console logging. Includes timing, positioning, and state information
accessibilityEnabled boolean true Enable ARIA roles, keyboard navigation, and screen reader support

Advanced Option Examples

Navigation with Options
const carousel = horizontalLoop("#carousel", {
  // Navigation elements with custom animation options
  prevNav: ["#prev-btn", { duration: 0.8, ease: "power2.inOut" }],
  nextNav: ["#next-btn", { duration: 0.8, ease: "power2.inOut" }],
  
  // Dots with custom animation
  dots: ["#pagination", { duration: 0.5, ease: "power1.out" }],
});

Responsive Configuration

The responsive system uses CSS custom properties and breakpoint-based configurations to create fluid, adaptive layouts that work across all device sizes.

Basic Responsive Setup

Mobile-First Approach
const carousel = horizontalLoop("#carousel", {
  responsive: {
    0: { items: 1 },      // Mobile phones
    640: { items: 2 },    // Large phones / small tablets
    768: { items: 2 },    // Tablets
    1024: { items: 3 },   // Small desktop
    1280: { items: 4 },   // Large desktop
    1536: { items: 5 },   // Extra large screens
  },
  gap: "16px",
});

Advanced Responsive Configuration

Complex Breakpoint System
const carousel = horizontalLoop("#carousel", {
  responsive: {
    // Mobile portrait
    0: { items: 1 },
    
    // Mobile landscape / small tablets
    480: { items: 1.5 },  // Show partial next slide
    
    // Tablets
    768: { items: 2.2 },  // Show 2 full + partial 3rd slide
    
    // Small desktop
    1024: { items: 3 },
    
    // Large desktop - different approach for wide screens
    1440: { items: 4 },
    
    // Ultra-wide screens
    1920: { items: 5 },
  },
  
  // Responsive gap sizing
  gap: window.innerWidth < 768 ? "12px" : "24px",
  
  // Different snap behavior on mobile
  snap: window.innerWidth < 768 ? 1 : 0.5,
});

CSS Integration

The carousel sets CSS custom properties that you can use for additional responsive styling:

CSS Custom Properties
/* The carousel automatically sets these CSS custom properties: */
.carousel-container {
  --items-per-row: 3;  /* Current items visible */
  --gap: 20px;         /* Current gap size */
}

/* Use them in your CSS for consistent styling */
.carousel-slide {
  /* Slide width automatically calculated */
  flex: 0 0 calc(100% / var(--items-per-row) - (var(--gap) * (var(--items-per-row) - 1) / var(--items-per-row)));
  
  /* Additional responsive styles */
  padding: calc(var(--gap) / 2);
}

/* Override styles at specific breakpoints */
@media (max-width: 767px) {
  .carousel-slide {
    padding: 8px;
  }
}

@media (min-width: 1200px) {
  .carousel-slide {
    padding: 16px;
  }
}

Dynamic Responsive Updates

Runtime Responsive Changes
// Listen for orientation changes
window.addEventListener('orientationchange', () => {
  // Wait for orientation change to complete
  setTimeout(() => {
    carousel.refresh(true);  // Deep refresh after orientation change
  }, 100);
});

// Update responsive config dynamically
function updateCarouselForViewport() {
  const width = window.innerWidth;
  
  // You can't change the responsive config after initialization,
  // but you can work with the existing breakpoints
  if (width < 768) {
    carousel.pauseAutoplay();  // Pause on mobile to save battery
  } else {
    carousel.playAutoplay();   // Resume on larger screens
  }
}

// Debounced resize handler
let resizeTimeout;
window.addEventListener('resize', () => {
  clearTimeout(resizeTimeout);
  resizeTimeout = setTimeout(updateCarouselForViewport, 150);
});
Automatic Refresh: The carousel automatically handles window resize events and updates layouts using ResizeObserver when available, falling back to window resize events for broader compatibility.
Performance Tip: Responsive breakpoints are processed in ascending order. Define them from smallest to largest screen sizes for optimal performance.

API Methods

The carousel returns a GSAP Timeline instance with additional methods for programmatic control. All methods include error handling and state validation.

Navigation Methods

Slide Navigation
// Navigate to specific slide by index (0-based)
carousel.toIndex(2);  // Go to 3rd slide

// Navigate with custom animation options
carousel.toIndex(2, {
  duration: 1.2,           // Custom duration
  ease: "power2.inOut",    // Custom easing
  onComplete: () => {      // Callback when animation completes
    console.log("Animation finished");
  }
});

// Navigate to next/previous slide
carousel.next();                    // Next slide with default options
carousel.previous();                // Previous slide with default options

// Navigate with options
carousel.next({
  duration: 0.8,
  ease: "back.out(1.7)"
});

// Get current slide index
const currentIndex = carousel.current();  // Returns current slide index (0-based)
console.log(`Currently on slide ${currentIndex + 1}`);

Playback Control

Animation Control
// Autoplay control (works with autoplayDelay setting)
carousel.pauseAutoplay();           // Pause automatic advancement
carousel.playAutoplay();            // Resume automatic advancement

// Standard GSAP timeline controls
carousel.play();                    // Play the continuous loop
carousel.pause();                   // Pause at current position
carousel.reverse();                 // Reverse direction
carousel.resume();                  // Resume from paused state

// Timeline properties
console.log(carousel.duration());   // Get total loop duration
console.log(carousel.progress());   // Get current progress (0-1)
console.log(carousel.time());       // Get current time position

// Set specific timeline position
carousel.progress(0.5);             // Jump to middle of loop
carousel.time(2);                   // Jump to 2 seconds into loop

Utility Methods

Layout and State Management
// Refresh layout calculations (after DOM changes)
carousel.refresh();                 // Light refresh - recalculate positions

carousel.refresh(true);             // Deep refresh - rebuild entire timeline
                                   // Use after adding/removing slides

// Get slide information
const closestIndex = carousel.closestIndex();      // Don't update internal state
const currentIndex = carousel.closestIndex(true);  // Update internal state

// State inspection (debug mode only)
if (carousel.getState) {
  const state = carousel.getState();
  console.log("Current state:", state);
  console.log("Configuration:", state.config);
  console.log("Timeline properties:", {
    duration: carousel.duration(),
    progress: carousel.progress(),
    paused: carousel.paused()
  });
}

// Check if carousel is destroyed
const isDestroyed = carousel.paused() && !carousel.duration();

// Cleanup (important for memory management)
carousel.cleanup();                 // Remove all listeners and animations
                                   // Call when removing carousel from DOM

Advanced Timeline Manipulation

GSAP Timeline Features
// Speed control
carousel.timeScale(0.5);            // Play at half speed
carousel.timeScale(2);              // Play at double speed
carousel.timeScale(1);              // Reset to normal speed

// Event callbacks (GSAP timeline events)
carousel.eventCallback("onComplete", () => {
  console.log("Loop cycle completed");
});

carousel.eventCallback("onUpdate", () => {
  // Called continuously during animation
  const progress = carousel.progress();
  updateProgressBar(progress);
});

// Kill specific animations
gsap.killTweensOf(carousel);        // Kill the main timeline
carousel.kill();                    // Kill timeline and all associated animations

// Chain method calls
carousel
  .pause()
  .toIndex(0, { duration: 1 })
  .play();
Full GSAP Timeline Integration: The carousel returns a native GSAP Timeline instance, which means you can use any GSAP Timeline methods, properties, and callbacks available in the GSAP API. This includes method chaining, event callbacks, timeline controls, and all advanced GSAP features.

Available Methods & Callbacks: play(), pause(), reverse(), restart(), seek(), timeScale(), duration(), progress(), eventCallback(), and many more.

📖 Complete Reference: GSAP Timeline Documentation - See all available methods, properties, and callbacks you can use with your carousel instance.

Events & Callbacks

The carousel provides comprehensive callback system for responding to user interactions and state changes. All callbacks receive detailed payload objects with relevant data.

onChange Callback

Fired when the active slide changes, providing comprehensive information about the current state:

onChange Event Details
const carousel = horizontalLoop("#carousel", {
  onChange: (payload) => {
    // Payload object contains:
    console.log("Current slide element:", payload.currentItem);        // HTMLElement
    console.log("Current slide index:", payload.currentIndex);         // 0-based index
    console.log("Total slides:", payload.totalItems);                  // Total count
    console.log("Animation progress:", payload.progress);              // 0-1 progress
    console.log("Current slide width:", payload.slideWidth);          // Width in pixels
    console.log("Timeline instance:", payload.timeline);              // GSAP timeline
    console.log("Configuration:", payload.config);                    // Current config
    
    // Practical usage examples:
    
    // Update slide counter UI
    document.querySelector('.slide-counter').textContent = 
      `${payload.currentIndex + 1} / ${payload.totalItems}`;
    
    // Update slide title
    const title = payload.currentItem.querySelector('h3')?.textContent;
    if (title) {
      document.querySelector('.current-slide-title').textContent = title;
    }
    
    // Update progress bar
    const progressPercent = (payload.currentIndex / (payload.totalItems - 1)) * 100;
    document.querySelector('.progress-bar').style.width = `${progressPercent}%`;
    
    // Analytics tracking
    if (typeof gtag !== 'undefined') {
      gtag('event', 'carousel_slide_change', {
        slide_index: payload.currentIndex,
        slide_title: title || `Slide ${payload.currentIndex + 1}`
      });
    }
    
    // Conditional logic based on slide
    if (payload.currentIndex === 0) {
      document.body.classList.add('first-slide-active');
    } else {
      document.body.classList.remove('first-slide-active');
    }
    
    // Load lazy content for upcoming slides
    const nextIndex = (payload.currentIndex + 1) % payload.totalItems;
    const nextSlide = document.querySelectorAll('#carousel > *')[nextIndex];
    loadLazyContent(nextSlide);
  },
  
  // Performance optimization: only fire after settling
  updateOnlyOnSettle: true,  // Reduces callback frequency during dragging
});

onInitialized Callback

Fired once after the carousel completes setup, ideal for initialization tasks:

Initialization Handling
const carousel = horizontalLoop("#carousel", {
  onInitialized: (payload) => {
    console.log("Carousel fully initialized!", payload);
    
    // Hide loading spinner
    document.querySelector('.carousel-loading')?.remove();
    
    // Show carousel with fade-in effect
    gsap.to("#carousel", {
      opacity: 1,
      duration: 0.5,
      ease: "power2.out"
    });
    
    // Initialize related UI components
    initializeCarouselControls(payload);
    
    // Set up keyboard shortcuts
    setupKeyboardShortcuts(payload.timeline);
    
    // Start analytics session
    trackCarouselSession({
      totalSlides: payload.totalItems,
      hasAutoplay: payload.config.autoplayDelay > 0,
      hasDragging: payload.config.draggable
    });
    
    // Preload images in slides
    preloadSlideImages(payload.timeline);
    
    // Initialize intersection observer for performance
    setupSlideVisibilityTracking();
  }
});

GSAP Timeline Events

Since the carousel returns a GSAP Timeline, you can use all standard GSAP event callbacks:

GSAP Timeline Events
// Set up GSAP timeline event callbacks
carousel.eventCallback("onStart", () => {
  console.log("Carousel animation started");
  document.body.classList.add('carousel-animating');
});

carousel.eventCallback("onUpdate", () => {
  // Called on every frame during animation
  const progress = carousel.progress();
  
  // Update visual progress indicator
  const indicator = document.querySelector('.carousel-progress');
  if (indicator) {
    indicator.style.transform = `scaleX(${progress})`;
  }
  
  // Parallax effect based on carousel progress
  gsap.set('.background-element', {
    x: progress * -100,
    ease: "none"
  });
});

carousel.eventCallback("onComplete", () => {
  console.log("Carousel loop cycle completed");
  
  // This fires after each complete loop when repeat is set
  updateLoopCounter();
});

carousel.eventCallback("onReverseComplete", () => {
  console.log("Reverse animation completed");
});

// Remove event callbacks
carousel.eventCallback("onUpdate", null);  // Remove specific callback
carousel.eventCallback(null);              // Remove all callbacks

Custom Event System

Custom Events with EventTarget
// Extend carousel with custom event system
const carouselEvents = new EventTarget();

const carousel = horizontalLoop("#carousel", {
  onChange: (payload) => {
    // Dispatch custom events
    carouselEvents.dispatchEvent(new CustomEvent('slideChange', {
      detail: payload
    }));
    
    // Dispatch specific events based on slide content
    const slideType = payload.currentItem.dataset.type;
    if (slideType) {
      carouselEvents.dispatchEvent(new CustomEvent(`slide${slideType}Active`, {
        detail: payload
      }));
    }
  }
});

// Listen for custom events
carouselEvents.addEventListener('slideChange', (event) => {
  const { currentIndex, currentItem } = event.detail;
  console.log(`Slide changed to index ${currentIndex}`);
});

carouselEvents.addEventListener('slideVideoActive', (event) => {
  // Handle video slides specifically
  const videoElement = event.detail.currentItem.querySelector('video');
  if (videoElement) {
    videoElement.play();
  }
});

carouselEvents.addEventListener('slideImageActive', (event) => {
  // Handle image slides with specific logic
  const img = event.detail.currentItem.querySelector('img');
  if (img && !img.complete) {
    // Show loading spinner for unloaded images
    showImageLoading(img);
  }
});
Performance Consideration: The onChange callback can fire frequently during dragging. Use updateOnlyOnSettle: true if your callback performs expensive operations like DOM manipulation or API calls.

Important: updateOnlyOnSettle only affects the onChange callback defined in the carousel configuration. If you need to receive every update while using updateOnlyOnSettle, you can use the timeline's native eventCallback("onUpdate") method instead:

carousel.eventCallback("onUpdate", () => { /* fires on every frame */ });

Browser Support

Supported Browsers

Browser Minimum Version Features Notes
Chrome 60+ All features Full support including advanced dragging
Firefox 55+ All features Excellent performance
Safari 12+ All features iOS Safari 12+ also supported
Edge 79+ (Chromium) All features Legacy Edge not supported
Chrome Mobile 60+ All features Touch interactions work perfectly
Samsung Internet 8.2+ Most features Some advanced CSS features limited

Required Browser Features

  • ES6 Support: Arrow functions, const/let, template literals
  • Modern DOM APIs: querySelector, addEventListener, classList
  • CSS Features: CSS Custom Properties, Flexbox, Transform3d
  • Optional APIs: ResizeObserver (graceful fallback), IntersectionObserver

Feature Detection

Browser Capability Checking
// The carousel automatically detects browser capabilities
function checkBrowserSupport() {
  const checks = {
    gsap: typeof gsap !== 'undefined',
    es6: (() => {
      try {
        new Function('const x = () => {}; class Y {}');
        return true;
      } catch (e) {
        return false;
      }
    })(),
    customProperties: CSS.supports('--foo', 'red'),
    resizeObserver: typeof ResizeObserver !== 'undefined',
    draggable: typeof Draggable !== 'undefined',
    intersectionObserver: typeof IntersectionObserver !== 'undefined'
  };
  
  console.log('Browser support check:', checks);
  return checks;
}

// Initialize carousel with fallbacks
function initCarouselWithFallback() {
  const support = checkBrowserSupport();
  
  if (!support.gsap) {
    console.error('GSAP not found - carousel cannot initialize');
    showStaticFallback();
    return;
  }
  
  if (!support.es6) {
    console.warn('Limited ES6 support - some features may not work');
  }
  
  const config = {
    draggable: support.draggable,
    // Adjust config based on capabilities
  };
  
  return horizontalLoop('#carousel', config);
}

function showStaticFallback() {
  // Show a basic CSS-only carousel or static grid
  document.querySelector('.carousel-container').style.overflowX = 'auto';
  document.querySelector('.carousel-container').style.scrollBehavior = 'smooth';
}
Internet Explorer: Not supported. The carousel requires modern browser features that are not available in IE11 or earlier. Consider showing a static fallback for IE users.
Progressive Enhancement: The carousel degrades gracefully when advanced features aren't available. Without JavaScript, slides are still viewable as a horizontal scrolling container.

Troubleshooting

Common Issues and Solutions

Carousel not initializing:
  • Check GSAP loading: Ensure GSAP 3.12+ loads before the carousel script
  • Verify DOM structure: Container element must exist with child elements
  • Console errors: Check browser console for JavaScript errors
  • Element timing: Initialize after DOM is ready or in window load event
Responsive layout issues:
  • Breakpoint order: Ensure responsive breakpoints are in ascending order (0, 640, 768, etc.)
  • CSS conflicts: Check for conflicting CSS that overrides carousel flex properties
  • ResizeObserver errors: Update to a browser that supports ResizeObserver or add a polyfill
  • Manual refresh: Call carousel.refresh(true) after dynamic content changes
Dragging not working:
  • Plugin missing: Include GSAP Draggable plugin
  • Configuration: Set draggable: true in carousel options
  • InertiaPlugin: For momentum scrolling, include InertiaPlugin (requires Club GreenSock)
  • Touch conflicts: Check for CSS touch-action properties that might interfere
Performance issues:
  • Heavy callbacks: Enable updateOnlyOnSettle: true for expensive onChange callbacks
  • Large datasets: Consider virtual scrolling for 50+ slides
  • Image optimization: Use appropriate image sizes and lazy loading
  • Memory leaks: Always call carousel.cleanup() when destroying
Accessibility problems:
  • Screen readers: Verify accessibilityEnabled: true (default)
  • Keyboard navigation: Test with Tab, Arrow keys, Enter, and Space
  • ARIA labels: Set meaningful ariaLabel in configuration
  • Focus management: Ensure buttons and slides receive proper focus

Debug Mode

Enable detailed logging to diagnose issues:

Debug Configuration
const carousel = horizontalLoop("#carousel", {
  debug: true,  // Enables detailed console logging
  // ... other options
});

// Access debug information
if (carousel && carousel.getState) {
  const debugInfo = carousel.getState();
  console.log("Carousel state:", debugInfo);
  
  // Log configuration
  console.log("Configuration:", debugInfo.config);
  
  // Log timeline properties  
  console.log("Timeline duration:", carousel.duration());
  console.log("Current progress:", carousel.progress());
  console.log("Is paused:", carousel.paused());
}

// Monitor carousel events
carousel.eventCallback("onUpdate", () => {
  console.log("Update:", {
    time: carousel.time(),
    progress: carousel.progress(),
    currentSlide: carousel.current()
  });
});

Error Handling Patterns

Robust Error Handling
function initializeCarouselSafely(selector, config = {}) {
  try {
    // Check prerequisites
    if (typeof gsap === 'undefined') {
      throw new Error('GSAP library not found');
    }
    
    if (gsap.version < '3.12') {
      console.warn('GSAP version 3.12+ recommended for best compatibility');
    }
    
    // Check container element
    const container = document.querySelector(selector);
    if (!container) {
      throw new Error(`Container element not found: ${selector}`);
    }
    
    const slides = container.children;
    if (slides.length === 0) {
      throw new Error('No slides found in container');
    }
    
    // Initialize with error boundaries
    const carousel = horizontalLoop(selector, {
      ...config,
      debug: process.env.NODE_ENV === 'development',
      
      // Wrap callbacks with error handling
      onChange: config.onChange ? (payload) => {
        try {
          config.onChange(payload);
        } catch (error) {
          console.error('Error in onChange callback:', error);
        }
      } : null,
      
      onInitialized: (payload) => {
        console.log('Carousel initialized successfully');
        if (config.onInitialized) {
          try {
            config.onInitialized(payload);
          } catch (error) {
            console.error('Error in onInitialized callback:', error);
          }
        }
      }
    });
    
    if (!carousel) {
      throw new Error('Carousel initialization failed');
    }
    
    // Set up error recovery
    window.addEventListener('error', (event) => {
      if (event.filename?.includes('gsap') || event.message?.includes('carousel')) {
        console.error('Carousel-related error detected:', event.error);
        // Could trigger fallback UI here
      }
    });
    
    return carousel;
    
  } catch (error) {
    console.error('Failed to initialize carousel:', error);
    
    // Show fallback UI
    showCarouselFallback(selector);
    
    return null;
  }
}

function showCarouselFallback(selector) {
  const container = document.querySelector(selector);
  if (container) {
    // Convert to simple scrollable container
    container.style.overflowX = 'auto';
    container.style.display = 'flex';
    container.style.gap = '1rem';
    
    // Add scroll buttons if needed
    const wrapper = container.parentElement;
    if (wrapper) {
      wrapper.insertAdjacentHTML('beforeend', `
        

Interactive carousel unavailable. Use scroll or swipe to navigate.

`); } } }

Common Console Warnings

"ResizeObserver loop limit exceeded": This is a browser issue, not a carousel problem. It can be safely ignored or suppress by adding CSS: * { resize: none; }
"Unknown config option": Check for typos in configuration property names. The carousel validates all options and warns about unknown properties.
"horizontalLoop: No child elements found": Ensure the container has direct child elements before initializing the carousel.

Performance Tips

Optimization Strategies

  1. Use updateOnlyOnSettle for expensive onChange callbacks to reduce execution frequency during dragging and animations
  2. Enable debug: false in production to eliminate console logging overhead
  3. Call cleanup() when removing carousels from the DOM to prevent memory leaks
  4. Optimize slide content by using CSS will-change property sparingly and only when needed
  5. Implement lazy loading for images and heavy content in slides
  6. Use transform3d for hardware acceleration in custom animations
  7. Debounce resize handlers when implementing custom responsive logic
  8. Minimize DOM queries in onChange callbacks by caching element references

Performance Monitoring

Performance Tracking
// Performance monitoring utility
class CarouselPerformanceMonitor {
  constructor(carousel) {
    this.carousel = carousel;
    this.metrics = {
      initTime: 0,
      frameCount: 0,
      lastFrameTime: 0,
      averageFPS: 0,
      memoryUsage: []
    };
    
    this.startMonitoring();
  }
  
  startMonitoring() {
    const startTime = performance.now();
    
    // Monitor initialization time
    if (this.carousel.eventCallback) {
      this.carousel.eventCallback('onUpdate', () => {
        if (!this.metrics.initTime) {
          this.metrics.initTime = performance.now() - startTime;
          console.log(`Carousel first frame: ${this.metrics.initTime.toFixed(2)}ms`);
        }
        this.trackFrameRate();
        this.trackMemoryUsage();
      });
    }
  }
  
  trackFrameRate() {
    const now = performance.now();
    if (this.metrics.lastFrameTime) {
      const delta = now - this.metrics.lastFrameTime;
      this.metrics.frameCount++;
      
      // Calculate rolling average FPS
      const fps = 1000 / delta;
      this.metrics.averageFPS = (this.metrics.averageFPS + fps) / 2;
      
      // Warn if FPS drops below 30
      if (fps < 30 && this.metrics.frameCount > 10) {
        console.warn(`Low FPS detected: ${fps.toFixed(1)} fps`);
      }
    }
    this.metrics.lastFrameTime = now;
  }
  
  trackMemoryUsage() {
    if (performance.memory) {
      const usage = {
        used: performance.memory.usedJSHeapSize,
        total: performance.memory.totalJSHeapSize,
        limit: performance.memory.jsHeapSizeLimit,
        timestamp: Date.now()
      };
      
      this.metrics.memoryUsage.push(usage);
      
      // Keep only last 50 measurements
      if (this.metrics.memoryUsage.length > 50) {
        this.metrics.memoryUsage.shift();
      }
      
      // Check for potential memory leaks
      if (this.metrics.memoryUsage.length >= 20) {
        const recent = this.metrics.memoryUsage.slice(-10);
        const older = this.metrics.memoryUsage.slice(-20, -10);
        
        const recentAvg = recent.reduce((sum, m) => sum + m.used, 0) / recent.length;
        const olderAvg = older.reduce((sum, m) => sum + m.used, 0) / older.length;
        
        // If memory usage consistently growing
        if (recentAvg > olderAvg * 1.1) {
          console.warn('Potential memory leak detected in carousel');
        }
      }
    }
  }
  
  getReport() {
    return {
      ...this.metrics,
      recommendations: this.generateRecommendations()
    };
  }
  
  generateRecommendations() {
    const recommendations = [];
    
    if (this.metrics.averageFPS < 45) {
      recommendations.push('Consider enabling updateOnlyOnSettle for better performance');
    }
    
    if (this.metrics.initTime > 100) {
      recommendations.push('Consider lazy loading slide content');
    }
    
    const latestMemory = this.metrics.memoryUsage[this.metrics.memoryUsage.length - 1];
    if (latestMemory && latestMemory.used > 50 * 1024 * 1024) { // 50MB
      recommendations.push('High memory usage detected - ensure cleanup() is called');
    }
    
    return recommendations;
  }
}

// Usage
const carousel = horizontalLoop('#carousel', config);
const monitor = new CarouselPerformanceMonitor(carousel);

// Get performance report
setTimeout(() => {
  console.log('Performance Report:', monitor.getReport());
}, 5000);

Memory Management Best Practices

Proper Lifecycle Management
// Carousel lifecycle manager
class CarouselManager {
  constructor() {
    this.carousels = new Map();
    this.setupGlobalCleanup();
  }
  
  create(selector, config) {
    const carousel = horizontalLoop(selector, {
      ...config,
      debug: false, // Always disable in production
      
      // Wrap onChange to prevent memory leaks
      onChange: config.onChange ? (payload) => {
        // Use WeakRef if available for payload references
        if (typeof WeakRef !== 'undefined') {
          const weakPayload = {
            ...payload,
            currentItem: new WeakRef(payload.currentItem),
            timeline: new WeakRef(payload.timeline)
          };
          config.onChange(weakPayload);
        } else {
          config.onChange(payload);
        }
      } : null
    });
    
    if (carousel) {
      this.carousels.set(selector, carousel);
    }
    
    return carousel;
  }
  
  destroy(selector) {
    const carousel = this.carousels.get(selector);
    if (carousel) {
      carousel.cleanup();
      this.carousels.delete(selector);
    }
  }
  
  destroyAll() {
    this.carousels.forEach((carousel, selector) => {
      carousel.cleanup();
    });
    this.carousels.clear();
  }
  
  setupGlobalCleanup() {
    // Cleanup on page unload
    window.addEventListener('beforeunload', () => {
      this.destroyAll();
    });
    
    // Cleanup on page visibility change (mobile)
    document.addEventListener('visibilitychange', () => {
      if (document.hidden) {
        // Pause all carousels to save resources
        this.carousels.forEach(carousel => {
          carousel.pauseAutoplay();
        });
      }
    });
  }
}

// Global manager instance
const carouselManager = new CarouselManager();

// Usage in your application
const carousel = carouselManager.create('#carousel', {
  autoplayDelay: 3,
  draggable: true
});

// Clean up when component unmounts
function cleanupComponent() {
  carouselManager.destroy('#carousel');
}

Production Optimization Checklist

  • ✓ Set debug: false in production builds
  • ✓ Use updateOnlyOnSettle: true for expensive callbacks
  • ✓ Implement proper cleanup in component lifecycle methods
  • ✓ Lazy load images and heavy content in slides
  • ✓ Minimize onChange callback complexity and use debouncing for heavy operations
  • ✓ Test on low-end devices and slow networks
  • ✓ Monitor bundle size and consider code splitting for large carousels
  • ✓ Use intersection observers for visibility-based optimizations
Production Ready: This carousel has been optimized for production use with automatic cleanup, memory management, error boundaries, and performance monitoring capabilities.