SPLICE iframe Protocol: Specification

If you read this as the implementer of a learning system that embeds smart learning content as iframes, you only need to care about the message protocol. You may find this starter code helpful.

If you read this as the implementer of smart learning content, which you would like to make available to learning systems, you may want to make use of this reference implementation that provides a friendly JavaScript interface for the message protocol.

Messages

These messages are supported:

The reportScoreAndState Message

The smart learning content sends this message whenever the activity’s state and/or score changes.

{ 
   subject: 'SPLICE.reportScoreAndState', 
   message_id: a unique string ID for this message,
   score: a floating-point number between 0 and 1
   state: an arbitrary JavaScript object
}

The learning system should update the user’s score and optionally store the state.

The getState Message

{ 
   subject: 'SPLICE.getState', 
   message_id: a unique string ID for this message,
   domain_id: an optional globally unique domain chosen by the content provider,
   content_id: an optional unique ID in the domain,
}

If the domain_id is not specified, it is implied to be the host domain that serves the iframe of this activity. If the content_id is not specified, it is implied to be the part of the iframe URL after the scheme, host, and port.

Use these IDs if you want to provide an identifier for the smart learning content that is more canonical than the iframe URL.

The getState message has a response:

{
    subject: "SPLICE.getState.response",
    message_id: the same ID as the request,
    state: the previously reported state,
    user_id: an optional unique and opaque user ID,
    user_model: an optional JSON object describing information about the user
        in a format that is specific to the embedding system 
        or that may be standardized in the future
    context_id: an optional unique and opaque ID of the context 
        (such as a s course assignment or ebook section)
}

If you do not support state restoration, reply to the getState message with a state of null.

The response may contain more information about the user that the smart learning content can interpret, such as their prior skill level. This information is not currently standardized.

If there is an error in the request, the response must contain, with key error, a JavaScript object

{
    code: a text string with an error code
    message: optionally, a text string with a humanly readable message
}

NOTE: We don’t use the lti.get_data, lti.put_data message from LTI for state storage because those data are described to be “short lived”

The sendEvent Message

This message informs about interactions with the smart learning content that are unrelated to scoring and state restoration. The learning system may choose to log or store them, and to make them available to interested parties, such as education researchers. The message content is not currently standardized.

{ 
   subject: 'SPLICE.sendEvent', 
   message_id: a unique string ID for this message,
   name: ...,
   data: ...,
   error: an optional error that may be of interest to the embedding page
}

An error contains, with key error, a JavaScript object

{
    code: a text string with an error code
    message: optionally, a text string with a humanly readable message
}

The lti.frameResize Message

The embedded iframe sends this message whenever its size changes. This allows the embedding page to display the iframe without scroll bars.

{ 
   subject: lti.frameResize', 
   message_id: a unique string ID for this message,
   height: ...,
   width: ... 
}

Protocol Implementation Notes

If you implement a smart learning system that embeds iframes with smart learning content, you need to be aware of a couple of issues with iframe communication.

When receiving a message from an iframe, and if you have more than one, you want to know which iframe sent it. Unfortunately, you cannot do the obvious and consult event.source.location.href. That’s a CORS violation. Instead, use code similar to the following:

function sendingIframe(event) {
  for (const f of document.getElementsByTagName('iframe'))
  if (f.contentWindow === event.source) return f
  return undefined
}

window.addEventListener("message", event => {
  let iframe = sendingIframe(event)
  let url = iframe.src
  . . .
})

When sending a message to an iframe, use

event.source.postMessage({
  message_id: event.data.message_id,
  subject: "SPLICE.getState.response",
  state: state,
}, "*");

with a "*" for the targetOrigin parameter. Otherwise your message may not be delivered:

Listen to the lti.frameResize messages and adjust the size of the iframe, using code such as the following:

Let newHeight = event.data.height
if ('chrome' in window) {
  const CHROME_FUDGE = 32 // to prevent scroll bars in Chrome
  newHeight += CHROME_FUDGE
}
if (iframe.scrollHeight < newHeight)
  iframe.style.height = newHeight + 'px'

JavaScript Reference Implementation

This section is addressed to authors of smart learning content, served as an HTML page, ready for inclusion as an iframe.

A reference implementation provides a simple JavaScript API that is easier to use than sending and receiving messages. You can use the JavaScript code as is, modify it for your situation, or provide your own messaging code.

The implementation supports multiple interactive elements on the page. Each of them has a location ID, which must be unique to the page. If you only have one element, simply provide an empty string as the ID.

The following calls are provided:

SPLICE.reportScoreAndState(location, score, state)
SPLICE.getState(location, callback)
SPLICE.sendEvent(location, name, data)
SPLICE.configure(params)

Call SPLICE.reportScoreAndState whenever an activity’s score changes.

If restoration of student work from a prior launch is desired, provide state information in each call to SPLICE.reportScoreAndState.

Call the SPLICE.getScores method when loading the page to retrieve the state information. Not all learning systems have the ability to save and restore state, so be prepared that this call may yield no information, or it may not even return. Call this method even if you are not interested in getting scores, to notify the learning system that your element is ready.

Optionally call SPLICE.configure before making any other calls. You can specify the following parameters:

The reference implementation emits lti.frameResize messages when the document size changes.

NOTE: We may want to standardize this API at some point in the future. Then we might want to replace the SPLICE identifier with something like org.cssplice.content.