Draw Path on Scroll
Animates an SVG stroke from 0% to 100% as the user scrolls, using GSAP DrawSVGPlugin and ScrollTrigger. Supports separate desktop and mobile SVGs that swap via GSAP matchMedia, with the animation scrubbed directly to scroll position.
Setup — External Scripts
<script src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/DrawSVGPlugin.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/ScrollTrigger.min.js"></script>Code
<div data-draw-scroll-wrap>
<!-- Desktop SVG -->
<svg data-draw-scroll-desktop xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 1040 2430" fill="none">
<path data-draw-scroll-path d="M587.951 30.0063C718.546 287.255 506.581 892.058 187.126 635.947C-154.429 362.118 969.689 156.621 930.009 690.21C890.328 1223.8 117.306 1508.68 117.306 1203.2C117.306 927.361 1193.01 872.595 982.75 1411.21C863.692 1716.19 62.0549 2560.79 30.9135 1937.26C3.77072 1393.8 587.952 1603.14 587.951 2400.01" stroke="currentColor" stroke-width="60" stroke-linecap="round"></path>
</svg>
<!-- Mobile SVG (Optional) -->
<svg data-draw-scroll-mobile xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 448 2433" fill="none">
<path data-draw-scroll-path d="M261.417 30.502C326.48 118.169 402.716 356.502 187.155 608.502C-82.2964 923.502 204.717 1253.5 364.28 1379C523.844 1504.5 813.866 1858 146.008 2039.5C-521.85 2221 -136.489 1394.5 146.008 1768C428.506 2141.5 253.89 2356 233.318 2402.5" stroke="currentColor" stroke-width="61" stroke-linecap="round"></path>
</svg>
</div>function initDrawPathOnScroll() {
const mm = gsap.matchMedia();
const wrappers = document.querySelectorAll("[data-draw-scroll-wrap]");
mm.add(
{
isDesktop: "(min-width: 768px)",
isMobile: "(max-width: 767px)"
},
(context) => {
const { isDesktop, isMobile } = context.conditions;
wrappers.forEach((wrap) => {
if (wrap._drawTl) {
if (wrap._drawTl.scrollTrigger) {
wrap._drawTl.scrollTrigger.kill();
}
wrap._drawTl.kill();
wrap._drawTl = null;
}
const desktopSVG = wrap.querySelector("[data-draw-scroll-desktop]");
const mobileSVG = wrap.querySelector("[data-draw-scroll-mobile]");
let svgToUse = desktopSVG;
if (isMobile && mobileSVG) {
svgToUse = mobileSVG;
}
if (!svgToUse) return;
const path = svgToUse.querySelector("[data-draw-scroll-path]");
if (!path) return;
const tl = gsap.timeline({
defaults: {
ease: "linear"
},
scrollTrigger: {
trigger: wrap,
start: "clamp(top center)",
end: "clamp(bottom center)",
scrub: true,
invalidateOnRefresh: true
}
});
tl.fromTo(path,
{ drawSVG: 0 },
{ drawSVG: "100%", duration: 1 }
);
wrap._drawTl = tl;
});
ScrollTrigger.refresh();
return () => {
wrappers.forEach((wrap) => {
if (wrap._drawTl) {
if (wrap._drawTl.scrollTrigger) {
wrap._drawTl.scrollTrigger.kill();
}
wrap._drawTl.kill();
wrap._drawTl = null;
}
});
};
}
);
}
document.addEventListener("DOMContentLoaded", function () {
initDrawPathOnScroll();
});Guide
Overview
This setup animates an SVG path that draws itself as the user scrolls, with support for desktop-only or separate SVGs per breakpoint. It works by controlling stroke-dashoffset and stroke-dasharray — this means it only animates strokes, not fills.
Wrapper
Use [data-draw-scroll-wrap] to mark the section whose SVG path will animate on scroll, binding ScrollTrigger to this element.
Desktop SVG
Use [data-draw-scroll-desktop] to define the main SVG that contains a [data-draw-scroll-path] for drawing on screens 768px and above.
Mobile SVG (Optional)
Use [data-draw-scroll-mobile] to supply an alternate SVG for smaller screens. It automatically replaces the desktop version when the mobile breakpoint is active.
Path
Use [data-draw-scroll-path] to identify the <path> that should animate from 0% to 100% draw progress. Place this attribute inside either or both SVGs within the wrapper.
Scroll Triggering
By default, the animation starts when the wrapper's top reaches the viewport center and ends when the bottom reaches the center. Adjust start and end in the scrollTrigger config to change activation timing.
Breakpoints
When switching between desktop and mobile, the script kills and rebuilds the associated timeline so only one animation runs at a time.
Linear Progress
The path uses a linear ease to keep scroll distance perfectly synced to draw progress.
Smooth Scroll
This effect works best when paired with a smooth scrolling library. The live demo uses the Lenis Setup resource.