Storyline360, Animation and GSAP ( Greensock )

Oct 23, 2020


Trying to figure something out in SL360 i stumbled upon  the ds-bootstrap.min.js file thats added to the lib/scripts folder when publishing a Storyline360 file. In that file are GSAP(Greensock) references... some samples..
* VERSION: 1.11.8
* DATE: 2014-05-13
* @license Copyright (c) 2008-2014, GreenSock. All rights reserved.
* This work is subject to the terms at or for
* Club GreenSock members, the software agreement that was issued with your membership.
* @author: Jack Doyle,

What i can find in that file that both GSAP CSS-plugin, easing and basic GSAP tweens are somehow used in Storyline.
Bad news however is that its a really old version they have implemented... from 2014... version 1.11.8, whereas the current GSAP version is version 3.5.1 !!!

Big advantages of the newer GSAP are mobile performance, speed and perfect control over SVG. So Storyline would certainly benefit from updating their internal used Storyline to the latest version.

As Storyline probably uses GSAP for all its animation features, the transitions and everything, i personally would really like it to have the control of GSAP in the hands of the frontend-user. I do use GSAP constantly in my elearning projects, but always have to add the newest version..

Is there an option to get the latest version of GSAP implemented in Storyline?
And can we have some better scripted control over Storyline you do use it already anyway?

Kind regards,
Math Notermans


63 Replies
Math Notermans

As said in this post i found evidence of Storyline360 using Greensock's library. So i wanted to figure out what is possible with it without the need of adding the newer libraries of GSAP 3.

Well as you can see in the attached SL360 file Articulate uses TweenLite version 1.11.8, on publishing it shows that in the console.

So right of the bat, without any extra code libraries you can use TweenLite code like shown in the sample.

Because Articulate removed JQuery from its common scripts, we cannot use JQuery selectors without adding extra libraries. So selecting the element on stage is selected by using a document.querySelectorAll for a given 'acc-text', and then moved by TweenLite.

So using code like this.. 

var hammerElements = document.querySelectorAll("[data-acc-text='hammer01']")[0], 1,{x:"+=25"});

You can animate anything with code in a default Storyline.

Alas TweenMax and GSAP plugins are not available default...

Kind regards,


Thanks @Math Notermans for sharing your examples using Storylines included GSAP library. This can be very useful for building our more interactive animations!

I've used GSAP to develop Flash objects in the past, and I'm excited to see that it is possible (though not officially supported) to use GSAP with Storyline.

While playing around with your first example (BuiltIn-TweenLite.story) I noticed that the location of the 'Judge-hammer.png' image moves around if you re-size your browser window after interacting with the activity.

I am building something that uses several animations on a slide and didn't want them to move around on the stage if the user suddenly decides to resize the browser window. I decided to built off of your example and target the 'transform' CSS rules for the hammer image ( actually changes the matrix of an item instead of the existing transform properties) so that they remain in line with their original location on the slide. I did have to add quite a bit of JavaScript to accomplish this, making it more complex than I originally wanted it to be, but it works fine for my purpose so I thought I would share.

I changed quite a few things, and added a few more triggers, so it might be too complex to easily reverse engineer, but it might act as an example of what can be done with JavaScript and TweenLite. For anybody reading this, my example uses specific ID's and Class names that exist in the current version of Storyline, but may not in the future. If this changes, then the file I'm sharing today would no longer work.

A more simple solution for using TweenLite in Storyline might be to just use the classic player as @Math Notermans did in his second example (Multipoint-Animation-TweenLite.story) so that you don't have to worry about device or browser window size affecting how the GSAP TweenLite animations behave. The classic player size is not responsive, so things stay inline in proportion to where they started.

Math Notermans

Nice T.P, will check this out tomorrow. This post on Gsap and TweenLite somewhat polder anyway, if you check some newer posts from me jou find solutions with TweenLite that maintain perfect position. Found quite some differences in the Modern player vs the Classic player and know how to handle that. Gladly share knowledge and tips and trucks on Gsap , Storyline and animation. Even managed to Morph  svg shapes inside Storyline with MorphSvg 

Math Notermans

Here one later sample T.P in which  i have a check to see whether the Classic or Modern player is used on based on that uses a calculation that gets the proper x position. Your method of using onUpdate and CSS Transform and the idea of giving an element ( or elements ) a specific ID at start i do like. Especially the idea of giving the element a specific ID i find usefull, cause that i often find cumbersome when working with JS in Storyline.

I do have a function that gets all elements in a scene in Storyline.. might be good to add this to it, so at start everything has a proper ID.

As said i got a setup working now that checks whether you publish in Classic Player or Modern Player mode. Based on that getting the x/y position is somewhat different for the player.

Check that here...

Modern Player version


Classic Player version

Clicking the 'move to spots' button triggers a animation that moves the candy to the endposition of the 3 spots. You can drag the green spots around to change the positions. When clicking the button the candy moves to the correct spot and the variables are updated.

The biggest issue, especially because the Classic and Modern player behave somewhat differently, was the x/y translation of the player ( Storyline slide ) on a webpage of a given size.
Figured that out and now on any device ( testing needed for that ;-) the x/y position of elements should be good. To get and set for animation.


Hey Math, sorry for the late response. I saw your reply as soon as you made it but was too busy to dig into it until today. Thank you so much for sharing this! The .story file you shared is very useful. I will definitely be referencing it in future projects. The example you made is great. I've checked your other posts and see a variety of very interesting examples you put together. Also, thanks for letting us know that GSAP 3.5.1 is now the default in Storyline 360. This is great news!

