Good learning content takes a lot of effort to create—especially video content. You want to make sure that the videos you create accomplish their goals. Do they convey the information you want them to? Are your students actually learning anything from them? Or, are they just letting the videos play?

In previous articles, I’ve covered the four steps to send an xAPI statement and way to build statements tailored to your users and your environment. But there are other things to consider when sending xAPI statements. You must consider what data you’ll need in order to show that the user learned anything, and how you can get it.

I presented on this topic at The eLearning Guild’s DevLearn 2016 Conference. For that presentation, I needed to show the following steps to demonstrate that the user actually consumed the video, and whether or not they learned from it, using a quiz question referring to a specific segment of the video:

  • Show how to collect video consumption using xAPI
  • Show how to collect test answers using xAPI
  • Show how to issue a query for the video and test answer, and tie them together

In this article, I’ll focus on the first two steps. I’ll discuss some of the decisions I made in how the data was collected and how it was formatted. And, of course, I’ll share some code with you to help you get started. The third step will be covered in an upcoming article.

Build your programming skills

To effectively use xAPI for an effort like this, you need to understand how you can construct the data to show a correlation between video consumption and quiz performance. In making this comparison, there are three practical outcomes:

  • The average user watches the video, but gets the answer incorrect. This could potentially indicate the video is not effective.
  • The average user does not watch the video, but does get the answer correct. This could indicate that the question was too simple.
  • The average user who did watch the video answers the question correctly, and the average user who did not watch the video answers the question incorrectly. This would tend to indicate that both the question and the video are effective.

By seeing the steps involved demonstrated with practical code, you’ll have a better understanding of how you can design your content to ensure you have the data you’ll need available and sent to your learning record store (LRS). Similarly, if your content isn’t performing as well as expected, you’ll see how xAPI can help you narrow down where the problems might be.

Getting started

In looking at how to demonstrate that the video is effective, I took a simple approach: Ask a question about a very specific piece of information presented in the video. In this case, I’m going to ask what the first animal is that you see in the video. It’s a silly question, agreed. But it illustrates the steps and concepts easily, and the answer is only 15.6 seconds into the video.

Note: I’m using the video Big Buck Bunny to demonstrate this. Big Buck Bunny is open source and shared under the Creative Commons license, so you can use it and share it at will. It’s also a pretty awesome video. (Editor’s note: The link will take you to a page where you must choose a version of the video suitable for your system. The files are large and take some time to download.)

First, I’ll need to know if the student played that bit of the video. Then, I need to ask the question. So, I’ll need to think about what information should be sent to the LRS when I capture that activity.

Capturing video playback

First things first: If we want to track video consumption, we’ll need some xAPI statements sent to reflect video playback. But we need to be smart about it. Do we want to send statements when the user hits play? Do we send statements when the user hits pause? And what data should be sent? To answer those questions, we first must ask ourselves, “How will I use the data I’m collecting?” and “How can I show that my videos are making a difference?”

Note: For the purposes of this discussion, I’m going to use vanilla HTML5 and the <video> tag. Using players such as JW Player or Video.js will use many of the same concepts, although the code will vary a little—but not much.

Looking at this logically, it would be easy to send a statement that says, “Anthony played the video from the xx second mark.” Then I could send another statement when I hit pause: “Anthony paused the video at yy seconds.” But if I do that, when I pull the statements from the LRS, I must make sure they are in chronological order by when the event occurred. And I’d have to compare multiple statements. So, what if I sent the start and end times in a single statement? Then I only need to look at one statement at a time when I do the reporting. If the Play timestamp is greater than 15.6, or the pause is less than 15.6, I can disregard that statement. That’s easier on both the front and back sides of this. So, we’ll do that. As before, we are using a bit of ADL code too.

I’ll need to know when the user starts to play the video. That can be easily done by creating a variable with the start time:

// When the user hits Play...
vid.onplay = function() {
    playFrom = (vid.currentTime).toFixed(2);
}; // End onPlay()

This sets the variable playFrom to the time in the video when the user hit play. It also rounds it to the nearest hundredth of a second (e.g., 15.6012334 becomes 15.60).

Now that we have that piece, we can set up the onPause statement:

