Instructions

This template uses GSAP (GreenSock Animation Platform) to create smooth, high-performance animations across multiple sections of the website. All GSAP scripts are written in pure JavaScript and organized for easy customization, allowing you to adjust animation speed, direction, easing, triggers, and timing without affecting the overall structure.


Each animation includes clear comments to help you understand how it works, making it simple to modify or extend the interactions to match your project's needs while remaining fully compatible with Webflow.
Lenis Script Documentation
This script integrates Lenis, a modern smooth-scrolling library, with GSAP (GreenSock Animation Platform) and ScrollTrigger. It provides an ultra-smooth, high-performance scrolling experience while ensuring all scroll-driven animations stay perfectly synchronized. By routing Lenis's requestAnimationFrame (raf) through GSAP's internal ticker and disabling lag smoothing, the setup prevents jitter and maintains precise scroll position tracking across all devices.
Key Features :
  • Smooth Scrolling: Delivers continuous, fluid scrolling using inertia (lerp: 0.1).
  • GSAP Synchronization: Keeps ScrollTrigger updates aligned with Lenis's custom scroll frame.
  • Lag Smoothing Override: Eliminates animation jumps during sudden frame drops or window re-focusing.
<!-- =========================================================
     LENIS SMOOTH SCROLL
     ---------------------------------------------------------
     Lenis handles smooth scrolling and is synchronized
     with GSAP ScrollTrigger.
     ========================================================= -->

<script src="https://unpkg.com/lenis@1.3.4/dist/lenis.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/lenis@1.3.4/dist/lenis.css"/>

<script>
  // Initialize Lenis smooth scrolling
  const lenis = new Lenis({
    smooth: true,
    lerp: 0.1,
    wheelMultiplier: 0.75,
    infinite: false,
  });

  // Keep ScrollTrigger synchronized with Lenis
  lenis.on("scroll", ScrollTrigger.update);

  // Run Lenis through the GSAP ticker
  gsap.ticker.add((time) => {
    lenis.raf(time * 1000);
  });

  // Disable GSAP lag smoothing for consistent scroll synchronization
  gsap.ticker.lagSmoothing(0);
</script>
A. How to Edit Lenis Animations
You can adjust the smooth scroll performance and GSAP integration behavior directly in the script using these key parameters:
  1. Lenis Smooth Scroll Settings:
    const lenis = new Lenis({
      smooth: true,          // Enables/disables smooth scrolling (true/false)
      lerp: 0.1,             // Scroll interpolation/smoothness (lower values = smoother/slower catch-up)
      wheelMultiplier: 0.75, // Mouse wheel scroll speed multiplier
      infinite: false,       // Enables or disables infinite looping scroll
    });
  2. GSAP Ticker & Lag Smoothing:
    // Disables GSAP's lag smoothing to ensure GSAP and Lenis tick on the exact same frame
    gsap.ticker.lagSmoothing(0);
B. Removing GSAP Animations
If you want to modify or remove the GSAP synchronization while keeping Lenis smooth scroll, follow these steps:
  1. To disable GSAP synchronization entirely while keeping basic Lenis smooth scrolling, remove or comment out the GSAP integration lines:
    // Remove or comment out these lines:
    
    // lenis.on("scroll", ScrollTrigger.update);
    // gsap.ticker.add((time) => { lenis.raf(time * 1000); });
    // gsap.ticker.lagSmoothing(0);
  2. Replace the ticker update with standard requestAnimationFrame logic to keep Lenis running independently:
    function raf(time) {
      lenis.raf(time);
      requestAnimationFrame(raf);
    }
    requestAnimationFrame(raf);
Philosophy Scroll Animation Documentation
This script creates an interactive philosophy section using GSAP and ScrollTrigger. It combines two scroll-driven effects: animated SVG lines that bend according to the scroll direction, and philosophy blocks that rotate with momentum as the user scrolls.
Key Features :
  • Directional Bend: SVG philosophy lines bend left or right depending on the user's scroll direction.
  • Automatic Line Reset: Lines smoothly return to their original straight position when scrolling stops.
  • Momentum Rotation: Philosophy blocks rotate according to the direction of scrolling.
  • Rotation Limit: Prevents blocks from rotating beyond the defined maximum range.
  • Friction Effect: Creates a smooth deceleration after scrolling stops.
  • GSAP Ticker: Continuously updates block rotation for fluid motion.
  • ScrollTrigger: Controls when the philosophy animation becomes active within the viewport.