The one issue this example doesn't seem to solve is the browser resize issue. If a GSAP animation finishes, and the browser is later resized, The animated object will no longer start at its original location on the slide. In the example you posted with the stick of candy (obj01.png). If you resize the browser, the stick of candy no longer starts from either its original location, or its last position in the animation. For some reason, GSAP seems to automatically animate off of it's previous translateX() and translateY() values to perform animations instead of the new values that Storyline assigns to it when the browser is resized. At least thats what it looks like to me. This is why I used the onUpdate: and translate3d(" +  method on the example that I had posted in my previous comment. It seems to fix this so that the item always animates from the correct position in relation to where it should be the slide, even after a browser resize.

If the starting location of the animated object doesn't matter much, as in your example then it's fine to use the default function without using onUpdate:. In my case I'm creating an animation where distance and location need to be exact as it is demonstrating a measurement. I don't want it to display incorrectly in the case of a browser resize.

After typing this out, it occurs to be that I there are probably other ways to accomplishing this, such as placing both beginning and end shapes inside of Storyline, then using gsap.set place an item to its start position and to animate it to it's end position. I think that's what I'll try next and see if that holds up after a browser resize.

Once again I just wanted to thank you for your responses and for sharing with the community! It is definitely appreciated.


Just an update. The gsap.set function turned out to be exactly what I was looking for. What I do now have 2 images. one on the slide and one off of the slide. When the user clicks a button, the one on the slide gets hidden, and I use its position to place the one that was off of the slide. This way the position is always correct regardless of how the slide is resized. 

I used your method to accurately gather the position of objects on the slide. So far it works great! 

Edit: Changed to gsap.set as that is what I meant to type

zhongyu wu

yeah... I am having a hard time locating the source of the element...the alternative way I am thinking to destroy the current image and add a new image on the placeholder of the destroyed image. I wanted to dynamically change the images so it can save a lot of time when creating a course without copy and paste all the images over to a similar course. 

Math Notermans

i basically have a function that gets everything from Storyline on start.

getElementsFromStoryline( );

The function gets several preset values that cannot be get by selectors. Most of them are always the same in Storyline. Shapes, buttons, images... As all images are treated as SVG in Storyline and thus have a 'path' element all of them are put in a shapesCollection ( is a WebCollection ).

That i loop get the 'data-text-attribute' and detect the type, text or image.. and treat them differently.

var elText = el.getAttribute('data-acc-text');

When i detect its a image, i get the source's name and can replace it if i want.

if(elText.indexOf(".png")!=-1 || elText.indexOf(".jpg")!=-1 )// if the source is png or jpg...
console.log("imagesArray: "+i+" / url: "+el.getElementsByTagName('image')[0].getAttribute('xlink:href'));

As said its tough...but it works. In my projects i have all my assets... images, shapes, SVG, paths, texts, buttons and UI-elements available to change/script with Javascript.

Making this function work properly cost my about 180 manhours work of my spare time so you know you have a tough road ahead trying this.

zhongyu wu

Thank you very much for the help! Math! I was able to change a single image for now with only the first slide. Going on a tough road is painful, but it is one of the best ways to learn. In this era, everyone wants more automation in the work to make it easier to implement. Nothing feels better to get the code working. Again thank you for your guidance and it really helps me a lot. 

Adrian Murray

Hi Math and all,

Having some real problems with GS code.  Essentially, I am using the below code to open up more advanced animated sequences but the problem is that sometimes they work, sometimes they don't.  The other issue is the moment you re-size the browser, it all just falls apart.  Do you have any thoughts please:

//Reference the object
var elderflowerCTA = document.querySelectorAll("[data-acc-text='elderflowerCTA']");

//Animate in GSAP
gsap.set(elderflowerCTA, { scale: 1.05, ease:"bounce.out" });

Really frustrating as when the JS executes, the animations look fantastic but they are just so unreliable.

Hope you can help folks.  Cheers.

Math Notermans

First of all, its for sure not GS code thats unreliable as its one the best engines there is and they are very helpfull on their own forum. Its probably due to either mistakes in your own code or due to Storyline's own way of interpreting it.

The resizing browser issue is due to the Modern Storyline player that scales up everything. You can fix that by calculating positions and animations. Quite some approaches for that allover the forum. The Classic player doesnot have that issue.

Do share a sample, as finding errors without it is just guessing.
One guess is the use of querySelectorAll when in fact selecting just a single element. querySelector is better in that case. Another one is using a 'bounce' ease when you use gsap.set... any ease needs time to execute properly. So is better in this case.

Kind regards,

larry van wave

I purchased Club Greensock, and I have not been able to successfully get SplitText plugin to work. Any idea what I am missing here:
================ code ==============

//name the vars of elements on the slide
var txt1 = document.querySelectorAll("[data-acc-text='text1']");

//load in the plugins
 "https://(our server site is here)/greensock/minified/SplitText.min.js",
], onLoadScripts); 

function onLoadScripts() { 
  split = new SplitText(txt1, {type:"chars"})
  animation.from(split.chars, {opacity:0, y:50, ease:"back(4)", stagger:{
        from:"end", //try "center" and "edges"

//the function that look for and loads the urls
function dynamicallyLoadScripts(urls, onComplete) { 
 if (typeof urls === "string") { 
     urls = [urls]; 
        let toLoad = urls.length; 
        urls.forEach(function(url) { 
        let script = document.createElement("script"); 
        script.src = url; 
        script.addEventListener("load", function() { 
             if (!--toLoad) { 
                 onComplete && onComplete();