Menu

D41. A web target beside SCORM 1.2: one build, a module for a plain web server (14 Oct 2026)

The first public version needs courses shown on the marketing site (free navigation, nothing kept), an online review with comments, and publication on Elabora’s servers. All three are the same thing to the core: a module served as static files, without an LMS. One build chain keeps two targets; the hosting (Cloudflare R2 and D1, a separate content domain, EU jurisdiction), accounts and the review’s hooks come later and build on this.

  • The target. elabora build --target web (MCP elabora_build target: "web", the engine’s build request target, @elabora/node build({ target: 'web', web })) writes dist/<course>-<version>-web/, a folder to serve as it is, and its ZIP (both by default). Everything the SCORM package holds but imsmanifest.xml; no LMS profile, suspend data budget, package limit or forbidden extension applies (validateProject takes target: 'web'), file names stay portable (they are URLs). Still deterministic (D8): the same course and settings give the same bytes. --profile is refused with it, the web settings without it. The report and the proof dossier say target: "web" and the settings; the pending human gate is a trial at the published address instead of the LMS smoke test.
  • Progress: local (default) or none. The player takes a third adapter (packages/player/src/web.ts) when the course data says delivery.target: "web", and then never looks for an LMS: local keeps the lesson location, status, score and suspend data in localStorage under elabora:web:<course id> (the same encoding and permanent keys as in an LMS, D4, so a learner resumes across updates), never the interactions or the session time (an LMS’s journal); none reads and writes nothing, the narration’s preferences included, and a reload starts again. Neither shows a banner nor Quit (no platform to go back to). SCORM packages and the preview’s standalone adapter (sessionStorage, its banner) are unchanged.
  • Navigation: authored (default) or free. free is applied by the compiler, in the course data, so the player has no second set of rules: navigation.mode: "free" (every screen reachable from the outline), lockUntilComplete: false (Next, Continue and narrated steps wait for nothing: no activity, visited controls or narration to finish first), the outline shown, and the narrations’ seek: "seen" becomes free (none, a timeline without a bar, stays). Events, visited controls and conditions still work as written; they no longer hold the learner back.
  • Defaults: a publication. local + authored: a module built for the web without a word is the course as designed, and a learner who comes back finds where they left. The locks are pedagogical choices (an assessment after its practice, a narration heard before going on), so opening them is a decision someone takes, never a default; losing progress silently is worse than keeping it in the learner’s own browser (nothing leaves it; no account, no cookie, no server). A showcase says --progress none --navigation free (nothing kept on a marketing site, a visitor goes where they like); a review link will say --navigation free --noindex, its progress to choose with the review.
  • The page. index.html gets the course’s title and description (<meta name="description">, Open Graph og:title, og:description, og:type, a Twitter card), --noindex (<meta name="robots" content="noindex">), --url (the absolute address of the module: <link rel="canonical">, og:url; an address without a file name is the module’s folder), and --image (a PNG, JPEG or WebP copied as share.<ext>, og:image absolute with --url). The course has no thumbnail: the app draws one per project in its data folder (<data>/thumbnail.png, machine-local, never in the course), so the build takes the image it is given rather than invent one; E_WEB_IMAGE, E_WEB_URL, W_WEB_IMAGE_WITHOUT_URL (sharing previews only read absolute addresses).
  • What hosting must give. One origin per publisher at least (localStorage is per origin: two courses with the same id on one origin share their progress; a course’s pages could read another’s), HTTPS, and the folder served as it is; X-Robots-Tag: noindex on a whole review publication is the host’s (the free objects’ frames are pages of their own). Revisit if: the review needs progress kept per reviewer, or publication needs progress on the server (accounts): a fourth adapter beside local, the course data unchanged; or the format gets a sharing image or a short description of its own (course.json share). Amended on 14 Oct 2026 (the privacy policy’s drafts): with --progress local the learner is told, quietly and in the module’s language, that the progress stays in this browser, and may clear it: a last line of the outline (and of the page an exit leaves), « Votre progression reste dans ce navigateur. » and « Effacer ma progression », which asks once (« Effacer » / « Annuler »), then ends the visit without saving it, removes elabora:web:<course id> and starts the module again (labels progressKept, clearProgress, clearProgressConfirm, clearProgressYes, cancel, in the seven languages). It is in the player, as the adapter it resets is: player.js and player.css change for every build, and nothing else does; a module with showMenu: false says it only when the visit ends. Amended on 29 Oct 2026 (the founder’s policy for handles, elabora-cloud C33): the progress kept with local is dated at each save (savedAt, milliseconds, beside the kept elements) and forgotten a year after the last one: the adapter removes elabora:web:<course id> and starts afresh (WEB_PROGRESS_KEPT_MS). Each visit saves, so each visit renews it. Elabora’s hosting enforces the same year for everything an origin keeps, whatever player runs there, with a cookie of its own and Clear-Site-Data (C33); the player’s rule holds on any other server too. A module on Elabora’s hosting may meet a new address when its author changes their handle: a new origin, so learners start again there (no redirect, the founder’s decision).