Jump to content


Animating SVG Gradients Dynamically Using GSAP

Recommended Posts

My goal: have an SVG (fill: gradient) animated to change into a different gradient fill. All this should be done dynamically, using GSAP v3 (maybe using gsap.fromTo()?).


I can create the SVG dynamically. I can also create other elements like the <defs> and <stop> to go inside the gradient elements.


At this point, I can have the SVG display the 1st gradient dynamically. And I can animate other properties (like size, speed, or location). What I cannot do, however, is animate the actual gradient fill change using GSAP. How do you do that?


There are a lot of examples from others online, but what I've found only includes:

- a solid color changing to another solid color, and back

- a gradient changing to a solid color, and back

- a gradient changing to another gradient, and back –> but does not use SVGs and uses CSS gradients instead

- a gradient changing to another gradient, and back –> but uses GreenSock v2 with the stagger function (which, apparently, doesn't exist with GSAP v3)

Link to comment
Share on other sites

It's pretty tough to troubleshoot without a minimal demo - the issue could be caused by CSS, markup, a third party library, your browser, an external script that's totally unrelated to GSAP, etc. Would you please provide a very simple CodePen or CodeSandbox that demonstrates the issue? 


Please don't include your whole project. Just some colored <div> elements and the GSAP code is best (avoid frameworks if possible). See if you can recreate the issue with as few dependancies as possible. If not, incrementally add code bit by bit until it breaks. Usually people solve their own issues during this process! If not, then at least we have a reduced test case which greatly increases your chances of getting a relevant answer.


Here's a starter CodePen that loads all the plugins. Just click "fork" at the bottom right and make your minimal demo

See the Pen aYYOdN by GreenSock (@GreenSock) on CodePen


If you're using something like React/Next/Nuxt/Gatsby or some other framework, you may find CodeSandbox easier to use. 


Once we see an isolated demo, we'll do our best to jump in and help with your GSAP-specific questions. 

Link to comment
Share on other sites

If you have below example CodePen then share that if it uses older GSAP code it will not be difficult to change it for GSAP 3. 

a gradient changing to another gradient, and back –> but uses GreenSock v2 with the stagger function (which, apparently, doesn't exist with GSAP v3)



Link to comment
Share on other sites

Here are some primary examples:


1. This

See the Pen MWwyJLe by malnurtured (@malnurtured) on CodePen

uses CSS gradients. Although the 3 samples claim to be browser-dependent, it doesn't seem to be the case for any of my browsers.


2. This

See the Pen OVWLOd?editors=0010 by dfranklinau (@dfranklinau) on CodePen

creates SVG gradients dynamically, but then stays static.


3. This

See the Pen bMxqYR by PointC (@PointC) on CodePen

does use GSAP v3 but only transform gradient into a solid color.


4. This

See the Pen MwbRoL by MAW (@MAW) on CodePen

uses SVG gradients, but only GSAP v2 (TimelineMax).


5. This

See the Pen WNoxoLP by garethj (@garethj) on CodePen

is pretty weird. Its syntax suggests it's staggering from one gradient to another, but it's only going from one solid color to another – this may give off the effect of gradients in motion, but they're not true gradients.


6. This

See the Pen ZWKmrK by jonathan (@jonathan) on CodePen

uses SVG gradients, but only GSAP v2 (TimelineMax)


7. This

See the Pen 56dc377001d36c64c154cc3a120098cb?editors=1111 by GreenSock (@GreenSock) on CodePen

uses CSS gradients and GSAP v2 (TweenMax).


8. This

See the Pen LYGXrMq by DickeySingh (@DickeySingh) on CodePen

uses GSAP v3, but uses CSS gradients.


9. This

See the Pen DgyRbB by rhernando (@rhernando) on CodePen

uses TweenLite and Raphel.js.


10. This

See the Pen XdBvEB by PointC (@PointC) on CodePen

is good, but still uses TimelineMax.


Essentially, I'm trying to produce a combined version of #8 and #10 (morphing between random shapes and applying gradient changes simultaneously). Creating the elements dynamically and appending them to one another is easy enough. How do I set up gsap.timeline(), or gsap.fromTo(), or MorphSVG to accommodate them?





Link to comment
Share on other sites

You can target any or all of the stops in your gradient. Here's a fork of my demo you posted above. Instead of a tweening the gradient to a solid fill, I've now tweened it to a new gradient. Happy tweening.


See the Pen RwJWwQQ by PointC (@PointC) on CodePen

  • Like 7
Link to comment
Share on other sites

Thank you, @PointC. Your demo was very helpful. I still have some questions regarding the syntax of gsap.timeline. Here is a sample


See the Pen VwdvmMY?editors=1010 by tcwadr (@tcwadr) on CodePen



1. In your code, you write .to("rect", {attr:{stroke:"white"}}, 0);, I deleted attr and wrote it as .to("rect", { stroke:"white"}, 0);. It ended up working exactly the same.

Can you explain what the purpose of attr is? What would be a situation where one would be required to include attr?


EDIT: I double checked and placed attr back in afterwards. Everything worked pretty much the same except for the rotate attribute, which no longer rotates the SVG. I checked the documentation and see nothing beyond the few sentences there. How does one rotate with attr? Is there better documentation available for this plugin?


2. In one of the to()s, I initially used a parameter named stroke-width (copied from the <rect> element), but that caused an error. I then made a guess and turns out the correct word is strokeWidth – it was a good guess, but a guess nonetheless.

Is there a list of attributes where one may find accurate available attribute names?


3. In HTML, the <rect> element's fill is defined as "url(#someID)", referring to the ID of the gradient element. In JavaScript, if I define the #rect's fill attribute as "url(#someID)", the fill color just stays white; or if I define the fill attribute as "#someID", nothing happens.

Is there a convenient way to define to() and from() using fill (or some other attribute, like background) that can encompass the entire gradient at once? Or am I forever stuck with having to minutely change each stopColor attribute at a time?


4. Related to question 3: Because you can't just use "url(#someID)" as a fill in JavaScript, you seem to be stuck with the same gradient structure as you started with.

How do I, for example, (1) animate from a radial to a linear gradient, and back? And (2) what if it's the same structure (linear/radial), but I just want to add additional colors or remove some colors entirely from the lineup?

Link to comment
Share on other sites

1. attr:{} is the attribute plugin. I'm more in the habit of specifically targeting attributes that way, but in this case, yes - simply using stroke without that wrapper will work just fine. Sometimes a property and an attribute may seem the same but they aren't. A rectangle for example has an x attribute. These 2 lines may appear the same but they are not. One will translate x:100 while the other targets the x attribute.

.to("rect", {x:100})
.to("rect", {attr:{x:100}})

If you specifically want to target an attribute, use the attr:{} wrapper.


2. You'll generally switch hyphenated properties to camelCase. So stroke-width becomes strokeWidth. If that's not to your liking you can also use the hyphenated version, but you'll need to use quotes. These two lines are effectively the same.

.to("rect", {  "stroke-width": 40  })
.to("rect", {  strokeWidth: 40  });

3. You can use GSAP to set a gradient fill with no problem. That will work with or without the attr:{} wrapper.

// add a fill this way
gsap.set("rect", {attr:{fill:"url(#someGradient)"}})
// inspect the element and you'll find this

// add a fill this way
gsap.set("rect", {fill:"url(#someGradient)"})
// inspect the element and you'll find this
style="fill: url("#someGradient");"

4. I don't know that you could smoothly interpolate between a linear and radial gradient. A crossfade would probably be a better way to go.

You can dynamically add stops with JS by creating the element and adding offset/color attributes, but since the gradient isn't rendered, I don't think it would refresh the changes.


If you want to go from a couple stops to multiple stops, I'd probably add all the stops at the beginning and just set the color and offset the same on some of them so it will appear to only have two. You can then loop though and target any or all of the stops offsets/colors .


I'd also add that animating gradients is pretty intensive so my recommendation would be to use it sparingly.


Good luck.

  • Like 5
Link to comment
Share on other sites

Thank you for the explanations, @PointC. I really appreciate it. 🙏

  • Like 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.