SCORM 2004 Sequencing Configuration
This document explains how to configure SCORM 2004 sequencing in the scorm-again library.
Overview
SCORM 2004 sequencing allows you to control the flow of content in a SCORM package. It defines how learners navigate between activities, how activities are ordered, and how the status of activities is determined based on the status of their children.
The scorm-again library provides a comprehensive implementation of SCORM 2004 sequencing that follows the IMS Simple Sequencing Specification and SCORM 2004 Sequencing and Navigation book. The implementation includes:
- Complete activity tree management with hierarchical activities
- All navigation request types (start, continue, previous, choice, jump, exit, suspend, etc.)
- Sequencing rules (pre-condition, post-condition, and exit rules)
- Rollup rules for propagating status from child to parent activities
- Full sequencing process implementation including all subprocesses
- Time-based sequencing features (time limits, tracked time, etc.)
- Navigation request validation through
adl.nav.request_valid
Configuration
To configure SCORM 2004 sequencing, you need to provide a sequencing object in the settings when
creating a SCORM 2004 API instance:
import { Scorm2004API } from "scorm-again";
const api = new Scorm2004API({
// Other settings...
sequencing: {
// Sequencing configuration...
},
});
The sequencing object can contain the following properties:
activityTree: Configures the activity tree, which defines the hierarchy of activities in the SCORM package.sequencingRules: Configures the sequencing rules, which define how navigation between activities is controlled.sequencingControls: Configures the sequencing controls, which define general behavior for sequencing.rollupRules: Configures the rollup rules, which define how the status of parent activities is determined based on the status of their children.collections: Reusable manifest sequencing collections, supplied as a keyed record or an array whose entries haveid.hideLmsUiandauxiliaryResources: Package-level delivery UI data.autoRollupOnCMIChange: Compatibility behavior for immediate rollup. Leavefalsefor SCORM-conforming end-attempt transfer.eventListeners: Sequencing lifecycle callbacks such asonActivityDelivery,onRollupComplete, andonNavigationValidityUpdate.
Configuration Hooks
You can supply event listeners under sequencing to receive delivery and navigation-validity updates:
const api = new Scorm2004API({
sequencing: {
activityTree: {/* ... */},
eventListeners: {
onActivityDelivery: (activity) => launchSco(activity.id),
onNavigationValidityUpdate: (validity) => updateNavUI(validity),
}
}
});
Activity Tree
The activity tree defines the hierarchy of activities in the SCORM package. Each activity has an ID, a title, and can have child activities.
sequencing: {
activityTree: {
id: 'root',
title: 'Course',
children: [
{
id: 'module1',
title: 'Module 1',
children: [
{
id: 'lesson1',
title: 'Lesson 1'
},
{
id: 'lesson2',
title: 'Lesson 2'
}
]
},
{
id: 'module2',
title: 'Module 2',
children: [
{
id: 'lesson3',
title: 'Lesson 3'
},
{
id: 'lesson4',
title: 'Lesson 4'
}
]
}
]
}
}
Each activity can also have the following properties:
isVisible: Whether the activity is visible to the learner (default: true)isActive: Whether the activity is currently activeisSuspended: Whether the activity is suspendedattemptAbsoluteDurationLimit: Maximum time allowed for an attempt (ISO 8601 duration)activityAbsoluteDurationLimit: Maximum time allowed for the activity (ISO 8601 duration)beginTimeLimit: Time window start for availability (ISO 8601 datetime)endTimeLimit: Time window end for availability (ISO 8601 datetime)sequencingControls: Activity-specific sequencing controls (overrides global controls)sequencingRules: Activity-specific sequencing rulesprimaryObjective,objectives: Learning objectives with satisfaction, measure, and map trackingsequencingCollectionRefs,sequencingIdRef: One or more reusable sequencing collection IDs
Sequencing Rules
Sequencing rules define how navigation between activities is controlled. There are three types of sequencing rules:
preConditionRules: Rules that are evaluated before an activity is attempted.exitConditionRules: Rules that are evaluated when an activity is exited.postConditionRules: Rules that are evaluated after an activity is completed.
sequencing: {
sequencingRules: {
preConditionRules: [
{
action: 'skip',
conditionCombination: 'all',
conditions: [
{
condition: 'completed',
operator: 'not'
}
]
}
],
exitConditionRules: [
{
action: 'exitParent',
conditions: [
{
condition: 'completed'
}
]
}
],
postConditionRules: [
{
action: 'continue',
conditions: [
{
condition: 'completed'
}
]
}
]
}
}
Each rule has an action, an optional conditionCombination (which can be all or any), and an
array of conditions. Each condition has a condition type, an optional operator (which can be
not), and optional parameters.
Sequencing Controls
Sequencing controls define general behavior for sequencing. These can be set globally or per-activity.
sequencing: {
sequencingControls: {
choice: true, // Allow choice navigation requests
choiceExit: true, // Allow choice requests to exit the current activity
flow: false, // Enable automatic flow to next activity
forwardOnly: false, // Restrict navigation to forward only
useCurrentAttemptObjectiveInfo: true, // Use current attempt's objective info
useCurrentAttemptProgressInfo: true, // Use current attempt's progress info
preventActivation: false, // Prevent activation of descendant activities
constrainChoice: false, // Constrain choice navigation
randomizationTiming: 'never', // When to randomize: 'never', 'once', 'onEachNewAttempt'
selectCount: null, // Number of children to select (null = all)
reorderChildren: false, // Allow reordering of child activities
selectionTiming: 'never', // When to select: 'never', 'once', 'onEachNewAttempt'
tracked: true, // Track activity attempt data
completionSetByContent: false, // Completion status set by content
objectiveSetByContent: false // Objective status set by content
}
}
Rollup Rules
Rollup rules define how the status of parent activities is determined based on the status of their children.
sequencing: {
rollupRules: {
rules: [
{
action: "completed",
consideration: "all",
conditions: [
{
condition: "completed",
},
],
},
{
action: "satisfied",
consideration: "all",
conditions: [
{
condition: "satisfied",
},
],
},
],
},
};
Each rule has an action, a consideration (which can be all, any, none, atLeastCount, or
atLeastPercent), optional minimumCount and minimumPercent values (for atLeastCount and
atLeastPercent considerations), and an array of conditions. Each condition has a condition
type and optional parameters.
Example
Here's a complete example of configuring SCORM 2004 sequencing:
import { Scorm2004API } from "scorm-again";
const api = new Scorm2004API({
// Other settings...
sequencing: {
activityTree: {
id: "root",
title: "Course",
children: [
{
id: "module1",
title: "Module 1",
children: [
{
id: "lesson1",
title: "Lesson 1",
},
{
id: "lesson2",
title: "Lesson 2",
},
],
},
{
id: "module2",
title: "Module 2",
children: [
{
id: "lesson3",
title: "Lesson 3",
},
{
id: "lesson4",
title: "Lesson 4",
},
],
},
],
},
sequencingRules: {
preConditionRules: [
{
action: "skip",
conditionCombination: "all",
conditions: [
{
condition: "completed",
operator: "not",
},
],
},
],
exitConditionRules: [
{
action: "exitParent",
conditions: [
{
condition: "completed",
},
],
},
],
postConditionRules: [
{
action: "continue",
conditions: [
{
condition: "completed",
},
],
},
],
},
sequencingControls: {
enabled: true,
choiceExit: true,
flow: true,
forwardOnly: false,
useCurrentAttemptObjectiveInfo: true,
useCurrentAttemptProgressInfo: true,
preventActivation: false,
constrainChoice: false,
rollupObjectiveSatisfied: true,
rollupProgressCompletion: true,
objectiveMeasureWeight: 1.0,
},
rollupRules: {
rules: [
{
action: "completed",
consideration: "all",
conditions: [
{
condition: "completed",
},
],
},
{
action: "satisfied",
consideration: "all",
conditions: [
{
condition: "satisfied",
},
],
},
],
},
},
});
This configuration creates a course with two modules, each with two lessons. It defines rules for skipping completed activities, exiting to the parent when an activity is completed, and continuing to the next activity when an activity is completed. It also defines rules for determining when a parent activity is completed or satisfied based on its children.
Navigation Requests
The SCORM 2004 API supports the following navigation requests through adl.nav.request:
start- Start the sequencing session from the rootresumeAll- Resume a suspended sessioncontinue- Navigate to the next activity in the sequenceprevious- Navigate to the previous activity in the sequencechoice- Navigate to a specific activity (with{target=<activityId>}syntax)jump- Jump to a specific activity (with{target=<activityId>}syntax)exit- Exit the current activityexitAll- Exit all activities and end the sessionabandon- Abandon the current activity without recording resultsabandonAll- Abandon all activitiessuspendAll- Suspend all activitiesretry- Retry the current activityretryAll- Retry from the root activity_none_- No navigation request (default)
Example usage:
// Simple navigation
api.SetValue("adl.nav.request", "continue");
api.Terminate("");
// Choice navigation with target
api.SetValue("adl.nav.request", "{target=lesson3}choice");
api.Terminate("");
// Check navigation validity
const canContinue = api.GetValue("adl.nav.request_valid.continue");
const canChooseLesson3 = api.GetValue("adl.nav.request_valid.choice.{target=lesson3}");
Commit() checkpoints SCO runtime data but does not end the activity attempt or process the
navigation request. Content-driven sequencing runs when the SCO calls Terminate().
Implementation Details
The sequencing implementation follows the algorithms defined in the SCORM 2004 Sequencing and Navigation book:
- Overall Sequencing Process (OP) - Main entry point for all sequencing requests
- Sequencing Request Process (SB.2.12) - Validates and processes navigation requests
- Flow Subprocess (SB.2) - Handles flow traversal through the activity tree
- Choice Sequencing Request Process (SB.2.9) - Handles choice navigation
- Sequencing Rules Check Process (UP.2) - Evaluates sequencing rules
- Rollup Process (RB.1.4) - Propagates status from children to parents
- Delivery Request Process (DB.1.1) - Prepares activities for delivery
The implementation maintains full state tracking, supports all navigation modes, and properly validates all requests according to the SCORM 2004 specification.
LMS Integration Requirements
What the LMS Must Provide
To enable SCORM 2004 sequencing, the LMS must extract and provide the following data from the SCORM package's imsmanifest.xml file:
1. Activity Tree Structure
The LMS must parse the <organization> element and its nested <item> elements to build the activity tree:
<!-- Example from imsmanifest.xml -->
<organization identifier="ORG-001">
<title>Sample Course</title>
<item identifier="ACT-001" identifierref="RES-001">
<title>Module 1</title>
<item identifier="ACT-002" identifierref="RES-002">
<title>Lesson 1.1</title>
</item>
<item identifier="ACT-003" identifierref="RES-003">
<title>Lesson 1.2</title>
</item>
</item>
</organization>
This should be converted to:
activityTree: {
id: 'ORG-001',
title: 'Sample Course',
children: [
{
id: 'ACT-001',
title: 'Module 1',
children: [
{
id: 'ACT-002',
title: 'Lesson 1.1'
},
{
id: 'ACT-003',
title: 'Lesson 1.2'
}
]
}
]
}
2. Sequencing Rules
The LMS must parse <imsss:sequencing> elements within each <item>:
<item identifier="ACT-001">
<imsss:sequencing>
<imsss:sequencingRules>
<imsss:preConditionRule>
<imsss:ruleConditions conditionCombination="all">
<imsss:ruleCondition condition="satisfied" operator="not"/>
</imsss:ruleConditions>
<imsss:ruleAction action="skip"/>
</imsss:preConditionRule>
</imsss:sequencingRules>
</imsss:sequencing>
</item>
3. Sequencing Controls
Parse <imsss:controlMode> elements:
<imsss:controlMode
choice="true"
choiceExit="true"
flow="false"
forwardOnly="false"/>
4. Rollup Rules
Parse <imsss:rollupRules> elements:
<imsss:rollupRules>
<imsss:rollupRule childActivitySet="all">
<imsss:rollupConditions conditionCombination="any">
<imsss:rollupCondition condition="satisfied"/>
</imsss:rollupConditions>
<imsss:rollupAction action="satisfied"/>
</imsss:rollupRule>
</imsss:rollupRules>
Data Flow
-
Package Import: When a SCORM 2004 package is imported, the LMS must:
- Parse the imsmanifest.xml file
- Extract the organization structure
- Extract all sequencing information
- Store this data in a format that can be provided to the API
-
API Initialization: When launching a SCO, the LMS must:
- Retrieve the stored sequencing configuration
- Restore persisted global objectives and sequencing state before exposing the API
- Pass it to the Scorm2004API constructor via the
sequencingsetting - Optionally provide the list of valid SCO IDs via
scoItemIds
-
Runtime Navigation: During execution:
- The SCO sets
adl.nav.requestand callsTerminate() - The API ends the attempt, commits, processes sequencing, and emits
onActivityDelivery - The LMS launches the delivered activity and resets SCO-local runtime data for the new SCO
- The SCO sets
Example LMS Integration
// 1. Parse manifest and extract sequencing data (done during package import)
const sequencingData = parseManifest(imsmanifestXML);
// 2. Initialize API with sequencing configuration
const api = new Scorm2004API({
lmsCommitUrl: 'https://lms.example.com/api/commit',
renderCommonCommitFields: true,
// Provide the extracted sequencing configuration
sequencing: {
...sequencingData,
autoRollupOnCMIChange: false,
eventListeners: {
onActivityDelivery: (activity) => launchSco(activity.id),
onActivityUnload: (activity) => unloadSco(activity.id),
onNavigationValidityUpdate: (validity) => updateNavUI(validity),
},
},
// Optional: provide list of valid SCO IDs for validation
scoItemIds: extractScoIds(sequencingData),
// Optional: provide runtime SCO validation
scoItemIdValidator: (scoId) => {
return lmsDatabase.validateScoId(scoId);
}
});
Endpoints Required
The normal lmsCommitUrl persists SCO runtime data and, with structured commits, the global-objective snapshot. An LMS that persists complete sequencing state also supplies the sequencingStatePersistence callbacks, typically backed by registration-scoped load/save/delete endpoints.
- Track Navigation Events: Store navigation history for reporting
- Validate SCO Access: Ensure learners can only access SCOs according to sequencing rules
- Update UI: Reflect the current navigation state in the LMS interface
Runtime Data Storage Considerations
Global Objectives
SCORM 2004 introduces the concept of global objectives - objectives that are shared across multiple SCOs within a package. The LMS must handle these specially:
-
Identifying Global Objectives:
- Global objectives are defined in the manifest with
<imsss:mapInfo>elements - Map targets are discovered from
primaryObjectiveandobjectivesin the activity tree globalObjectiveIdsis only for host-defined rows exposed directly to CMI outside manifest maps- When a SCO sets data for a global objective, it affects all SCOs that reference it
- Global objectives are defined in the manifest with
-
Storage Requirements:
// Normal objective data is stored per SCO:{"scoId": "SCO-001","cmi": {"objectives": {"0": {"id": "local-obj-1","success_status": "passed","score": { "scaled": 0.85 }}}}}// Global objectives must be stored separately and shared:{"courseId": "COURSE-001","globalObjectives": {"global-obj-1": {"satisfiedStatus": true,"satisfiedStatusKnown": true,"normalizedMeasure": 0.85,"normalizedMeasureKnown": true}}} -
Implementation in scorm-again:
- The sequencing engine resolves manifest objective maps from the activity tree
- End Attempt transfers SCO runtime data to activity tracking and writes eligible objective maps
- With
renderCommonCommitFields: true, every SCORM 2004 structured commit includes a top-levelglobalObjectivessnapshot - Restore that snapshot with
restoreGlobalObjectiveSnapshot()before initial delivery
Suspend Data and Location
For sequenced courses, suspend data handling requires special attention:
- Suspend Data: When a learner suspends a SCO, the
cmi.suspend_datamust be preserved - Location: The
cmi.locationindicates where the learner left off - Entry State: The
cmi.entryvalue indicates if this is a new attempt or resume - Activity State: The sequencing engine tracks which activities are suspended
Attempt Management
Sequenced courses track attempts at multiple levels:
- Course-level attempts: Overall attempts at the entire course
- Activity-level attempts: Individual attempts at each activity
- Objective attempts: Attempts at satisfying objectives
The LMS should store:
- Attempt count per activity
- Attempt duration and absolute duration
- Success/completion status per attempt
- Whether an attempt is suspended or active
LMS Commit Data Handling
When the API sends commit data to the LMS, sequenced courses require special handling:
1. What Data is Sent
With renderCommonCommitFields: true, scorm-again sends a structured CommitObject that includes:
{
// Core commit data
successStatus: SuccessStatus; // Enumerated success status
completionStatus: CompletionStatus; // Enumerated completion status
totalTimeSeconds: number; // Total time in seconds
runtimeData: { // SCO-local CMI data
cmi: { /* all CMI data */ }
};
globalObjectives: { // Registration-scoped sequencing snapshot
[objectiveId: string]: GlobalObjectiveMapEntry
};
score?: ScoreObject; // Optional score data
// Optional metadata (if provided by LMS)
commitId?: string;
courseId?: string;
learnerId?: string;
learnerName?: string;
sessionId?: string;
activityId?: string;
}
The commit object does not contain the complete activity tracking tree. Persist that through
sequencingStatePersistence or explicit saveSequencingState() calls. The sequencing snapshot includes:
- Current activity ID
- Activity states (suspended, attempted, etc.)
- Sequencing request results
- Navigation history
adl.nav.request is processed inside the API at Terminate() and is not a separate structured
commit field.
2. Global Objectives Handling
The LMS must:
- Extract global objectives from the commit data
- Store them separately from SCO-specific data
- Make them available to other SCOs that reference them
- Load them when initializing any SCO that uses them
Example LMS implementation:
async function handleScormCommit(commitData) {
// 1. Save SCO-specific data
await saveScoData(commitData.scoId, commitData.runtimeData.cmi);
// 2. Store the synchronized global-objective snapshot as one registration value
await saveGlobalObjectives(courseId, commitData.globalObjectives ?? {});
// 3. Update sequencing state
await updateSequencingState(courseId, commitData.scoId, {
attemptCount: commitData.attempt,
isSuspended: commitData.runtimeData.cmi.exit === "suspend",
completionStatus: commitData.runtimeData.cmi.completion_status,
successStatus: commitData.runtimeData.cmi.success_status
});
}
// Before the first delivery, restore shared objectives directly into the API.
api.restoreGlobalObjectiveSnapshot(await loadGlobalObjectives(courseId));
3. Sequencing State Persistence
Provide a sequencingStatePersistence adapter to store the engine snapshot. For deterministic
resume, set autoLoadOnInitialize: false, await loadSequencingState(metadata), restore the last
commit's global-objective snapshot, and only then expose the API and start delivery:
const api = new Scorm2004API({
...settings,
sequencingStatePersistence: {
persistence,
autoLoadOnInitialize: false,
autoSaveOn: "commit",
},
});
api.restoreGlobalObjectiveSnapshot(persistedGlobalObjectives);
await api.loadSequencingState({ learnerId, courseId, attemptNumber });
window.API_1484_11 = api;
autoSaveOn defaults to "commit" and includes termination commits. Use "navigate" to save
post-navigation state, or "never" when the LMS controls every save explicitly.
API Reset Between SCOs
When to Reset the API
Each SCO needs a fresh SCORM communication session and SCO-local CMI model. A sequenced player may
keep one API instance and call reset() between delivered SCOs:
- Terminate the current SCO: Call
Terminate()which will commit any pending data - Reset the API: Call
api.reset()to clear the current state - Keep shared state:
reset()preserves the sequencing tree, tracking state, and global objectives - Initialize the new SCO: Load the new SCO's local runtime data, then let it call
Initialize()
What Gets Reset
When api.reset() is called:
- SCO-local CMI and ADL data are cleared
- The API returns to the pre-initialize state for the next SCO
- Sequencing tracking and global objectives are preserved
- Sequencing callbacks configured through
sequencing.eventListenersremain installed
Recommended Implementation
let hasDeliveredSco = false;
const eventListeners = {
onActivityDelivery: async (activity) => {
if (hasDeliveredSco) {
// The prior SCO has terminated before subsequent delivery events.
api.reset();
}
api.loadFromJSON(await loadScoData(activity.id));
hasDeliveredSco = true;
launchSco(activity.id);
},
};
Alternative Approach: Multiple API Instances
Some LMS implementations maintain separate API instances for each SCO, but they must explicitly share persisted sequencing state and global-objective snapshots between instances:
// Create a new API instance for each SCO
const apis = {};
function getApiForSco(scoId) {
if (!apis[scoId]) {
apis[scoId] = new Scorm2004API({
// ... settings ...
sequencing,
renderCommonCommitFields: true,
});
}
return apis[scoId];
}
The single-instance reset() flow is usually simpler for a sequenced player.
Important Notes
- The sequencing configuration must be provided when the API is initialized
- The API handles all sequencing logic internally based on the SCORM 2004 specification
- The LMS is responsible for actually launching/switching SCOs based on navigation events
- All navigation validation is handled by the API through
adl.nav.request_valid - Persist and restore the structured commit's
globalObjectivessnapshot across SCOs - Persist the activity tracking tree through
sequencingStatePersistence - The API should be reset between SCO launches to ensure clean state
Recent Enhancements (Sequencing)
- Per-target request validity:
- The library computes per-target maps for
choiceandjumpand emitsonNavigationValidityUpdatewith{ continue, previous, choice, jump, hideLmsUi, auxiliaryResources }. - It attempts to set
adl.nav.request_valid.choice/jumpmaps when writable; prefer the event payload for UI updates.
- The library computes per-target maps for
- New sequencing control:
stopForwardTraversalis honored when set (e.g., via a post-condition rule) to halt forward traversal through a cluster.