Matthew Bibby
Matthew Bibby

Track Time Spent on a Slide or in a Multi-Slide Activity

Matthew BibbyMatthew Bibby

Have you ever wanted to track how much time a learner spent on an individual slide?

Or maybe you wanted to know how long it took them to complete an activity that spans multiple slides?

While some time-based data (such as session time) is captured by your LMS, that doesn't help you do stuff like:

Wouldn't it be cool if you could get Storyline to do that? Well, now you can!

In this tutorial, we will write some JavaScript that you can use to track how long a learner takes to do something, whether that's complete the whole course or just a few slides.

In this tutorial, we will write some JavaScript that we can use to track how long it takes the learner to do something.

Then we will look at a couple of different ways this code could be used.

Before We Begin

We'll start by writing some JavaScript and then we will look at how to set this up in our Storyline course.

Open your text editor.

If you don't already have a preferred text editor, then grab yourself a copy of Sublime Text. Then create a new file and save it as countup.js (or whatever you want to call it):

Save as dialog

If we don't save our file as a .js file, then the text editor won't know what language we are writing in. And that's a problem because we want the text editor to include all the pretty colours in the right places (programmers call this syntax highlighting, but let's stick with pretty colours!)

Recording the Start Time

Let's create a new JavaScript variable called start and pop the date in there:

var start = new Date();

The Date object grabs the system time from the device being used and returns a value such as .

Now we need to convert this date and time into a different format so that we can do stuff with it.

So let's create another new JavaScript variable and parse the date:

var startComparable = Date.parse(start);

You may be wondering what this Date.parse is all about? Well, it takes the date that we just grabbed and counts how many milliseconds there are between midnight Jan 1, 1970, and our date.

Why 1970? I've no idea, but I'm going to blame The Beatles.

So now our start date will look like .

Yes, that's a lot of milliseconds.

Now, as you've probably realised, we are grabbing this start date so that we can keep track of when a learner started viewing a slide.

So let's pass this data to Storyline so we can use it later. First, we need to establish communication with Storyline:

var player = GetPlayer();

Then we can ask Storyline to store our parsed date:

player.SetVar("1_Start",startComparable);

So what we've done here is say:

Hey Storyline! Are you listening? Cool. Can you please take the value of our startComparable variable and store it in your 1_Start variable?

Then we can trust that Storyline will hang on to that value until we need it.

So your code so far should look like this:

contents of countup.js

This is all we need to capture the time that a learner begins viewing a slide or starts an activity.

Recording the End Time

Now, when the learner finishes viewing a slide or completes the activity, we want to record the end time. We can do that using the same code, with a couple of slight changes:

var end = new Date();
var endComparable = Date.parse(end);
var player = GetPlayer();
player.SetVar("1_End",endComparable);

As you can see above, all that we are doing differently here is changing the variable names.

Calculating the Difference

Now, let's figure out the difference between our start and end times.

First we need to grab our variables back from Storyline:

var player = GetPlayer();
var startComparable = player.GetVar('1_Start');
var endComparable = player.GetVar('1_End');

Then we are going to find the difference between them:

var ms = endComparable - startComparable; 

Now we need to translate this value back into a time format that's more easily understood. So let's do some math:

var s = ms % 1000;
ms = (ms - s) / 1000;
var secs = ms % 60;
ms = (ms - secs) / 60;
var mins = ms % 60;
var hrs = (ms - mins) / 60;

Um. What? That doesn't look like math!

Welcome to JavaScript, where math is weird thanks to things like the modulus operator (%), which we can use to figure out the remainder after division.

So, what's going on in this little bit of code? We are saying:

Hey, Milliseconds! How many hours fit in that big number of yours? Okay, got it. Now, how many minutes fit in the number that remains? Thanks. Now, just one last question real quick, how many seconds are left over?

The answers will be stored in three JavaScript variables, hrs, mins and secs. So let's pass these values to Storyline:

