Articulate Storyline: Export to Google Drive


On the Articulate user days in Utrecht (2015), we held a session about exporting Articulate Storyline variables to Google Drive (Spreadsheet). This export is achieved via JavaScript (jQuery).

Use the button Source to download the Storyline project. If you want the same lay-out as the example above, please install the following fonts (see source file):

  • flaticons-stroke.ttf
  • fontawesome-webfont.ttf

Why do you want to make an export to Google Drive?

  • No LMS (or database) available;
  • Store more information than just the test results. You can export all Storyline variables to Google Drive.

Why using Javascript?

Besides using Javascript it's also possible to embed a Google form as WEB object. This is a lot easier than using Javascript.

We have chosen for Javascript because we would like the ability to change the appearance of the form. In addition, we want to collect data across multiple slides and then store them in Google Drive.

UPDATE 2017-06-06:

There is also an article available for importing records of a Google Spreadsheet into Storyline:

Articulate Storyline: Import from Google Drive


Google changed somthing within the mechanism of storing variables into a Google Spreadsheet. Creating a new spreadsheet can give you problems for storing variables. Before you try the steps below, please use a template:

Go to the page below and use the button Use this template:

And go further with step 4 below!

Export to Google Drive

The export consists of several steps. Some steps take place on the side of Google. For these steps you will need a Google account. The remaining steps take place in Articulate Storyline.

Below you'll find the steps to create the export to Google Drive: 

1. Login with your Google account

Go to the page below and login with your credentials:

2. Create a new spreadsheet

Click on the Plus-sign (+) to create a new spreadsheet.

3. Rename the sheet to DATA

Give the spreadsheet a title and change the name of the sheet to DATA.

4. Add extra columns

Add extra columns you would like to use. Probably these columns will have the same name as the variables in Articulate Storyline. As example: the column names name, email and message like in the source project.

You can add the column date in the spreadsheet, if you would like to save the date when the form is sent to Google.

The column names needs to be identical to the variable names in Articulate Storyline. The column names are case sensitive.

 5. Copy the ID of your form

Find out your spreadsheet ‘key’ by looking in the address bar, the key is the long series of letters and numbers after /d/ and before /edit: Like:

The KEY will be = 1AzBuim89ma_ght1-O14cksVzXrQL5Vh4XnRqY9OM_gc

Save this KEY in a Notepad file to keep safe, or other application, you will need this ID in step 8.

6. Open the Script Editor

Open the script editor Tools, Script Editor.

7. Paste custom script

If you are using the template, then you can skip this step. The code is already in the template available. If not, paste the script below, which is needed for importing the Storyline variables into this spreadsheet:

// 1. Enter sheet name where data is to be written below

// 2. Enter the KEY of your form
var KEY = "KEY"

// 3. Run > setup

// 4. Publish > Deploy as web app
// - enter Project Version name and click 'Save New Version'
// - set security level and enable service (most likely execute as 'me' and access 'anyone, even anonymously)

// 5. Copy the 'Current web app URL' and post this in your form/script action

// 6. Insert column names on your destination sheet matching the parameter names of the data you are passing in (exactly matching case)

var SCRIPT_PROP = PropertiesService.getScriptProperties(); // new property service

// If you don't want to expose either GET or POST methods you can comment out the appropriate function

