RevLearning

SCORM and xAPI for Unity content, behind one API

Status
Available
Type
Library
Platform
Unity 6000.3+ · WebGL for LMS deployment · v1.0, API frozen
Price
Licensed — enquire
Built with
C# · SCORM 1.2 / 2004 · xAPI · WebGL

What it is

A bridge between your Unity content and the LMS. Your content decides what happened; RevLearning reports it; the LMS records it.

What it isn’t

A learning management system, an authoring tool, or a course template. It has no opinion about what your content teaches.

Version 1.0 — the API is frozen

Released 2026-10-02. Everything in it landed as a correctness and hardening pass after a full review, so several fixes change behaviour a course may have been relying on — the changelog’s Changed and Fixed sections are worth reading before upgrading a live course rather than after.

What the release added, in the order a course author would meet it:

  • CompletionStatus and SuccessStatus separately, so SCORM 2004’s two axes can be reported independently instead of collapsed into one.
  • Cmi.Entry, Cmi.Credit and Cmi.PassingScore — whether this launch is a resume, whether it counts, and the score the LMS treats as a pass.
  • PassCourseAsync and FailCourseAsync, for the two endings most courses actually need.
  • xAPI queue control — PendingStatementCount, FlushPendingAsync and ClearPendingStatements, so you can see and drive what has not been delivered yet.
  • XApiActor.FromLmsLearner, so xAPI identifies the learner the LMS reported rather than one you invented.

One API per family

Content written against ILmsRuntime and IScormDataModel runs under SCORM 1.2 or SCORM 2004 without being rewritten. xAPI sits alongside them through IXApiRuntime.

Your code depends on RevLearning rather than on any one standard, and certainly not on any one LMS.

_runtime = await RevLearningRuntime.InitializeAsync(version);
if (_runtime?.Cmi == null) return;   // not connected — handle it gracefully

_runtime.Cmi.LessonLocation = "Module1/Scene1";
_runtime.Cmi.ScoreMax       = 100;
_runtime.Cmi.ScoreRaw       = 85;
_runtime.Cmi.LessonStatus   = LessonStatus.Completed;

if (!await _runtime.CommitAsync())
    Debug.LogError($"progress was not saved: {_runtime.DescribeLastError()}");

Develop without an LMS

An editor stub runtime is used automatically in the Editor and on non-WebGL platforms, so learning flows can be built and tested without standing up a course package and uploading it somewhere first.

The parts that are harder than they look

Suspend data. Learner state is stored as JSON against the standard’s suspend_data size limit, compressed to fit. A payload that still will not fit is refused and reported rather than trimmed — because a trimmed payload will not deserialize, and writing it would destroy the last save that did. Failing loudly is the only safe behaviour, and it is the one most implementations get wrong.

Error visibility. LastError mirrors the SCORM GetLastError value and reflects the most recent call only, so it is checked after the write you care about rather than once at the end of a batch. A write RevLearning blocks before it reaches the LMS does not refresh it — those are reported as warnings instead, because the LMS was never asked. The documentation is explicit about this, including the trap: a single LastError check after several writes can read clean precisely when writes are being discarded.

xAPI delivery. A FIFO queue with exponential-backoff retry and persistence — PlayerPrefs, or IndexedDB on WebGL — so statements survive a flaky connection and a closed tab.

Resumable sessions, exit and resume control, and a page helper that commits progress when the tab closes.

Documentation

A full MkDocs site, same as the other products: getting started, the mental model, both SCORM standards and what actually differs between them once a real LMS is reading, xAPI delivery, WebGL, packaging, the editor tooling, and the conformance results.

Read the documentation ↗ (opens in a new tab)

Packaging

A single-SCO imsmanifest.xml builder, post-build packaging, and an LMS validator window.

It has been run against a real LMS

Offline tests cannot answer whether an LMS accepts your package or agrees with how you report learner state. That needs a live run, and it was the last unverified thing about the SCORM path.

Both standards were driven end to end on SCORM Cloud, reading the actual LMSSetValue / SetValue traffic out of the registration debug log. Nineteen steps, eighteen executed on both standards, every one mapped to a control in the shipped validation panel — several of which had to be built, because the checklist was asking for things the panel could not yet do.

The nineteenth is the interesting one. Step 17 — writes after finish — is not reachable on that platform, because SCORM Cloud navigates the window to its return URL about a second after Terminate, unloading the SCO before a post-finish write can be attempted. That is recorded as not run, with what covers it instead, rather than quietly counted as a pass.

Some of what the live runs caught:

  • On SCORM 1.2 a pass was being overwritten with completed, following the finish sequence the FAQ recommends. Set Passed then Set Completed now leaves the pass standing on both standards.
  • An objective score of 20 was once recorded as a pass. Writing an objective score now writes score elements only — no success, no completion.
  • cmi.score.scaled is derived by SetScore; setting the three score properties directly does not derive it, and a run against a panel that did so measured scaled at 403, value not initialized.
  • PassingScore comes back null, because the in-box packer emits no mastery score. That is a documented limitation, confirmed in the run rather than assumed away.

Package validation runs in CI on every push, against packages the EditMode suite generates through the shipped packaging code. It checks what an importer inspects before it will accept a package: manifest well-formedness, identifier uniqueness, identifierref resolution, href safety, declared-versus-present file parity, the launch file and the injected page helper.

It is not schema validation and it is not the ADL Test Suite. It says nothing about runtime behaviour. The full record, including the steps and what each one guards, is in Tests~/CONFORMANCE.md.

Compatibility, stated honestly

The declared floor is Unity 6000.3, because that is the version CI holds. Every commit runs the full EditMode suite, the JavaScript suites and the package validator on it.

2021.3 LTS almost certainly works, and is not claimed. It was exercised by hand — 336/336 EditMode tests, a WebGL player and a SCORM package at 23/23 on the validator, recorded with its date. Nothing in the package uses a Unity API newer than 2020.2 or a C# feature above version 9.

What is missing is automation: Unity licence activation fails on 2021.3 in CI with the credentials that work on 6000.3, so there is no 2021.3 leg to hold that result on the next commit. A claim held up by a memory is not a support commitment, and passing once is not the same as being supported.

So the Package Manager will show a compatibility warning on 2021.3 and you would be installing deliberately. If that matters to you, say so — lowering a declared floor is an additive change, and restoring the claim with a real CI leg behind it is the right shape for a later release.