Instructions

Numbers counter
Info
Numbers animate from a starting value to a target value when scrolling into view. The system automatically detects decimal places from your target number and maintains the same format throughout the animation. Supports both dot and comma as decimal separators — perfect for international number formats.
How it works
  • Add data-count="100" to any text element with your target number.
  • Optional: add data-count-start="50" to set a starting number (default 0).
  • Optional: add data-count-duration="3" to set duration in seconds (default 2).
  • For decimals, use the format you want displayed-data-count="99.9" for dot  separator-data-count="99,9" for comma separator.
Features
  • Scroll-triggered counting that brings your metrics to life.
  • Automatic decimal detection — set your target, it handles the rest.
  • Plays once per page load — no repeated animations on scroll up/down.
  • Customizable per-element — different speeds and start values as needed.
  • Integrates with ScrollTrigger for precise viewport detection.
Gsap Code
<script>
// ==============================================
// GSAP Number Counter Animation
// ==============================================

document.addEventListener('DOMContentLoaded', function() {
    
    // ============================================
    // Check for GSAP and ScrollTrigger
    // ============================================
    if (typeof gsap === 'undefined') return;
    
    gsap.registerPlugin(ScrollTrigger);

    // ============================================
    // DOM Element Selection
    // ============================================
    const counters = document.querySelectorAll('[data-count]');
    
    if (!counters.length) return;

    // ============================================
    // Configuration
    // ============================================
    const CONFIG = {
        defaultDuration: 2,
        defaultStart: 0,
        ease: "power2.out",
        triggerStart: "top 85%"
    };

    // ============================================
    // Utility Functions
    // ============================================
    
    function getDecimalPlaces(numString) {
        const normalized = numString.replace(',', '.');
        const parts = normalized.split('.');
        
        if (parts.length > 1) {
            return parts[1].length;
        }
        return 0;
    }

    function parseNumber(numString) {
        const normalized = numString.replace(',', '.');
        return parseFloat(normalized);
    }

    function formatNumber(num, decimals, useComma) {
        const formatted = num.toFixed(decimals);
        
        if (useComma) {
            return formatted.replace('.', ',');
        }
        return formatted;
    }

    // ============================================
    // Process each counter
    // ============================================
    counters.forEach(function(element) {
        
        // Get target number from attribute
        const targetString = element.getAttribute('data-count');
        
        if (!targetString) return;

        // Parse configuration
        const targetNumber = parseNumber(targetString);
        const startNumber = parseNumber(element.getAttribute('data-count-start') || String(CONFIG.defaultStart));
        const duration = parseFloat(element.getAttribute('data-count-duration') || CONFIG.defaultDuration);
        const decimals = getDecimalPlaces(targetString);
        const useComma = targetString.includes(',');

        // Validate target number
        if (isNaN(targetNumber)) return;

        // Set initial value
        element.textContent = formatNumber(startNumber, decimals, useComma);

        // Counter object for GSAP
        const counter = { value: startNumber };

        // Create ScrollTrigger animation
        gsap.to(counter, {
            value: targetNumber,
            duration: duration,
            ease: CONFIG.ease,
            scrollTrigger: {
                trigger: element,
                start: CONFIG.triggerStart,
                once: true
            },
            onUpdate: function() {
                element.textContent = formatNumber(counter.value, decimals, useComma);
            }
        });
    });
});
</script>
Lenis smooth scroll
Info
Lenis is a lightweight JavaScript library for creating smooth scrolling effects on websites. It’s popular in modern portfolio sites, animation-heavy landing pages, and creative web experiences because it keeps scrolling fluid while still using native browser scrolling behavior
How it works
  • Include the required library
  • Add this code to your JavaScript file.
  • Lenis will smoothly scroll to the section automatically.
Features
  • Smooth scrolling - Makes page movement fluid
  • Lightweight - Very small bundle size
  • Native scrollbar - Keeps browser scrollbar
  • Accessibility - Better keyboard/search support
  • Custom easing - Control scroll feel
Gsap Code
<script>
  gsap.registerPlugin(ScrollTrigger);

  const lenis = new Lenis({
    duration: 1.5,
    easing: (t) => 1 - Math.pow(1 - t, 3),
    smooth: true,
    smoothTouch: false, // ✅ important
  });

  // sync
  lenis.on('scroll', ScrollTrigger.update);

  // raf loop
  function raf(time) {
    lenis.raf(time);
    requestAnimationFrame(raf);
  }
  requestAnimationFrame(raf);

  // ✅ correct scroller
  ScrollTrigger.scrollerProxy(document.documentElement, {
    scrollTop(value) {
      return arguments.length ? lenis.scrollTo(value, { immediate: true }) : lenis.scroll;
    },
    getBoundingClientRect() {
      return {
        top: 0,
        left: 0,
        width: window.innerWidth,
        height: window.innerHeight,
      };
    },
  });

  // ✅ important
  ScrollTrigger.defaults({
    scroller: document.documentElement,
  });

  // refresh fix
  ScrollTrigger.addEventListener('refresh', () => lenis.resize());

  ScrollTrigger.refresh();
</script>
More Templates