// When the user hits Pause...
// onpause also runs when the video ends
vid.onpause = function() {
    console.log("The user has paused the video");
    var statement = {
        "actor": {
            "mbox": "mailto:" + firstName + "@devlearn16.com",
            "name": firstName,
        "objectType": "Agent"
        },
        "verb": {
            “id": "http://adlnet.gov/expapi/verbs/Play",
            "display": { "en-US": "Video Played" }
        },
        "object": {
            "id": "http://example.com/bigbuckbunnyvid.html",
            "definition": {
                "name": { "en-US": "Big Buck Bunny Video" },
                "description": { "en-US": "sample description" }
            },
            "objectType": "Activity"
        },
        "result": {
            "extensions":{
                "http://example.com/xapi/period_start" : playFrom,
                "http://example.com/xapi/period_end" : (vid.currentTime).toFixed(2)
            }
        },
    };  //end statement definition
    ADL.XAPIWrapper.sendStatement(statement, function(){}); 
}; // End onPause()

Not much new here from what we’ve seen before. But look at the Result section, lines 23 through 27. Here, you’ll see I’m using extensions to send two keyed pairs: The period_start, paired with the playFrom variable we defined in onPlay(), and period_end, paired with the timestamp in the video when the user hit pause (both of which I’m also rounding to two decimal places). So, this statement will effectively say, “The user played the video from xx seconds to yy seconds.” By sending the statement on PAUSE and not on PLAY, we can get both the start and stop times in a single statement. This will be important later.

Tracking test responses

We know what parts of the video the student watched. Now there are two more things we need: what the student answered to the question and where in the video that answer is found. For this, we need to look at two things: building the quiz and sending the statement.

The easiest way to find out where the answer to the question is found is to simply add that to the answer when we send that statement. To build the quiz, I used the excellent xAPI Wrapper Tutorial from Tyler Mulligan as the basic starting point. And I built the quiz using a simple form:

<form onsubmit="submission()">
    <label> What is the first animal you see in the video?</label>
    <label><input type="radio" name="question1" ts="15.6" /> Flying Squirrel
    </label>
    <label><input type="radio" name="question1" ts="15.6"/> Bunny</label>
    <label><input type="radio" name="question1" ts="15.6"/> Bird</label>
    <label><input type="radio" name="question1" ts="15.6"/> Butterfly</label>
    <br />
    <input type="submit" />
</form>

I ask a simple question: “What is the first animal you see in the video?” Then, for each answer, there are two attributes that really matter: the answer given, and where in the video the answer can be found (denoted here by the variable “ts”).

When the user selects an answer, a function is called to send the statement:

function answered(question, answer, ts) {        
    var statement = {
        "actor": {
            mbox": "mailto:" + firstname + "@devlearn16.com",
            "name": firstname,
            "objectType": "Agent"
        },
        "verb": {
            "id": "http://adlnet.gov/expapi/verbs/answered",
            "display": { "en-US": "answered" }
        },
        "object": {
            "id": "http://example.com/xapi/quiz_tracker",
            "definition": {
                "name": { "en-US": "xAPI Video Quiz" },
                "description": { "en-US": 
                "Correlating quiz answers to video consumption" }
            },
            "objectType": "Activity"
        },
        "result": {
            "response" : answer,
            "extensions": {
                "http://example.com/xapi/location" : ts
            }
        }
    };
    ADL.XAPIWrapper.sendStatement(statement, function(){}); 
}

Same as with the video, most of this looks familiar by now. But, again, notice the Result section. The response given is a keyed pair, with the answer the user selected as the value. Also, I’m using another extension recording the location of the answer in the video. In this case, another keyed pair with the ts variable sent as the value.

So, when the user selects, for example, “Bunny” as the answer, the page will build a statement that says “This user answered Bunny to this question. And you can find the answer in the video at 15.6 seconds.”

In conclusion…

We’ve started collecting data on who is watching which parts of the video. We’re also collecting their answers on the quiz, and when in the video you can find the correct answer to that question. Next, we need to look at how to pull this information back out of the LRS and start to show whether the users who got the question correct are actually watching that part of the video or not. That’s when the fun starts.

Because this is what xAPI was built for: real-time analytics on how your users are consuming and applying your content! No need for a dedicated reporting platform. The opportunities are wide open! This could mean content can tailor itself to how your students have done in previous classes. This could mean content tailors itself to how other students are doing in that same class. This could mean reporting on student progress in a course as you’re watching them take it.

And that means sending some xAPI queries!