player.SetVar("1_Hours",hrs);
player.SetVar("1_Minutes",mins);
player.SetVar("1_Seconds",secs);

Now the number of hours, minutes and seconds that it took the learner to view the slide or finish the activity will be available in Storyline.

Quick Recap

So at this stage, we have three bits of code that we can use.

This is used to capture the start time:

var start = new Date();
var startComparable = Date.parse(start);
var player = GetPlayer();
player.SetVar("1_Start",startComparable);

This is used to capture the end time:

var end = new Date();
var endComparable = Date.parse(end);
var player = GetPlayer();
player.SetVar("1_End",endComparable);

And this is used to find the difference between the two:

var player = GetPlayer();
var startComparable = player.GetVar('1_Start');
var endComparable = player.GetVar('1_End');
var ms = endComparable - startComparable; 
var s = ms % 1000;
ms = (ms - s) / 1000;
var secs = ms % 60;
ms = (ms - secs) / 60;
var mins = ms % 60;
var hrs = (ms - mins) / 60;
player.SetVar("1_Hours",hrs);
player.SetVar("1_Minutes",mins);
player.SetVar("1_Seconds",secs);

Note that there is a 1_ in front of each Storyline variable in the examples above. This just means this is the first time we are using this code in a project. If you want to track the time spent on a different slide or activity as well, you'd use the same code, but just change the 1_ to a 2_.

Setting up the Storyline File

Now we need to set up our Storyline file to work with this code.

For this tutorial, let's track:

  1. How long the learner spends on the first slide.
  2. How long it takes them to complete the activity that spans slides 2 and 3.
  3. How long it takes them to complete all three slides. And if it is less than a minute, we'll give them a cupcake .

Okay, so here is the first slide:

Slide 1

We want to start tracking the time from when the slide first loads, so let's add an Execute JavaScript trigger and set it to fire when the Timeline Starts.

Execute JavaScript on timeline start

Then if we click on the ... next to Script, we can then add our start code:

var start = new Date();
var startComparable = Date.parse(start);
var player = GetPlayer();
player.SetVar("1_Start",startComparable);

So this means that when the first slide loads, we will be making note of the date and time that occurred and storing that info in a Storyline variable called 1_Start.

Next, we need to record what time the learner stops viewing the first slide. So let's set up another Execute JavaScript trigger but have this one fire when the User Clicks the Next button.

Execute JavaScript when User Clicks Next

Then if we click on the ... next to Script, we can add our end code:

var end = new Date();
var endComparable = Date.parse(end);
var player = GetPlayer();
player.SetVar("1_End",endComparable);

It is important that this trigger fires before the learner jumps to the next slide. So in the Triggers panel, drag the Execute JavaScript trigger up so it is first on the list:

Execute JavaScript trigger above jump to next slide trigger

Now, let's look at our second slide:

Slide 2

Once again, we want to start tracking the time when this slide loads. So let's add an Execute JavaScript trigger and set it to fire when the Timeline Starts.

Execute JavaScript on timeline start

Then if we click on the ... next to Script, we can then add our start code:

var start = new Date();
var startComparable = Date.parse(start);
var player = GetPlayer();
player.SetVar("2_Start",startComparable);

So this means that when the second slide loads, we will be making note of the date and time that occurred and storing that info in a Storyline variable called 2_Start.

Now, we don't want to record when the learner finishes this slide, as we are looking at how long it takes them to complete the whole activity. So let's go to slide 3:

Slide 3

In this case, we want to add our end code to both the Yes and No buttons as clicking those indicates that the learner has finished the activity.

So let's add an Execute JavaScript trigger that fires when the User Clicks the Yes button:

Execute JavaScript when user clicks Next

Then if we click on the ... next to Script, we can add our end code:

var end = new Date();
var endComparable = Date.parse(end);
var player = GetPlayer();
player.SetVar("2_End",endComparable);