<script>
  gsap.registerPlugin(ScrollTrigger);

  // =========================================================
  // BEND LINE
  // =========================================================

  const paths = document.querySelectorAll('.philosophy-line');

  const bendAmount = 35;

  let lastDirection = 0;
  let debounce;

  const straightD = 'M20,0 Q20,132.75 20,265.5';
  const rightD = `M20,0 Q${20 + bendAmount},132.75 20,265.5`;
  const leftD = `M20,0 Q${20 - bendAmount},132.75 20,265.5`;

  // =========================================================
  // ROTATE BLOCK
  // =========================================================

  const blocks = document.querySelectorAll('.block-line-philosophy');

  const rotationSpeed = 70;
  const rotationRange = 1420;

  // Semakin mendekati 1 = semakin lama berhenti
  const friction = 0.96;

  let rotationVelocity = 0;
  let scrollDirection = 0;

  // =========================================================
  // INITIAL BLOCK SETUP
  // =========================================================

  blocks.forEach((el) => {
    el._baseAngle = parseFloat(el.getAttribute('data-angle')) || 0;

    el._currentAngle = el._baseAngle;

    gsap.set(el, {
      rotation: el._baseAngle,
      transformOrigin: '50% 100%',
    });
  });

  // =========================================================
  // SCROLL TRIGGER
  // =========================================================

  if (paths.length || blocks.length) {
    ScrollTrigger.create({
      trigger: '.wrapper-philosophy',

      start: 'top bottom',
      end: 'bottom top',

      // =====================================================
      // ON UPDATE
      // =====================================================

      onUpdate: (self) => {
        scrollDirection = self.direction;

        rotationVelocity = scrollDirection * rotationSpeed;

        // ===================================================
        // BEND LINE
        // ===================================================

        if (paths.length && self.direction !== lastDirection) {
          lastDirection = self.direction;

          gsap.to(paths, {
            duration: 0.5,

            overwrite: 'auto',

            attr: {
              d: self.direction === 1 ? rightD : leftD,
            },

            ease: 'power2.out',
          });
        }

        // ===================================================
        // RESET TIMER
        // ===================================================

        clearTimeout(debounce);

        debounce = setTimeout(() => {
          resetLine();

          scrollDirection = 0;
        }, 120);
      },
    });

    // =======================================================
    // RESET LINE
    // =======================================================

    function resetLine() {
      if (!paths.length) return;

      gsap.to(paths, {
        duration: 0.6,

        overwrite: 'auto',

        attr: {
          d: straightD,
        },

        ease: 'power2.inOut',
      });

      lastDirection = 0;
    }

    // =======================================================
    // ROTATION LOOP
    // =======================================================

    gsap.ticker.add(() => {
      if (!blocks.length) return;

      const dt = gsap.ticker.deltaRatio(60) / 60;

      blocks.forEach((el) => {
        // ===============================================
        // CURRENT OFFSET
        // ===============================================

        let offset = el._currentAngle - el._baseAngle;

        // ===============================================
        // APPLY ROTATION
        // ===============================================

        offset += rotationVelocity * dt;

        // ===============================================
        // ROTATION LIMIT
        // ===============================================

        offset = gsap.utils.clamp(-rotationRange, rotationRange, offset);

        // ===============================================
        // SAVE ANGLE
        // ===============================================

        el._currentAngle = el._baseAngle + offset;

        // ===============================================
        // APPLY TRANSFORM
        // ===============================================

        gsap.set(el, {
          rotation: el._currentAngle,
        });
      });

      // ===================================================
      // FRICTION
      // ===================================================

      rotationVelocity *= Math.pow(friction, dt * 60);

      // ===================================================
      // STOP WHEN VERY SMALL
      // ===================================================

      if (Math.abs(rotationVelocity) < 0.05) {
        rotationVelocity = 0;
      }
    });
  }
</script>
A. How to Edit Philosophy Animations
You can customize the visual behavior of the Philosophy section directly in the script using the parameters below.
  1. Bend Line Settings
    const bendAmount = 35; //Higher value → stronger/more dramatic bend
    
    const straightD = 'M20,0 Q20,132.75 20,265.5';
    const rightD = `M20,0 Q${20 + bendAmount},132.75 20,265.5`;
    const leftD = `M20,0 Q${20 - bendAmount},132.75 20,265.5`;
  2. Line Animation Duration
    if (paths.length && self.direction !== lastDirection) {
      lastDirection = self.direction;
    
      gsap.to(paths, {
        duration: 0.5, //Lower value → faster response
    
        overwrite: 'auto',
    
        attr: {
          d: self.direction === 1 ? rightD : leftD,
        },
    
        ease: 'power2.out',
      });
    }
  3. Line Reset Duration
    function resetLine() {
      if (!paths.length) return;
    
      gsap.to(paths, {
        duration: 0.6, //Lower value → faster response
    
        overwrite: 'auto',
    
        attr: {
          d: straightD,
        },
    
        ease: 'power2.inOut',
      });
    
      lastDirection = 0;
    }
  4. Editing Block Rotation
    The .block-line-philosophy elements rotate based on the user's scroll direction.
    const rotationSpeed = 70; //Higher value → faster rotation
    const rotationRange = 1420; //Increasing the value allows the blocks to rotate further.
    const friction = 0.96; //The closer the value is to 1, the longer the movement continues.
  5. Editing the Scroll Trigger
    trigger: '.wrapper-philosophy', //Class of main trigger
    
    //This makes the animation operate within a more restricted viewport area.
    start: 'top bottom',
    end: 'bottom top',
B. Removing the Bend Line Animation
If you want to remove the SVG line bending effect while keeping the block rotation, remove or comment out the following parts:
  1. Remove the line selector
    const paths = document.querySelectorAll('.philosophy-line');
  2. Remove the bend settings
    const bendAmount = 35;
    
    const straightD = 'M20,0 Q20,132.75 20,265.5';
    const rightD = `M20,0 Q${20 + bendAmount},132.75 20,265.5`;
    const leftD = `M20,0 Q${20 - bendAmount},132.75 20,265.5`;
  3. Remove the line animation inside onUpdate
    if (paths.length && self.direction !== lastDirection) {
      ...
    }
  4. Remove the resetLine() function
    function resetLine() {
      ...
    }