All GSAP animations used in this template are collected here. On this page, you’ll find guidance on how to locate and edit them. Each code block comes with extra notes to make it easier to understand.
You can find the code in the Embed Code inside this template.
<link rel="stylesheet" href="https://unpkg.com/lenis@1.3.15/dist/lenis.css" />
<script src="https://unpkg.com/lenis@1.3.15/dist/lenis.min.js"></script><script>
// Initialize Lenis
const lenis = new Lenis({ duration: 1.4 });
// Connect to GSAP ScrollTrigger
lenis.on('scroll', ScrollTrigger.update);
gsap.ticker.add((time) => {
lenis.raf(time * 1000);
});
gsap.ticker.lagSmoothing(0);
</script>For efficiency in animating text on headings and subheadings, you can use the following GSAP script by applying the class "text-enterance" to the div block or text element of the heading, and do the same for the subheading as well.
<script>
gsap.registerPlugin(ScrollTrigger);
/* ================= TEXT ENTRANCE ================= */
gsap.utils.toArray('.text-enterance').forEach((el) => {
gsap.from(el, {
opacity: 0,
y: 60,
duration: 1.2,
ease: 'power3.out',
scrollTrigger: {
trigger: el,
start: 'top 85%',
once: true,
},
});
});
/* ================= DESC ENTRANCE ================= */
gsap.utils.toArray('.desc-enterance').forEach((el) => {
gsap.from(el, {
opacity: 0,
y: 40,
filter: 'blur(10px)',
duration: 1.4,
delay: 0.2,
ease: 'power3.out',
scrollTrigger: {
trigger: el,
start: 'top 85%',
once: true,
},
});
});
</script>And in this FAQ section, there is a mouse trail animation effect, featuring a gradient image element moving in the background of the section that follows your cursor.
<script>
document.addEventListener('DOMContentLoaded', () => {
const faqItems = document.querySelectorAll('.wrapper-faq');
if (!faqItems.length) return;
faqItems.forEach((trigger) => {
const target = trigger.querySelector('.hoverglow');
if (!target) return;
// Initial state
gsap.set(target, {
opacity: 0,
xPercent: -50,
yPercent: -50,
pointerEvents: 'none',
});
// Smooth follow
const xTo = gsap.quickTo(target, 'x', { duration: 0.3 });
const yTo = gsap.quickTo(target, 'y', { duration: 0.3 });
// Move
trigger.addEventListener('pointermove', (e) => {
const rect = trigger.getBoundingClientRect();
xTo(e.clientX - rect.left);
yTo(e.clientY - rect.top);
});
// Enter
trigger.addEventListener('pointerenter', (e) => {
const rect = trigger.getBoundingClientRect();
gsap.set(target, {
x: e.clientX - rect.left,
y: e.clientY - rect.top,
});
gsap.to(target, {
opacity: 0.5,
duration: 0.25,
});
});
// Leave
trigger.addEventListener('pointerleave', () => {
gsap.to(target, {
opacity: 0,
duration: 0.2,
});
});
});
});
</script>
Dynamic hero text with a looping scramble effect
<script>
gsap.registerPlugin(ScrambleTextPlugin);
document.querySelectorAll('.scramble-title').forEach((el) => {
const originalText = el.textContent.trim();
gsap.to(el, {
duration: 1.2,
scrambleText: {
text: originalText,
chars: '░▒▓█', // scramble characters and cursor
speed: 0.5,
revealDelay: 0.1,
},
ease: 'none',
repeat: -1, // Repeat infinitely
repeatDelay: 2.5, // Delay of 2.5 seconds between repeats
});
});
</script>Then, in the team section, you need to insert the following script code to enable the mouse effect on the horizontal team wrapper.
<script>
gsap.registerPlugin(Draggable, InertiaPlugin);
const init = () => {
const marquee = document.querySelector('[wb-data="marquee"]');
if (!marquee) return;
const duration = parseInt(marquee.getAttribute("duration"), 20) || 5;
const marqueeContent = marquee.firstChild;
if (!marqueeContent) return;
// Clone for seamless loop
const marqueeContentClone = marqueeContent.cloneNode(true);
marquee.append(marqueeContentClone);
// Make sure marquee can be grabbed
marquee.style.cursor = "grab";
let tween;
let distanceToTranslate;
const playMarquee = () => {
let progress = tween ? tween.progress() : 0;
tween && tween.progress(0).kill();
const width = parseInt(
getComputedStyle(marqueeContent).getPropertyValue("width"),
10
);
const gap = parseInt(
getComputedStyle(marqueeContent).getPropertyValue("column-gap"),
10
);
distanceToTranslate = -1 * (gap + width);
tween = gsap.fromTo(
marquee.children,
{ x: 0 },
{ x: distanceToTranslate, duration, ease: "none", repeat: -1 }
);
tween.progress(progress);
};
playMarquee();
// ---- PAUSE ON HOVER ----
marquee.addEventListener("mouseenter", () => {
if (tween) gsap.to(tween, { timeScale: 0, duration: 0.3 });
});
marquee.addEventListener("mouseleave", () => {
if (tween) gsap.to(tween, { timeScale: 1, duration: 0.3 });
});
// ---- DRAGGABLE ----
// A proxy element that Draggable controls; we read its x and apply it to the tween
const proxy = document.createElement("div");
let startProgress = 0;
Draggable.create(proxy, {
type: "x",
trigger: marquee,
inertia: true,
onPressInit() {
// Pause the tween while user is interacting
gsap.killTweensOf(tween);
tween.timeScale(0);
marquee.style.cursor = "grabbing";
startProgress = tween.progress();
gsap.set(proxy, { x: 0 });
},
onDrag() {
// Map drag distance to tween progress
// Negative distanceToTranslate means dragging right should move progress backwards
const progressDelta = this.x / distanceToTranslate;
let newProgress = startProgress + progressDelta;
// Wrap progress between 0 and 1 so it loops infinitely
newProgress = ((newProgress % 1) + 1) % 1;
tween.progress(newProgress);
},
onThrowUpdate() {
const progressDelta = this.x / distanceToTranslate;
let newProgress = startProgress + progressDelta;
newProgress = ((newProgress % 1) + 1) % 1;
tween.progress(newProgress);
},
onRelease() {
marquee.style.cursor = "grab";
},
onThrowComplete() {
// Resume autoplay (respect hover state)
const isHovering = marquee.matches(":hover");
gsap.to(tween, { timeScale: isHovering ? 0 : 1, duration: 0.3 });
},
});
// ---- DEBOUNCED RESIZE ----
function debounce(func) {
var timer;
return function (event) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => func(), 500, event);
};
}
window.addEventListener("resize", debounce(playMarquee));
};
document.addEventListener("DOMContentLoaded", init);
</script>Then, in the team section, you need to insert the following script code to enable the mouse effect on the horizontal team wrapper.
<script>
gsap.registerPlugin(Draggable, InertiaPlugin);
const initBot = () => {
const marquee = document.querySelector('[wb-data="marquee-bottom"]');
if (!marquee) return;
const duration = parseInt(marquee.getAttribute("duration"), 20) || 5;
const marqueeContent = marquee.firstChild;
if (!marqueeContent) return;
// Clone for seamless loop
const marqueeContentClone = marqueeContent.cloneNode(true);
marquee.append(marqueeContentClone);
// Make sure marquee can be grabbed
marquee.style.cursor = "grab";
let tween;
let distanceToTranslate;
const playMarquee = () => {
let progress = tween ? tween.progress() : 0;
tween && tween.progress(0).kill();
const width = parseInt(
getComputedStyle(marqueeContent).getPropertyValue("width"),
10
);
const gap = parseInt(
getComputedStyle(marqueeContent).getPropertyValue("column-gap"),
10
);
distanceToTranslate = width + gap; // Reverse direction for bottom marquee (moving right)
tween = gsap.fromTo(
marquee.children,
{ x: -distanceToTranslate },
{ x: 0, duration, ease: "none", repeat: -1 }
);
tween.progress(progress);
};
playMarquee();
// ---- PAUSE ON HOVER ----
marquee.addEventListener("mouseenter", () => {
if (tween) gsap.to(tween, { timeScale: 0, duration: 0.3 });
});
marquee.addEventListener("mouseleave", () => {
if (tween) gsap.to(tween, { timeScale: 1, duration: 0.3 });
});
// ---- DRAGGABLE ----
const proxy = document.createElement("div");
let startProgress = 0;
Draggable.create(proxy, {
type: "x",
trigger: marquee,
inertia: true,
onPressInit() {
gsap.killTweensOf(tween);
tween.timeScale(0);
marquee.style.cursor = "grabbing";
startProgress = tween.progress();
gsap.set(proxy, { x: -distanceToTranslate });
},
onDrag() {
// Adjusting for reverse direction (from left to right)
// ProgressDelta should be calculated with respect to the direction of drag
const progressDelta = this.x / distanceToTranslate; // Directly using 'this.x' for forward movement
let newProgress = startProgress + progressDelta;
// Make sure progress is wrapped between 0 and 1 so that it loops infinitely
newProgress = ((newProgress % 1) + 1) % 1;
tween.progress(newProgress);
},
onThrowUpdate() {
const progressDelta = this.x / distanceToTranslate; // Adjusted for rightward movement
let newProgress = startProgress + progressDelta;
newProgress = ((newProgress % 1) + 1) % 1;
tween.progress(newProgress);
},
onRelease() {
marquee.style.cursor = "grab";
},
onThrowComplete() {
// Resume autoplay (respect hover state)
const isHovering = marquee.matches(":hover");
gsap.to(tween, { timeScale: isHovering ? 0 : 1, duration: 0.3 });
},
});
// ---- DEBOUNCED RESIZE ----
function debounce(func) {
var timer;
return function (event) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => func(), 500, event);
};
}
window.addEventListener("resize", debounce(playMarquee));
};
document.addEventListener("DOMContentLoaded", initBot);
</script>


.webp)