It is important to check that this trigger fires before the learner jumps to the next slide. So in the Triggers panel, drag the Execute JavaScript trigger up so it is first on the list:

Execute JavaScript trigger above Jump to next slide trigger

You can then copy this same Execute JavaScript trigger to the No button.

Setting up the Variables

Now, before we continue, you will need to make sure you have the following text variables set up in Storyline:

1_Start
1_End
1_Hours
1_Minutes
1_Seconds
2_Start
2_End
2_Hours
2_Minutes
2_Seconds
3_Hours
3_Minutes
3_Seconds

You'll want to give these a default value of 0.

To set up a variable, simply click the Manage Project Variables button:

Click manage project variables

Then hit the + button and enter the name of the variable:

Click create a new variable

Comparing our Start and End Times

Now we need to compare our start and end times. So let's go to slide 4:

Slide 4

You'll notice that there are variable references (e.g. %1_Minutes%) set up to show the results on screen. This is just so we know it's working correctly, these don't need to be displayed.

It is also worth noting that we are not showing hours on this slide. So if someone leaves this open for 1 hour and 1 second, then only the 1 second bit will be displayed. But that's okay, as this is just a demo, and the data regarding hours is still stored in the %1_Hours% variable.

Let's add another Execute JavaScript trigger and set it up to fire when the Timeline Starts. This time, we want to start by adding the following code:

var player = GetPlayer();
var startComparable = player.GetVar('1_Start');
var endComparable = player.GetVar('1_End');
var ms = endComparable - startComparable; 
var s = ms % 1000;
ms = (ms - s) / 1000;
var secs = ms % 60;
ms = (ms - secs) / 60;
var mins = ms % 60;
var hrs = (ms - mins) / 60;
player.SetVar("1_Hours",hrs);
player.SetVar("1_Minutes",mins);
player.SetVar("1_Seconds",secs);

You'll remember that this is the code we need to find the difference between our first start and end times.

Let's now add the following to the end of this so that we can also find the difference between our second start and end times:

var startComparable = player.GetVar('2_Start');
var endComparable = player.GetVar('2_End');
var ms = endComparable - startComparable; 
var s = ms % 1000;
ms = (ms - s) / 1000;
var secs = ms % 60;
ms = (ms - secs) / 60;
var mins = ms % 60;
var hrs = (ms - mins) / 60;
player.SetVar("2_Hours",hrs);
player.SetVar("2_Minutes",mins);
player.SetVar("2_Seconds",secs);

Then, we also want to figure out how long it took the learner to view all three slides, so let's add in this:

var startComparable = player.GetVar('1_Start');
var endComparable = player.GetVar('2_End');
var ms = endComparable - startComparable; 
var s = ms % 1000;
ms = (ms - s) / 1000;
var secs = ms % 60;
ms = (ms - secs) / 60;
var mins = ms % 60;
var hrs = (ms - mins) / 60;
player.SetVar("3_Hours",hrs);
player.SetVar("3_Minutes",mins);
player.SetVar("3_Seconds",secs);

So, all together, it should look like this:

var player = GetPlayer();
var startComparable = player.GetVar('1_Start');
var endComparable = player.GetVar('1_End');
var ms = endComparable - startComparable; 
var s = ms % 1000;
ms = (ms - s) / 1000;
var secs = ms % 60;
ms = (ms - secs) / 60;
var mins = ms % 60;
var hrs = (ms - mins) / 60;
player.SetVar("1_Hours",hrs);
player.SetVar("1_Minutes",mins);
player.SetVar("1_Seconds",secs);
var startComparable = player.GetVar('2_Start');
var endComparable = player.GetVar('2_End');
var ms = endComparable - startComparable; 
var s = ms % 1000;
ms = (ms - s) / 1000;
var secs = ms % 60;
ms = (ms - secs) / 60;
var mins = ms % 60;
var hrs = (ms - mins) / 60;
player.SetVar("2_Hours",hrs);
player.SetVar("2_Minutes",mins);
player.SetVar("2_Seconds",secs);
var startComparable = player.GetVar('1_Start');
var endComparable = player.GetVar('2_End');
var ms = endComparable - startComparable; 
var s = ms % 1000;
ms = (ms - s) / 1000;
var secs = ms % 60;
ms = (ms - secs) / 60;
var mins = ms % 60;
var hrs = (ms - mins) / 60;
player.SetVar("3_Hours",hrs);
player.SetVar("3_Minutes",mins);
player.SetVar("3_Seconds",secs);

