Forum Discussion
Video Played, Completed… (Custom xAPI)
My customers love to track students' media use, but the video player that Articulate offers in Storyline 360 doesn't send xAPI, yet. Nor does the video player have any controls to which we can attach a trigger. However, Storyline does have a couple of other methods to track media activity. Read on!
Prerequisites: For this example, you'll need a slide with video, either linked or embedded. You must know the length of the video, in seconds.
Video Played
If the student clicks anywhere on a video, even with the media player controls hidden, then the media begins playing. So, although we can't attach triggers to the media player controls, we can still track that generic click.
- In the slide with the video, select the video.
- Create a new trigger.
- In the Trigger Wizard, change the Action to More > Adjust Variable.
- Change the Variable to
VideoId, and enter a value, such asvideo01. - Click the OK button.
- Open the Trigger Wizard again and change the Action to More > Adjust Variable, again.
- Change the Variable to
VideoName, and enter a value, ideally a string that matches the title of the video. A good practice is to add a prefix like "video 1.02 - " to help sort and filter the objects in the LRS. - Click the OK button.
- Open the Trigger Wizard again and change the Action to More > Adjust Variable, again.
- Change the Variable to
VideoLength, and enter the length as a number, in seconds. - Click the OK button.
- Open the Trigger Wizard again and change the Action to More > Send xAPI Statement.
- In the Statement, click + xAPI….
- In the xAPI Statement Editor, replace the JSON code with this:
{ "verb": {
"id": "https://w3id.org/xapi/video/verbs/played",
"display": {"en-US": "played"}},
"object": {
"objectType": "Activity",
"id": "%Project.ActivityId%/%Slide.Id%/%VideoId%",
"definition": {
"name": {"en-US": "%VideoName%"},
"type": "https://w3id.org/xapi/video/activity-type/video"}},
"context": {
"contextActivities": {
"parent": [{
"objectType": "Activity",
"id": "%Project.ActivityId%/%Slide.Id%",
"definition": {
"name": {"en-US": "%Project.SlideTitle%"},
"type": "http://adlnet.gov/expapi/activities/module"}}],
"grouping": [{
"objectType": "Activity",
"id": "%Project.ActivityId%",
"definition": {
"name": {"en-US": "%ActivityName%"},
"type": "http://adlnet.gov/expapi/activities/course"}}]},
"extensions": {
"https://w3id.org/xapi/video/extensions/length": "%VideoLength%"}}}
- In the xAPI Statement Editor, click the OK button.
- In the Trigger Wizard, change the When to Mouse Events > User clicks.
- Change the Object to the selected video.
- In the Trigger Wizard, click the OK button.
- In the Slide palette (usually docked to the right side of the window), in the Triggers section, click the Manage project variables button.
- In the Variables dialog box, double-click the Default Value of
ActivityName, and enter the name of your course (one of the only values for which Storyline doesn't have a system variable, like%Project.ActivityId%). - Click the OK button.
If you already entered triggers and variables, then this trigger will inherit the default values from that earlier action.
Video Completed
Storyline does have a media event that we could use to track if the video reached its end, Media completes.
- In the slide with the video, select the video.
- Create a new trigger.
- In the Trigger Wizard, change the Action to More > Send xAPI Statement.
- In the Statement, click + xAPI….
- In the xAPI Statement Editor, replace the JSON code with this:
{ "verb": {
"id": "http://adlnet.gov/expapi/verbs/completed",
"display": {"en-US": "completed"}},
"object": {
"objectType": "Activity",
"id": "%Project.ActivityId%/%Slide.Id%/%VideoId%",
"definition": {
"name": {"en-US": "%VideoName%"},
"type": "https://w3id.org/xapi/video/activity-type/video"}},
"context": {
"contextActivities": {
"parent": [{
"objectType": "Activity",
"id": "%Project.ActivityId%/%Slide.Id%",
"definition": {
"name": {"en-US": "%Project.SlideTitle%"},
"type": "http://adlnet.gov/expapi/activities/module"}}],
"grouping": [{
"objectType": "Activity",
"id": "%Project.ActivityId%",
"definition": {
"name": {"en-US": "%ActivityName%"},
"type": "http://adlnet.gov/expapi/activities/course"}}]},
"extensions": {
"https://w3id.org/xapi/video/extensions/length": "%VideoLength%"}}}
- Click the OK button.
- In the Trigger Wizard, change the When to Control Events > Media completes.
- Change the Media to the selected video.
- Click the OK button.
If you've entered this trigger on the same slide as the “played” action and the slide has no other videos, then this trigger should inherit the variables from the other.
3 Replies
- NedimCommunity Member
A similar question came up in the community a couple of years ago, and I think the discussion could still be relevant today. I recommend reviewing that thread, as it provides useful patterns you can apply to your current project. xAPI for user pausing and playing video
In my implementation, I rely on the video element’s ontimeupdate event as the core mechanism for tracking playback. This event fires multiple times per second while the video is playing, which allows very granular control. Using this, you can decide exactly when to update Storyline variables or trigger xAPI statements. For example:
- When the video starts playing
- When it is paused
- When it reaches the end
- When playback crosses specific timestamps
- Or even how many times the learner presses Play or Pause
By combining these event triggers with xAPI, you can build detailed tracking such as:
- Counting how many times the learner paused
- Detecting whether they reached a specific point in the timeline
- Sending periodic "heartbeat" statements during playback
- Marking the video as completed once it ends
- Logging specific user interactions like seeking or skipping
This approach avoids flooding your LRS with statements and gives you precise control over when information is sent. The key idea is that ontimeupdate gives you high-resolution timing control, but you should only send xAPI at meaningful moments (e.g., play/pause events, checkpoints, or timed intervals), not on every update tick.
If you structure your script around these events, you can create a robust and efficient tracking system that captures any type of interaction you need—play counts, pauses, rewinds, timestamp milestones, or full progression tracking similar to YouTube analytics.
- ThomasTShaffnerCommunity Member
Very cool!
My contributions in this community tend to focus on out-of-the-box solutions for xAPI tracking of student performance, while building-in conformance to make the measurements most effective in our LRS.
The solution I gave uses the W3ID Video Profile to loosely detect and report “played” and “completed” actions, as well as total video “length”. Your solution offers more precise detection, and — as you suggest — the potential to measure many results too, including “time” or percent “progress” within the video at the instance of each action.
If you could add a routine to your JavaScript to extract a timestamp, then the followers of this thread could use it to report it using a result.extension inserted in the custom xAPI…
"result": {
"extensions": {
"https://w3id.org/xapi/video/extensions/time": %VideoTime%
}
},Thanks for your follow-up. 😄
- NedimCommunity Member
Yes, we can report virtually any video interaction as long as we have JavaScript supporting it. For example, we can create a series of video events that send xAPI statements when specific actions occur in JavaScript, such as onplay, onpause, onended, onseeked, ontimeupdate, and so on.
I was particularly happy with the “seeked” event, because it allows us to track both the from and to timestamps directly in JavaScript. Similarly, the “progressed” event can be configured to fire at specific intervals, such as every 5% of video completion or at any percentage milestone, giving precise reporting on how far learners have watched the video.
Screenshots shown are taken from the Verocity LRS account, demonstrating how the events are captured and reported.
Related Content
- 4 months ago
- 4 months ago