In June of 2016, ADL announced the release of cmi5, the next generation of LMS-to-online content communication. The specification defines a set of rules for using xAPI in the “LMS launches content” scenario. ADL recently released a comparison of cmi5 and SCORM, highlighting the advantages and capabilities of cmi5. The benefits include elimination of pop-up windows, distributed content, and a more flexible data model than SCORM.

While many vendors have announced that they plan to support cmi5, and some LMS and content tools have released versions of their products that incorporate the specification, what if your particular content tool does not yet support cmi5? Can you still take advantage of all the benefits?

The short answer is “yes.” This article will discuss how we added cmi5 to HTML5 content using free script libraries.

Why cmi5?

At its core, xAPI is a data transport and storage mechanism. By itself, xAPI was never intended to replace SCORM. In fact, the term LMS never appears in the xAPI specification. So what if you still want to launch content from an LMS, but take advantage of xAPI? To address this need, ADL developed cmi5. (The cmi5 effort was originally started by the Aviation Industry CBT Committee, or AICC.)

Let’s take a quick look at some of these benefits we mentioned earlier.

Pop-up windows

If you have ever supported students using your LMS, you know one of the most common student issues is “I clicked the start button and nothing happened.” You know that you are now on the hunt for the dreaded “pop-up blocker.” Often, you’ll find more than one pop-up blocker installed on a student’s workstation. Each has to be either removed or modified to allow SCORM content to open. With cmi5, it is possible to completely eliminate pop-up windows for content launched from your LMS.

Distributed content

It seems like SCORM content has gotten bigger and bigger as the internet has gotten faster. Typically with SCORM, all your content is loaded on your LMS server. If your LMS and your students are located in the same area, there’s no problem with this. But what if your students are all around the world and your LMS server is in Atlanta? When a student in London launches your content from Atlanta, it is going to be slow. Wouldn’t it be great if you could put your eLearning on a content distribution network, allowing students to download the content from a server close to them, wherever they are? With cmi5, you can.

Store any data you want

While the SCORM data model is big, it is not extensible. You are basically out of luck if you have a need to track something that is not part of the SCORM data model. Since cmi5 is based on xAPI, you can use extensions to store any data you want.

Our scenario

The Public Health Informatics Institute’s (PHII) Informatics Academy, part of the Task Force for Global Health, developed an innovative, informatics capacity–building module using Adobe Captivate. The module included training about the critical success factors of an informatics project with a dashboard-enabled, reusable self-assessment tool (Figure 1).

Figure 1: The informatics project’s dashboard-enabled, reusable self-assessment tool

To capture data from the self-assessment tool, PHII decided to leverage xAPI. This allowed for real-time data capture on how public health agencies rated their informatics capabilities at the project level. The data provides insight on additional support the agencies need to be successful.

This project started with content originally created in Adobe Captivate 9 and published to HTML5. The content had already been set up to send custom xAPI statements using JavaScript within Advanced Actions (see Sean Putman’s article “Captivate JavaScript xAPI Customization”). As in the linked article, JavaScript was entered into Captivate, but in this case ADL’s “xapiwrapper” JavaScript library was used instead of the TinCan script library.

There were some problems with the initial approach:

  1. The LRS endpoint, as well as the credentials to write to the LRS, were “hard-coded” in a script file. While the credentials were encoded, this was not as secure as we would have liked.
  2. There was no way to consistently determine the “actor” since the content was not attached to any credentialing system. Sure, we could have prompted the user to enter an email address, but most people have more than one email address and this would have produced inconsistent reporting. PHII wanted the ability to tie data collected to unique users in their LMS.
  3. There was no consistent method of reporting context for the activities to be tracked. The context property of the statement had to be hand-entered into each custom script in Captivate.

The solution: cmi5

To solve these problems, we converted the content to cmi5. We did this using a free JavaScript library from RISC. (Note: This library depends on xAPI JavaScript libraries from ADL as well as jQuery.) This provided the following benefits:

  1. In cmi5, the LRS endpoint is passed to the launched content. All we had to do was read the endpoint from the launch URL and store it in a global JavaScript variable. We could then use that variable when sending xAPI statements.
  2. Security in cmi5 is handled through a one-time-use token. This provides two security benefits:
    1. The only way the user can access the content is to go through the LMS credentialing process.
    2. Since the token can only be used once, the user cannot “bookmark” the content link to bypass security.
  3. The actor, registration, and activityId are also passed on the launch URL, so we can again save these values to global JavaScript variables and use them throughout the content. There are two advantages to this:
    1. Since the content is now being launched from the LMS, we don’t have to worry about the consistency of the actor properties.
    2. Since the registration is created by the LMS, rather than the content, we know it will consistently map to other values in the LMS. For example, if there are other content modules in the course, the registration value will allow us to report activity for a single user across all the modules.
  4. In cmi5 there is a concept called Publisher ID. This is a value that uniquely identifies the content across all systems. Suppose you want to use the same piece of content in multiple courses or across multiple LMSs. It will have the same Publisher ID each time, so you can compare results across all places where the content is used.

Getting started

To use these free script libraries, we just had to reference some JavaScript files at the top of our published content module:

<!DOCTYPE html>
<html lang="en">
<meta name='viewport' content='initial-scale = 1, minimum-scale = 1, maximum-scale = 1'/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="x-ua-compatible" content="IE=10">
<title>Critical Success Factors for Informatics Projects</title>
<style type="text/css">
		background:url(assets/htmlimages/loader.gif) no-repeat center center;