function doGet(e){
return handleResponse(e);
function doPost(e){
return handleResponse(e);

function handleResponse(e) {
// shortly after my original solution Google announced the LockService[1]
// this prevents concurrent access overwritting data
// [1]
// we want a public lock, one that locks for all invocations
var lock = LockService.getPublicLock();
lock.waitLock(30000); // wait 30 seconds before conceding defeat.

try {
// next set where we write the data - you could write to multiple/alternate destinations
var doc = SpreadsheetApp.openById(SCRIPT_PROP.getProperty(KEY));
var sheet = doc.getSheetByName(SHEET_NAME);

// we'll assume header is in row 1 but you can override with header_row in GET/POST data
var headRow = e.parameter.header_row || 1;
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
var nextRow = sheet.getLastRow()+1; // get next row
var row = [];
// loop through the header columns
for (i in headers){
if (headers[i] == "Timestamp"){ // special case if you include a 'Timestamp' column
row.push(new Date());
} else { // else use header name to get data
// more efficient to set values as [][] array than individually
sheet.getRange(nextRow, 1, 1, row.length).setValues([row]);
// return json success results
return ContentService
.createTextOutput(JSON.stringify({"result":"success", "row": nextRow}))
} catch(e){
// if error return this
return ContentService
.createTextOutput(JSON.stringify({"result":"error", "error": e})) .setMimeType(ContentService.MimeType.JSON);
} finally { //release lock

function setup() {
var doc = SpreadsheetApp.getActiveSpreadsheet();
SCRIPT_PROP.setProperty(KEY, doc.getId());

8. Paste your key

There is one place in the script where it says var KEY = "KEY". Copy and paste your key between the "".

9. Run the script

Run the script via Run, Run function, setup. The first time you run the script it will ask for permission to run. You will need to grant it. If you run the script for a second time you won't get any popup. This is an indication it has run successfully.

10. Deploy a web app

Go to Publish, Deploy as web app. Enter a project name (optional) and set security level. Choose for Me and access Anyone, even anonymously. Click on the button Deploy to create the web app.

11. Copy the Current web app URL

Copy the 'Current web app URL' and paste it in a Notepad file to keep safe.

Example URL:

12. Add jQuery library

In Articulate, add a trigger to run javascript (Execute Javascript) and use the code below.

This code will add the jQuery library to this project, so you won't have to change the HTML files after publishing the project. The jQuery library is needed for exporting the information to Google Drive.

var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.src = '//';
script.type = 'text/javascript';

13. Store information

Add another trigger to run Javascript (Execute Javascript). You can use the code below.

Replace the value Current web app URL for the webapp url you've saved in step 11

Below the webapp URL, you can place the column names of the spreadsheet and the Storyline variables. Please be aware of the comma if you add multiple variables.

var player = GetPlayer();

WEB_APP_URL = "Current web app URL";

// "Columnname_Google_Spreadsheet" : player.GetVar("Name_Storyline_Variable")
// ATTENTION: Use a comma if you use multiple Storyline variables
storyline =
"date" : new Date().toJSON().slice(0,10), //STORE DATE
"name" : player.GetVar("name"),
"email" : player.GetVar("email"),
"message" : player.GetVar("message")

Don't delete the row below, if you would like to save the date when the form is sent:

"date" : new Date().toJSON().slice(0,10), //STORE DATE

14. Export code to Google Drive

Latest Javascript code. Add another trigger to run Javascript (Execute Javascript). You can use the code below. This trigger will send the information from step 13 to Google Drive.

setTimeout(function (){

//Export to Google
type: "POST",
data : storyline,
success: function(data)
error: function(err) {
console.log('Error:', err);
return false;
}, 1000);

15. Publish to SCORM or WEB format

Publish your articulate project to WEB or SCORM format. You need to host it on a WEB server or somewhere like SCORM cloud (or a LMS).

This export will work in Flash and HTML5 output. You can't use the Articulate Mobile Player, because it won't support Javascript code.

Thank You

Thanks Kate Robertson for making the blog post below:
Exporting Variables into a Google Spreadsheet

I started with your setup and then have tried to simplify the steps by changing the Javascript code and importing the JQuery Library.


246 Replies
Matthew Bibby
Scott Syer

Being able to capture variables and then building custom dashboards above and beyond normal LMS completions has changed the way we look at learning! 

It'd be good to see an example of the custom dashboard you've put together Scott. 

And thanks for updating this tutorial Bastiaan. 

Camille Goodwin-Boyd

Hello, okay, I have followed all of the wonderful instructions but unfortunately all I'm receiving is "undefined".  I have checked and rechecked all of the script information both in google and in articulate 3 and I can't get the data in google sheets.  I'm not working on a LMS, just working with articulate 3 and google sheets from my work desktop.  Any more suggestions that I can try?  Leaving this job next week and I really want to get this project finished.  Thanking you all in advance for your support with this issue. 

Suhasini Kotcherlakota

Hello Bastiaan,I

I need to track date and time. I tried changing in this location of the code:

"date" : new Date().toJSON().slice(0,10), //STORE DATE


"date" : new Date().toJSON(), //STORE DATE 

I removed the slice option display the time. I was able to get the time but not the time zone I want. How can I get the time to be stamped for US Central time.


Thanks so huge for this helpful and superb tutorial... I managed to do it 3 years back using similar approach but yours is more efficient and does not add blank rows in the google sheet when appended, mine did :-(... Now that I need to develop a project like this again, this has been truly a "savior" so I can get started with my new task. God bless Basti ;-) 

Mark Bawden

Hi Bastiaan,

First of all, thanks for being amazing and continuing to monitor this thread!

I've been using this reporting method successfully for several modules now, and all has gone well. 

I created a new module today, however even when I recreate my steps exactly the same, the report does not display any data. Usually if I'd messed up a variable or something similar I'd see "undefined", but instead I get no input at all.

I've attached a test .story file to this post, and the link to my test form is

Any idea what might be going wrong? The one factor I'm considering is that my workplace uses Google to facilitate comms and sharing of documents (Google Drive, Gmail, etc) and there is a domain that applies to all email addresses within the workplace. This has never prevented the reporting from working in the past, however.



Ray Handley

Thanks very much for this. Works great. I have followed these steps and got it to work perfectly on a test module. When I applied it to another module I keep getting this error "actionator::exeJavaScript - this.execUserJs is not a function" in the inspector. Does anyone know what I possibly am doing wrong here?

The other module has some other Javascript being used as well on different slides - 1. to email results and 2. to print a certificate; but none of it is working and I am getting the same error.


Bastiaan Timmer

Hi Ray,

I think there is a conflict within the Javascript code. Maybe your scripts are having a conflict with my Javascript code. The error refers to UserJS. All the javascript code is stored in a user.js file. So I think there must be a problem within this file.

Could you disable your own Javascript code and test if this solve the problem?

Best regards,



Ray Handley

Thanks Bastiaan,

I have disabled all my other javascript but still no joy. I also cannot locate (in the published output) the user.js file you referred to. I will keep trying but also wonder whether calling functions from an external js file might be a better way to go and easier to manage. What do you think? Have you tried this approach before?


Jack Waller

Hi Bastiaan, 

Many thanks for the great tutorial. I've been trying all day to get this work with my own spreadsheet but no luck so far. 

Every time I run the storyline file and load the page that executes the jQuery I get a 404 for the script in the inspector and the following message:  No 'Access-Control-Allow-Origin' header is present on the requested resource. 

You can see your original storyline file trying to point to my spreadsheet here.

Any thoughts?



Jack Waller

Slight update. I've narrowed the issue down to the web script. It's not working when I test it:

I've followed the steps identically to your instructions, so I don't understand why this wouldn't be working. 



Jack Waller

Hi there! 

Just wanted to let you know I managed to resolve the issue I was having. The web app script was causing a conflict because I was signed into multiple google accounts.

I signed out of all of the accounts and re-signed in again and copied the web app script. It worked straight away after that. 

Thanks again for the tutorial. 


arwa zeidan

Hello Bastiaan,

Thank you a lot for this post.
I have created a form where user has to enter his name, surname and date of birth.
I inserted  a datepicker as web object.

The thing is, all variables are being extracted correctly to google sheets except for the variable extracted from the datepicker . Both names of this variable are the same in the google sheet and in SL, but "undefined" values are being stored in the date of birth column. 

Any help is appreciated.
Thank you!

Alison Gretchen Stander

Hi everyone


I am having the same issue as Mark Bawden. I have used spreadsheets before and everything worked fine, but with the latest SL project, nothing seems to pull through. Does any one have any solutions with regards to this.

I have re-published the project, made new spreadsheets, remade the project. Nothing. I am currently using SL2.

Any suggestions, advice or solutions any one can think of?