{"id":143,"date":"2026-10-13T06:28:55","date_gmt":"2026-10-13T06:28:55","guid":{"rendered":"https:\/\/webdevpuneet.com\/blog\/?p=143"},"modified":"2026-10-13T06:36:23","modified_gmt":"2026-10-13T06:36:23","slug":"gsap-tutorial-for-beginners-animate-your-first-html-element","status":"publish","type":"post","link":"https:\/\/webdevpuneet.com\/blog\/gsap-tutorial-for-beginners-animate-your-first-html-element\/","title":{"rendered":"GSAP Tutorial for Beginners: Animate Your First HTML Element"},"content":{"rendered":"<p><!--\n================================================================\n  POST TITLE (paste into the WordPress title field \u2014 it becomes the page H1)\n  GSAP Tutorial for Beginners: Animate Your First HTML Element\n\n  SEO TITLE (55 chars, target 50\u201360, max 60 \u2014 \"Optimize SEO\" panel \u2192 SEO TITLE)\n  GSAP Tutorial for Beginners: Animate Your First Element\n\n  SEO DESCRIPTION (152 chars, target 150\u2013160, max 160 \u2014 \"Optimize SEO\" panel \u2192 SEO DESCRIPTION)\n  Learn GSAP from scratch with 10 live demos: gsap.to, from and fromTo, easing, delay, timelines, stagger, repeat and yoyo, plus your first ScrollTrigger.\n\n  HOW TO PASTE: click the first line after this comment, press Ctrl+Shift+End, Ctrl+C.\n  In WordPress click \"Type \/ to choose a block\", then Ctrl+V.\n  Fallback: \u22ee menu \u2192 Code editor \u2192 paste.\n================================================================\n--><\/p>\n\n\n<p class=\"wp-block-paragraph\">GSAP (GreenSock Animation Platform) is the JavaScript animation library most professional sites actually reach for once plain CSS transitions stop being enough \u2014 smooth cross-browser easing, animations you can sequence and reverse on command, and a scroll-triggering system (ScrollTrigger) that would otherwise take real effort to build by hand. It&#8217;s also, refreshingly, not hard to get started with: the entire library boils down to a handful of core ideas, and once you&#8217;ve animated your first element you already understand most of what real GSAP code looks like. This tutorial builds those ideas up one at a time, from moving a single box to a scroll-triggered reveal, using nothing but the free CDN build \u2014 no install, no build step, no account.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Every demo below is <strong>live and interactive<\/strong> right here in the article \u2014 click the buttons, hover the card, scroll the last one. Each embed also has a <strong>Fork &amp; Edit<\/strong> button that opens the exact code, already running, in <a href=\"https:\/\/webdevpuneet.com\/ui-snippets\/mycode\/\" target=\"_blank\" rel=\"noopener\">My Code<\/a> \u2014 webdevpuneet&#8217;s free in-browser editor. Change a duration, swap an ease, and see what happens \u2014 that&#8217;s a much faster way to build real intuition for GSAP than reading about it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Once this post clicks, the <a href=\"https:\/\/webdevpuneet.com\/gsap-playground\/\" target=\"_blank\" rel=\"noopener\">GSAP Playground<\/a> is the natural next stop \u2014 a free, click-based, in-browser course that runs beginner to pro across 55 lessons in 18 chapters, covering everything from <code>gsap.to()<\/code> through timelines, stagger, ScrollTrigger, and the official plugins, each with a live editor and instant preview.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why reach for GSAP instead of plain CSS?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">CSS transitions and <code>@keyframes<\/code> genuinely handle a lot of simple cases fine, and this tutorial isn&#8217;t telling you to abandon them. GSAP earns its place once an animation needs to be controlled from JavaScript rather than just declared once and left alone: playing, pausing, reversing, or restarting on demand; chaining several animations into one precise sequence without a pile of <code>animation-delay<\/code> values; animating properties CSS can&#8217;t touch directly, like a number counting up or a value along an SVG path; or triggering smoothly off scroll position instead of just a class toggle. It also quietly papers over years of small cross-browser inconsistencies in how transforms and easing behave, so an animation that looks right in one browser looks right everywhere. Every demo below is deliberately simple, but each one demonstrates a real, load-bearing piece of how GSAP code is actually written.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">1. gsap.to() \u2014 your first tween<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The single most-used line in all of GSAP. It animates an element <em>toward<\/em> the values you give it, starting from wherever it already is right now.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/11\/1.html\" style=\"width:100%;height:420px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"gsap.to Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>gsap.to('.box', {\n  x: 160,\n  rotation: 360,\n  backgroundColor: '#16a34a',\n  duration: 1.2,\n  ease: 'power2.out'\n});<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A &#8220;tween&#8221; (short for &#8220;in-betweening&#8221;) is GSAP&#8217;s core unit of animation: one call, one target, one set of end values, one duration. Note <code>x<\/code> and <code>rotation<\/code> rather than raw CSS <code>transform<\/code> strings \u2014 GSAP quietly handles combining transforms under the hood, which is genuinely fiddly to get right by hand, especially once translation and rotation need to animate at the same time. <code>ease<\/code> shapes the rate of change over that duration; without one specified, GSAP defaults to a gentle <code>power1.out<\/code> rather than the linear motion most people expect. <code>gsap.to()<\/code> can animate almost any numeric CSS property, plus a handful of GSAP-specific shorthand properties like <code>x<\/code>\/<code>y<\/code> for transforms.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> the vast majority of one-off animations \u2014 moving, resizing, fading, or recoloring something in response to code running. <strong>Tip:<\/strong> nothing here needs a build step or npm install \u2014 the single CDN script tag in this demo&#8217;s CDN panel is the entire dependency.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2. gsap.from() \u2014 animate from a state<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The mirror image of <code>gsap.to()<\/code>: instead of animating toward values you give it, it animates <em>from<\/em> them back to the element&#8217;s real, current CSS state.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/11\/2.html\" style=\"width:100%;height:400px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"gsap.from Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>gsap.from('.card', {\n  opacity: 0,\n  y: -40,\n  scale: 0.8,\n  duration: 0.8,\n  ease: 'back.out(1.7)'\n});<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This is the technique behind almost every &#8220;entrance animation&#8221; you&#8217;ve seen on a real website: the card&#8217;s actual layout position never changes at all \u2014 only the starting point of its animation does. GSAP briefly sets the element to the values you provided, then animates it forward to whatever its normal CSS already says, so you never have to hand-write the destination values yourself. That also means a <code>gsap.from()<\/code> tween is instantly reversible into a <code>gsap.to()<\/code> in your head: the numbers you&#8217;d write are simply the numbers you already wrote here.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> entrance animations \u2014 a card, modal, or section fading and sliding into its normal resting position the moment it appears. <strong>Tip:<\/strong> because the destination is &#8220;whatever the CSS already says,&#8221; redesigning the element&#8217;s final look later needs zero changes to this tween \u2014 only the CSS.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3. gsap.fromTo() \u2014 set both ends explicitly<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The most explicit of the three: you specify the starting values and the ending values yourself, so the animation looks identical no matter what state the element happened to be in beforehand.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/11\/3.html\" style=\"width:100%;height:380px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"gsap.fromTo Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>gsap.fromTo('.dot',\n  { x: 0, opacity: 0.3, scale: 1 },\n  { x: 260, opacity: 1, scale: 1.15, duration: 1, ease: 'power1.inOut' }\n);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>gsap.to()<\/code> depends on wherever the element already is; <code>gsap.from()<\/code> depends on whatever its final CSS already says. <code>gsap.fromTo()<\/code> depends on neither \u2014 both objects are spelled out, so replaying the animation (as this demo does, via <code>yoyo<\/code> and <code>repeat<\/code>) always starts from the exact same place every time, which matters a lot once an animation needs to run more than once from a predictable state.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> any animation that needs to replay identically \u2014 a toggle switch, a progress indicator, or anything triggered repeatedly by user interaction rather than played once on load. <strong>Tip:<\/strong> if an animation ever looks &#8220;off&#8221; the second time it plays but fine the first time, that&#8217;s usually a sign it should be a <code>fromTo()<\/code> instead of a <code>to()<\/code> or <code>from()<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">4. Easing changes everything<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Same distance, same duration, four completely different feelings. Easing is where GSAP animations stop looking like a spreadsheet and start looking like real motion.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/11\/4.html\" style=\"width:100%;height:420px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"GSAP Easing Comparison Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>gsap.to('.dot', { x: 260, duration: 1.4, ease: 'power2.out' });\n\/\/ also try: 'none', 'back.out(2)', 'bounce.out', 'elastic.out(1, 0.3)'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>ease<\/code> is a plain string naming one of GSAP&#8217;s built-in easing functions, and the differences are dramatic: <code>none<\/code> (linear) moves at a perfectly constant speed and reads as mechanical; a <code>power<\/code> family ease (<code>power1<\/code> through <code>power4<\/code>, each with <code>.in<\/code>\/<code>.out<\/code>\/<code>.inOut<\/code> variants) accelerates or decelerates smoothly, which is what almost all natural-feeling UI motion actually uses; <code>back<\/code> overshoots past the target and springs back; <code>bounce<\/code> literally bounces like a dropped ball. Getting comfortable naming a handful of these by ear is one of the highest-value things to learn early \u2014 the exact same tween code reads as completely different quality of motion depending only on this one string.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> tuning any animation&#8217;s personality without touching its duration, distance, or target values. <strong>Tip:<\/strong> <code>power2.out<\/code> or <code>power3.out<\/code> is a safe, professional-feeling default for most UI motion \u2014 reach for <code>back<\/code> or <code>elastic<\/code> deliberately for playful moments, not by default everywhere.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">5. duration &amp; delay<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Two properties that are easy to mix up: <code>duration<\/code> controls how long a tween takes; <code>delay<\/code> controls how long it waits before starting at all.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/11\/5.html\" style=\"width:100%;height:400px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"GSAP duration and delay Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>gsap.to('.box', { y: 0, opacity: 1, duration: 0.6, delay: 0.4 });<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">All three boxes in this demo take the same 0.6s <em>duration<\/em> to complete once they start \u2014 what <code>delay<\/code> changes is purely when that clock starts ticking, measured in seconds from the moment the tween is created (or, inside a timeline, from a point you control, as demo #6 shows next). This distinction matters most once several elements need to appear in a deliberate sequence rather than all at once: getting that sequence right by hand means carefully calculating a different <code>delay<\/code> for every single element, which is exactly the tedious math the next two techniques exist to eliminate.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> a small, fixed number of elements that need to start at specific, deliberately chosen moments. <strong>Tip:<\/strong> once you&#8217;re hand-calculating more than two or three different delay values for similar elements, that&#8217;s the signal to reach for <code>stagger<\/code> (demo #7) instead.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">6. gsap.timeline() \u2014 chaining tweens<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A timeline is a container that plays multiple tweens in order automatically \u2014 no delay math required at all.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/11\/6.html\" style=\"width:100%;height:420px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"gsap.timeline Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const tl = gsap.timeline();\ntl.to('.box', { x: 120, duration: 0.6 })\n  .to('.box', { y: 60, duration: 0.6 })\n  .to('.box', { rotation: 180, duration: 0.6 });<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Chaining <code>.to()<\/code> calls onto a timeline (instead of calling <code>gsap.to()<\/code> directly, three separate times) means each step automatically starts exactly when the previous one finishes \u2014 add a sixth step, or reorder two of them, and nothing else needs recalculating. This is the single biggest reason real GSAP code reaches for timelines almost immediately: any animation with more than one meaningful step benefits from one, and a timeline itself can be paused, reversed, or replayed as a single unit, exactly like an individual tween can.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> any multi-step animation \u2014 an intro sequence, a multi-part hover effect, a step-by-step onboarding animation. <strong>Tip:<\/strong> GSAP timelines support position parameters (like <code>\"-=0.2\"<\/code> as a second argument to <code>.to()<\/code>) for overlapping steps slightly instead of always waiting for a full, clean handoff \u2014 worth exploring once the basic chain above feels comfortable.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">7. stagger \u2014 animating a group<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">One call, ten targets, one <code>stagger<\/code> value \u2014 the fix for the &#8220;hand-calculating delays&#8221; problem demo #5 raised.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/11\/7.html\" style=\"width:100%;height:400px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"GSAP Stagger Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>gsap.from('.col', {\n  scaleY: 0,\n  duration: 0.5,\n  stagger: 0.07\n});<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Pass a CSS selector matching multiple elements to any tween method, add a <code>stagger<\/code> value, and GSAP automatically offsets each target&#8217;s individual start time by that many seconds \u2014 the first bar starts immediately, the second 0.07s later, the third 0.07s after that, and so on, with zero loop or per-element math required. <code>stagger<\/code> also accepts an options object for more control (staggering from the center outward, or from a specific index), but the plain number shown here covers the large majority of real use cases: a list of cards, a grid of icons, or a row of chart bars all revealing themselves in a natural, cascading sequence instead of popping in simultaneously.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> revealing a list, grid, or row of similar items one after another instead of all at once. <strong>Tip:<\/strong> a stagger between roughly <code>0.05<\/code> and <code>0.1<\/code> seconds reads as a natural cascade for most UI lists \u2014 much larger than that starts to feel slow and deliberate rather than snappy.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">8. repeat &amp; yoyo \u2014 looping<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Two properties for animations that don&#8217;t just play once: <code>repeat<\/code> controls how many extra times a tween plays, and <code>yoyo<\/code> controls whether it reverses each time instead of snapping back to the start.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/11\/8.html\" style=\"width:100%;height:440px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"GSAP repeat and yoyo Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>gsap.to('.pulse', {\n  scale: 1.5,\n  duration: 0.6,\n  repeat: -1,\n  yoyo: true\n});<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>repeat: -1<\/code> is GSAP&#8217;s convention for &#8220;loop forever&#8221; \u2014 a plain positive number instead (like <code>repeat: 3<\/code>) plays that many additional times and then stops. Without <code>yoyo: true<\/code>, each repeat snaps instantly back to the tween&#8217;s starting values before playing forward again, which usually reads as a visible jump; with it, the tween plays forward, then genuinely reverses back to where it started, then forward again, producing the smooth back-and-forth &#8220;breathing&#8221; pulse this demo shows. This combination is exactly how most looping attention-getters \u2014 a pulsing notification dot, a subtly breathing call-to-action button \u2014 are actually built.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> pulsing indicators, breathing buttons, and any subtle looping attention cue. <strong>Tip:<\/strong> always give yourself a way to stop or kill a <code>repeat: -1<\/code> tween (this demo&#8217;s Stop button calls <code>.kill()<\/code>) \u2014 an infinite loop with no way to end it is very easy to accidentally leave running in real code.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">9. Animating on a real event<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Everything above ran on a button click for the sake of a clean demo. This is how GSAP actually ships in real interfaces \u2014 tied to a genuine user interaction, not autoplay.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/11\/9.html\" style=\"width:100%;height:400px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"GSAP Hover Animation Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>card.addEventListener('mouseenter', function () {\n  gsap.to(card, { y: -8, duration: 0.3, ease: 'power2.out' });\n});\ncard.addEventListener('mouseleave', function () {\n  gsap.to(card, { y: 0, duration: 0.3 });\n});<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">There&#8217;s nothing GSAP-specific about the event listeners themselves \u2014 <code>mouseenter<\/code> and <code>mouseleave<\/code> are plain DOM events, exactly as they&#8217;d be used with any other JavaScript. The only GSAP-specific part is what happens inside each handler: a fresh <code>gsap.to()<\/code> call with the values for that state. Calling <code>gsap.to()<\/code> again mid-animation (say, if the cursor leaves before the hover animation finished) doesn&#8217;t cause a jarring jump \u2014 the new tween starts from the element&#8217;s current values, not the interrupted animation&#8217;s original starting point, and because it renders after the older tween it wins on every frame. GSAP 3 doesn&#8217;t kill the older tween by default (<code>overwrite<\/code> is <code>false<\/code>); add <code>overwrite: 'auto'<\/code> if you want the conflicting parts of the old tween removed outright, which is cleaner on busy hover effects.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> hover states, focus states, click feedback \u2014 any animation that should respond to something the visitor actually does. <strong>Tip:<\/strong> this &#8220;continue from wherever it is&#8221; behavior is a big part of why GSAP hover effects feel more polished than naive manual class-toggling under fast mouse movement \u2014 and <code>overwrite: 'auto'<\/code> keeps it tidy when the cursor flicks in and out quickly.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">10. ScrollTrigger \u2014 a first taste<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The exact same <code>gsap.from()<\/code> from demo #2, just handed a <code>scrollTrigger<\/code> option instead of firing immediately or on click.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/11\/10.html\" style=\"width:100%;height:400px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"GSAP ScrollTrigger First Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>gsap.registerPlugin(ScrollTrigger);\n\ngsap.from('.reveal', {\n  opacity: 0,\n  y: 50,\n  duration: 0.8,\n  scrollTrigger: {\n    trigger: '.reveal',\n    start: 'top 80%'\n  }\n});<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">ScrollTrigger is an official GSAP plugin (loaded from its own CDN script, separate from core GSAP) that needs one line of setup \u2014 <code>gsap.registerPlugin(ScrollTrigger)<\/code> \u2014 before it can be used. Once registered, any tween can take a <code>scrollTrigger<\/code> object instead of playing right away: <code>trigger<\/code> names the element to watch, and <code>start: 'top 80%'<\/code> means &#8220;fire when this element&#8217;s top edge reaches 80% down the viewport.&#8221; This barely scratches the surface of what ScrollTrigger can do \u2014 scroll-scrubbed animations, pinned sections, and full scroll-driven storytelling all build on this exact same foundation, and a whole separate post on this blog digs much deeper into it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> the simplest, most common scroll-triggered use case \u2014 an element fading and sliding in the moment it enters view. <strong>Tip:<\/strong> for a full deep dive into scroll-triggered animation \u2014 scrubbing, pinning, thresholds, and several more real patterns \u2014 look for &#8220;GSAP ScrollTrigger Tutorial: Animate Elements on Scroll&#8221; in the <a href=\"https:\/\/webdevpuneet.com\/blog\/category\/javascript\/\" target=\"_blank\" rel=\"noopener\">JavaScript<\/a> category on this blog. For the same &#8220;is it in view yet&#8221; idea without any library, &#8220;How to Detect When an Element Enters the Viewport Without a Library&#8221; covers it from the browser API side.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Common pitfalls<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Reaching for <code>gsap.to()<\/code> when the goal is really an entrance animation.<\/strong> <code>gsap.to()<\/code> needs you to know and hand-write the destination values; <code>gsap.from()<\/code> (demo #2) already knows them \u2014 they&#8217;re just the element&#8217;s existing CSS.<\/li>\n\n\n\n<li><strong>Hand-calculating a different <code>delay<\/code> for every item in a list.<\/strong> That&#8217;s exactly the tedious math <code>stagger<\/code> (demo #7) exists to replace \u2014 one property instead of N carefully-computed numbers.<\/li>\n\n\n\n<li><strong>Chaining animations with manual <code>setTimeout()<\/code> calls instead of a timeline.<\/strong> A <code>gsap.timeline()<\/code> (demo #6) sequences steps automatically and lets the whole sequence be paused, reversed, or replayed as one unit \u2014 a pile of timeouts can do none of that.<\/li>\n\n\n\n<li><strong>Starting a <code>repeat: -1<\/code> tween with no way to stop it.<\/strong> An infinite loop needs a reference to the tween (or timeline) so <code>.kill()<\/code> or <code>.pause()<\/code> can actually be called on it later, exactly as demo #8&#8217;s Stop button does.<\/li>\n\n\n\n<li><strong>Forgetting <code>gsap.registerPlugin(ScrollTrigger)<\/code> before using scrollTrigger.<\/strong> ScrollTrigger ships as a separate script from core GSAP and has to be explicitly registered once, or any <code>scrollTrigger<\/code> option on a tween is silently ignored.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Frequently asked questions<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Do I need to install GSAP with npm to follow this tutorial?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No \u2014 every demo above loads GSAP from a CDN with a single <code>&lt;script&gt;<\/code> tag, visible in each demo&#8217;s CDN panel. npm installation matters once a project has a real build step (React, Vue, a bundler), but nothing here requires one.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Is GSAP free to use?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Yes. Since GSAP 3.13 (April 2025, after Webflow acquired GreenSock), the whole library \u2014 every plugin, ScrollTrigger included \u2014 is free for all use, commercial included, under the standard GSAP license. The old paid &#8220;Club GreenSock&#8221; tier for the bonus plugins is gone.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What&#8217;s the difference between gsap.to(), gsap.from(), and gsap.fromTo()?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><code>gsap.to()<\/code> animates from the element&#8217;s current state to values you specify. <code>gsap.from()<\/code> animates from values you specify back to the element&#8217;s current (real) CSS state. <code>gsap.fromTo()<\/code> specifies both ends explicitly, depending on neither the element&#8217;s current state nor its final CSS \u2014 see demos #1, #2, and #3 for a side-by-side comparison of exactly this.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Why does my animation snap back to normal instead of staying at its end state?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">This usually means the animated CSS property was also set with a real stylesheet rule that keeps re-applying after the tween&#8217;s inline styles are considered, or an unrelated re-render reset the element&#8217;s markup. Double-check nothing else in the page is setting the same property, and confirm the tween isn&#8217;t accidentally being killed or overwritten by another tween targeting the same element right after it.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Can GSAP animate things CSS transitions can&#8217;t?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Yes \u2014 GSAP can animate along an SVG path, tween a plain JavaScript number (useful for a count-up effect), sequence dozens of steps precisely with a timeline, and hook into scroll position via ScrollTrigger, none of which plain CSS transitions or <code>@keyframes<\/code> can do on their own.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Every demo above ships its full HTML, CSS and JS in the <strong>HTML \/ CSS \/ JS<\/strong> tabs in its own top bar. The fastest way to actually lock this in is to click <strong>Fork &amp; Edit<\/strong> on demo #6, add a fourth <code>.to()<\/code> step to the timeline, and watch it slot into the sequence with zero delay math of your own. Once you&#8217;ve got a version you like, save it to <a href=\"https:\/\/webdevpuneet.com\/ui-snippets\/mycode\/\" target=\"_blank\" rel=\"noopener\">My Code<\/a> and it&#8217;s yours to keep, tweak further, or drop straight into a real project.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Recommended next step:<\/strong> open the <a href=\"https:\/\/webdevpuneet.com\/gsap-playground\/\" target=\"_blank\" rel=\"noopener\">GSAP Playground<\/a> and work through its early chapters on tweens and timelines \u2014 free, click-based lessons with a live editor, covering exactly the ground this post just introduced, one guided step at a time. For more tutorials like this one, browse the <a href=\"https:\/\/webdevpuneet.com\/blog\/category\/gsap\/\" target=\"_blank\" rel=\"noopener\">GSAP<\/a> category on the blog.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>GSAP (GreenSock Animation Platform) is the JavaScript animation library most professional sites actually reach for once plain CSS transitions stop being enough \u2014 smooth cross-browser easing, animations you can sequence and reverse on command, and a scroll-triggering system (ScrollTrigger) that would otherwise take real effort to build by hand. It&#8217;s also, refreshingly, not hard to [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":144,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_et_pb_use_builder":"","_et_pb_old_content":"","_et_gb_content_width":"","advanced_seo_description":"Learn GSAP from scratch with 10 live demos: gsap.to, from and fromTo, easing, delay, timelines, stagger, repeat and yoyo, plus your first ScrollTrigger.","jetpack_seo_html_title":"GSAP Tutorial for Beginners: Animate Your First Element","jetpack_seo_noindex":false,"jetpack_seo_schema_type":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"Want to learn GSAP animations from scratch?\n\nThis beginner-friendly tutorial covers 10 live demos to help you master web animations, including:\n\n* gsap.to(), gsap.from() & gsap.fromTo()\n* Easing and animation delays\n* Timelines for sequencing animations\n* Stagger animations\n* Repeat and yoyo effects\n* ScrollTrigger basics\n\nLearn how to bring your HTML elements to life with smooth, professional animations using JavaScript.\n\nRead the tutorial and try the live demos:\n\n#GSAP #JavaScript #WebDevelopment #CSSAnimations #FrontendDevelopment\n","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[15,9],"tags":[],"class_list":["post-143","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-gsap","category-tutorials","et-has-post-format-content","et_post_format-et-post-format-standard"],"jetpack_publicize_connections":[],"jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/phrh5V-2j","jetpack_featured_media_url":"https:\/\/i0.wp.com\/webdevpuneet.com\/blog\/wp-content\/uploads\/2026\/10\/gsap-tutorial-header-1200x630-1.jpg?fit=1200%2C630&ssl=1","_links":{"self":[{"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/posts\/143","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/comments?post=143"}],"version-history":[{"count":3,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/posts\/143\/revisions"}],"predecessor-version":[{"id":147,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/posts\/143\/revisions\/147"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/media\/144"}],"wp:attachment":[{"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/media?parent=143"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/categories?post=143"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/tags?post=143"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}