<script type="text/javascript" src="Getcmi5Scripts.js"></script>
<script type="text/javascript" src="cmi5Callbacks.js"></script>

Note: These scripts get dropped into our content AFTER publishing it to HTML5.

So what do these scripts do for us?

  1. They load all the external JavaScript files that we will need to send cmi5 statements. These include the cmi5Controller library from RISC and the two libraries from ADL for sending statements.
  2. They set up several global variables that we can use in our code. These include the “actor” for our statements.
  3. They get the security token we discussed earlier.
  4. They automatically set up the configuration for sending xAPI statements using the ADL library.
  5. Finally, the required cmi5 “Initialized” statement is sent to the LRS.

Sending other xAPI statements

So at this point we are fully initialized for cmi5, and now we are ready to send custom statements. For example, when a student chose an answer from the screen shown in Figure 2, we wanted to record that selection with xAPI. How can our cmi5 library help us with that?

Figure 2: The cmi5 library can help us record “Not Started” for this item

The script below is associated with the “Not Started” answer for this question. It will send our desired xAPI statement. We created similar scripts for the other questions and answers.

stmt = new
ADL.XAPIStatement(); = actor;
stmt.verb = ADL.verb.answered;
stmt.object = new
ADL.XAPIStatement.Activity('', 'LeadershipQ1');
stmt.result = { 'response': 'Not Started' };
stmt.context = cmi5AllowedContext;
var resp_obj = ADL.XAPIWrapper.sendStatement(stmt);

For this to work in cmi5, we need to use something called “cmi5 allowed” statements. These are xAPI statements that conform to certain minimum rules. Mostly, this involves having particular values in the Context part of a regular xAPI statement. Our RISC cmi5 library automatically creates a global variable that we can use in our statements to meet this rule; we just had to set our context in JavaScript as shown on the last line of the example above.

We can still add other Context properties if we wish; this is just the minimum to conform to cmi5.

To associate this script to an answer in Captivate, you perform the following (see Figure 3):

  1. For this scenario, the Script_Window was set up in the advanced action. In other scenarios you can simply make the button (or SmartShape being used as a button) call the Script_Window to run the JavaScript.
  2. Click the Script_Window button to open the JavaScript window and enter the script needed to create the statement.
  3. Once the script is entered, click the OK button to save the JavaScript in the window.
  4. If using an advanced action, save the action to complete the setup.

Figure 3: The Script Window in Captivate

Once the setup was complete and the project was published, the .js libraries described earlier were copied into the published folder. The final step in the process was to add the scripts to call the libraries into the head section of the .htm file for the published Captivate project.


Table 1 shows the statements from the LRS. The white row is a cmi5 allowed statement; the others are cmi5 required statements.

Table 1: Statements from the LRS

So what’s that crazy value in the Authority Name column? That is the one-time-use token that we described earlier.

Here is the resulting JSON for our “answered” statement.

 	"version": "1.0.0",
 	"id": "af4a596b-3476-4973-8e30-e8848c8796f4",
 	"actor": {
 		"objectType": "Agent",
 		"name": "Werkenthin,+Art",
 		"account": {
 			"homePage": "",
 			"name": "0000100087"
 	"verb": {
 		"id": "",
 		"display": {
 			"en-US": "answered"
 	"object": {
 		"objectType": "Activity",
 		"id": "",
 		"definition": {
 			"name": {
 				"en-US": "Leadership Q1"
 	"result": {
 		"response": "Not Started"
 	"context": {
 		"registration": "46e3bb5a-7ad2-49a9-9363-443019003c81",
 		"contextActivities": {
 			"grouping": [{
 				"objectType": "Activity",
 				"id": ""
 		"extensions": {
 			"": "71"
 	"timestamp": "2017-07-13T14:39:42.659Z",
 	"stored": "2017-07-13T14:39:42.659Z",
 	"authority": {
 		"objectType": "Agent",
 		"account": {
 			"homePage": "",
 			"name": "591c809b-574a-44df-b205-d6882c49a4c5"

Returning to the LMS

Earlier we talked about how cmi5 can eliminate pop-up windows. When our content was launched, the LMS went away and the content appeared in the same window. This means we had to add a “return to LMS” button to the content. Otherwise, when the user was done they would have no way to get back to the LMS. So, we added a SmartShape button at the top right of each screen. In the on-click Action, we execute the JavaScript below. Our code also sends the cmi5 “Terminated” statement.

document.location.href = GoLMS();

The GoLMS() function was provided by the RISC libraries. This simple function call returned us magically to the LMS, right where we left it.


In this article, we described a use case for adding cmi5 to existing xAPI content. Our content had some security issues that were easily solved with cmi5, and we were still allowed to send all the xAPI statements we needed. While it required some expertise in JavaScript, it was relatively easy to convert the content; we finished the task in about a day of trial and error. Since we used free, easily available libraries, this approach can easily be reused with other content.

Next steps

Our goal with this project was to see how public health agencies rated themselves on certain critical success factors. We will continue to monitor and evaluate the data collected. This will help us to identify whether there are any other opportunities to capture additional helpful data, as well as determine the need to remove unnecessary statements that are reporting from the project. We also plan to set up a dashboard with drill-down reporting to better visualize our results. This will allow us to find the factor areas where public health departments are experiencing challenges. Then, we can use that data in conjunction with other information to determine specifically where we should provide additional training and support to better help these agencies.

All Contributors

Sarah Mercier

CEO & Strategic Consultant, Build Capable

Sean Putman

Vice President of Learning Development, Altair Engineering

Art Werkenthin

President, RISC