Loading an image at run time in Storyline

Feb 24, 2024

Back in 2013, I wrote an article entitled "Gamification in e-Learning design". Alongside the article I created some demonstrations to illustrate some of the points being made. One of these demos was a card game quiz.

It was a fairly familiar TV quiz show format. The learner was presented with 10 cards on the screen. When they turned over the first card, a timer started and they had just 60 seconds to identify all 10 images:Card Game The demo didn't use any JavaScript and was partly done to show off the use of triggers, layers and states in Storyline which was a fairly new product at that time (the demo was written in Storyline 1).

We've used this approach in various client projects over the years and it always goes down well adding a bit of fun to quizzes. I always meant to re-visit the original game and see if I could improve it using a bit of Javascript coding, but somehow never got round to it. 

A few weeks ago I had a bit of spare time and picked the project up again (after all, 10 years was long enough for it to lay dormant!). What I wanted to do was two things:

(a) make the card selection random - In the original game the cards always appeared in the same position.

(b) load the card images (and their descriptions) from a set of image files at run time - if I could do this, then I could make the game 'generic' and change the images outside of Storyline. So if, for example, I wanted a new quiz every week, all I would have to do is change the image files and a table of descriptions and the quiz would be done.

Turns out this is not trivial. If you have ten images loaded, you have to be able to select them at random with no 'repeats'. Actually, this wasn't too bad to code in JavaScript. BUT the big problem was how do I change the card image??

We had been using the GreenSock GSAP JavaScript library for advanced image animation and there is the facility in this to change the source ('src' attribute) of an image. Surely, this was the way to go. Sadly, I couldn't get it to work. I could rotate the cards, shake them, shrink them, but I couldn't change the 'src'. I could be wrong - and if so, if someone cracks this problem, please let me know - but, eventually, I concluded that images are somehow 'sandboxed' in Storyline. This is probably reasonable. After all, it would be pretty dangerous to allow coders like me to just load an image file at run time into the Storyline app!

I needed a new approach. I could change an image using GSAP - just not inside Storyline. But if I created a simple web page and loaded it as a web object, then I could get where I wanted to go. And this works fine as the following demo shows (click the link):

Single Card Draw Demo

The card is loaded in a responsive HTML5 page and JavaScript loads a complete deck of playing card images into one array and their descriptions into another at run time. The cards are selected at random and put in a 'discard' array when used. When the deck is exhausted the deck is reset and the process starts again (as though the deck has been shuffled).

The clever bit is sending the card description to Storyline so it knows what card you have drawn - but that's a story for another article, I guess!

I'm off to re-build my original quiz...

4 Replies
Math Notermans

Using an image as WebObject and changing the source of that works fine..and i use that regularly to dynamically change images. Using Cloudinary, AWS or Firestore you can even use it to upload images and make a complete dynamic setup.

When you add images upfront to the external files folder in Storyline you can use this approach.
Semicode below, but i do have it working like this.

function addImage2Stage(_color,_i){
var zInd =  1500;
var currentImage;
var tmpEl;
var indexString;
var bodyRect = document.body.getBoundingClientRect();
var elemRect;
var offsetTop;
var offsetLeft;
var urlArray = [
'story_content/external_files/avatar1.png',
'story_content/external_files/avatar2.png',
'story_content/external_files/avatar3.png'
];
 
 
//for(var i = 0; i < imagesArray.length;i++){
zInd++;
currentImage = shapesCollection[_i];
console.log("showArrayIndex2: "+_i);
tmpEl = document.getElementById(currentImage.id);
var image = new Image();
image.src = urlArray[_i];
var $newdiv1 = $( "<div id='ispr' class='indexImageArr' style='background: "+_color+";width: 60px;height: 60px;border-radius: 50%;z-index:1500;position:absolute;font-family:Arial, sans-serif;text-align:center;padding:5px;font-size:20px;color:#FFFFFF;font-weight:normal;left:0;top:0;visibility: inherit'>"+(_i+1)+"</div>" );
    elemRect = tmpEl.getBoundingClientRect(),
  offsetTop   = elemRect.top - bodyRect.top;
offsetLeft  = elemRect.left - bodyRect.left;
$('body').append($newdiv1);
$newdiv1.append(image);
$newdiv1.css('top', offsetTop);
$newdiv1.css('left',offsetLeft);
}
John Cooper

Thanks Math - useful suggestion. In my case it's our server - so I can just copy the files into the right folder - but I will bear this approach in mind for client use.

I know we discussed whether you could change the 'src' attribute using GSAP directly in Storyline before. I'm sure you can't - which, as I said in my post, makes sense.

Best regards

John Cooper

Hi Math - just read your code in more detail. Absolutely. I use a similar approach to load the card images into an array cards[ ] and I then read their descriptions into a second array descriptions [].

I do most of the hard work in the html page of the web object putting the JavaScript code there. I set up an event listener in the code to wait for the card to be clicked This triggers a routine that selects a random number (between 1 and the length of the card array) - I extract the card image and its description by 'splicing' each array to remove the card and description in question. I also add the selected card and description to two discard arrays so I can reset the original arrays when the deck runs out.

I have an event listener in the Storyline code and the web object sends the details of the selected card to Storyline each time a card is clicked. This is assigned to a variable which triggers the display on the screen.

I know you do a lot of fairly advanced games so this is probably not new to you - but the ability to send information from the web object to Storyline was a revelation for me!

I'm really looking forward to using this in many future advanced interactions.

All the best, John