Now, About That Cupcake

Remember that we wanted to give the learner a cupcake if they completed all three slides in less than a minute?

If we add a cupcake to Slide 4 and make it hidden by default (by going to States and changing the Default State to Hidden), then we can make it appear via a conditional trigger.

Cupcake object on slide 4

Here's the trigger we need:

Conditional trigger showing cupcake if 3_hours and 3_minutes equal to 0

And if you want to display something different for those who don't get a cupcake, you could use a trigger like this:

Conditional trigger showing no cupcake if 3_hours and 3_minutes not equal to 0

Okay... enough about cupcakes.

Here's a Demo

Here is a demo of the final project so you can see how it works:

So, now you know how to track the amount of time a learner has spent on an individual slide or in a multi-slide activity.

This code is flexible and can be used in countless ways, so if you use this in a project, please let me know what you did with it via a comment below.

It'll be both interesting and inspiring to hear about the different ways that people put this code to use. Thanks!

Files you might need:

Here is the .story file that was used in this example.

Here is a SCORM package you can use to see how this works in your LMS.

Frequently Asked Questions:

Q. Can the learner go back and view these slides more than once?
A. Sure, as long as you set the When Revisiting option in the Slide Preferences to Reset to Initial State.

Q. Why didn't you use that silly font you like so much to display the variable references on slide 4?
A. Because fonts can't be embedded in the published output as noted here.

Q. Is it possible to store this time data in an LMS?
A. Sure, see the Capturing Storyline Variables in an LMS tutorial for details.

Q. Does this JavaScript account for leap seconds?
A. If you know enough about Unix time to ask that question, then perhaps you should be writing this tutorial! (That's a no.)

Q. Can this be manipulated by changing the system date?
A. Yes, it can. So if you are using this code as part of a very important assessment and are concerned that someone may cheat by changing the system time, then you'll want to modify the JavaScript. One approach that may help solve this potential issue would be to grab the date and time from the time servers used by the NIST Internet time service. You can find the server locations here.

Q. What if someone exits the course in the middle of an activity and doesn't log back in for three weeks?
A. It depends if the course is set up to resume or not. If the course resumes and they were in the middle of viewing a slide or completing an activity when they exited it originally, then the time tracked will be an accurate representation of how long it took them (i.e. 3 weeks +).

Q. Does this work in HTML5? (i.e. will it work on my phone and tablet?)
A. Yes, it will.

Q. Will this work in Articulate Mobile Player?
A. No, it won't, due to the whole JavaScript doesn't work in the Articulate Mobile Player thing.

Q. Will this work in an LMS?
A. Yes, it will, I've tested it in SCORM Cloud and it worked perfectly. If you'd like to test it in your LMS, you can grab the SCORM package above and use that for a quick test.

Q. Do Articulate support this method?
A. No, they are not able to offer assistance with JavaScript stuff. So if you run into issues when using this approach, their support team won't be able to assist. However, I may be able to help, so reach out if you're stuck.

Q. My question isn't listed here, what should I do?
A. Google it? And if that doesn't help, try bing you can leave a comment below or contact me. I'll get back to you once I've finished my cupcake.

If you found this tutorial helpful and think others in your network will also, please share using the share buttons below. Thanks!

Matthew Bibby
Author

Matthew Bibby

I'm Matt. I'm an eLearning Consultant. I help people like you develop memorable, engaging and profitable training programs. What do you need a hand with?

Comments