Self-hosted H5P without an H5P server. One web component plays .h5p files
from your own URLs or from disk, on static hosting you control: no Moodle, WordPress or Lumi, no
backend, no third-party service between your learners and your content.
Live player demo · Offline PWA app · H5P to SCORM · Emded H5P Lovable demo · VS Code extension
The library pack follows the H5P hub, and the Moodle's translations, updates every week. Compatibility says what a version number promises.
With a bundler (Vite, webpack 5, Rollup):
npm install @missing-elements/h5p-offline-player @missing-elements/h5p-runtime<h5p-player src="https://host.example/course.h5p"></h5p-player>import '@missing-elements/h5p-offline-player'
import { runtime } from '@missing-elements/h5p-runtime'
const player = document.querySelector('h5p-player')
player.runtime = runtime
player.addEventListener('xapi', (e) => console.log(e.detail.statement))The bundler emits the Service Worker and the runtime (the H5P core, a package of its own because
it is GPL-3.0) as assets, so there is nothing to copy. The page must be on https:// or
localhost, and a package from another origin needs CORS headers on its host. For exports
without their libraries, as h5p.com makes them, add libraries="pack": the
library pack, from jsDelivr or a copy on your
site.
Setting up the player covers a site without
a bundler and the Embed setup; the element's API
lists every attribute, property and event.
On Lovable, one prompt is enough:
Embed an H5P file using https://github.com/missing-elements/h5p-offline-player, with the .h5p
file uploaded in the chat. This example was built that way.
An H5P package is JavaScript, and the player runs it on the origin that serves the frame.
| Your site | Setup |
|---|---|
Needs the element's API (results to your backend, resume="host", cmi5, a file picker) and plays packages you made |
Install |
| Plays packages you did not make | Install with untrusted on them, or Embed |
| Has signed-in users | Embed, from a separate domain you run, or Install with every package untrusted |
| Cannot host a file (page builder, hosted CMS, LMS page) | Embed |
Install is the quick start above; untrusted on the element plays a package as data, none of
its own code running. Embed frames the player page that
@missing-elements/h5p-embed
writes, from a domain that holds nothing else, so a package never sees your site's cookies,
storage or page.
- Plays the packages people have. Large ones stream, and exports without their libraries, as h5p.com and h5p.org make them, play against a library pack of every content type the H5P hub serves.
- Plays packages you did not make with
untrusted: none of their code runs, and their content is checked by a port of H5P's own validator. - Reports results you can audit: xAPI statements that name the build of the package they came from, cmi5, resume, and a SCORM 1.2 package for Moodle, ILIAS, OpenOLAT, Chamilo, SCORM Cloud or Frappe LMS.
- No tracking: no account, cookies or telemetry, and nothing it stores leaves the learner's browser (privacy).
- Works offline, in the content's language: an installable app, and H5P's own buttons in any of more than fifty languages.
- Comes with tools:
h5p-verifychecks that a package plays,h5p-normalizemakes its video stream, andh5p-authorbuilds one from a short JSON source.
It is a player, not an editor, and it stores no results: its events go wherever your page sends
them. Coming from h5p-standalone? This player
reads the .h5p itself, with nothing to unzip, and
takes its options by the same names.
- Set up: Setting up the player · The element's API · Troubleshooting · Single-worker hosts and upgrades · Coming from h5p-standalone
- Packages: Libraries · Packages you did not make · Video that cannot stream · Verifying a package · The frame's CSP
- Results: Resume · Which build a learner completed · cmi5 · A SCORM package
- Policies: Compatibility · Privacy and data protection · Accessibility · Security
| Package | What it is |
|---|---|
@missing-elements/h5p-offline-player |
The <h5p-player> element and its workers |
@missing-elements/h5p-runtime |
The H5P core the player loads in its frame, GPL-3.0 |
@missing-elements/h5p-libraries |
The library pack: the runtime libraries of every content type the H5P hub serves |
@missing-elements/h5p-embed |
The player page as a static site, and a package as a SCORM zip |
@missing-elements/h5p-verify |
Plays a package headless and reports whether it works, or opens it in your browser |
@missing-elements/h5p-normalize |
Rewrites a package so its video streams |
@missing-elements/h5p-author |
Builds a package of any content type from a short JSON source |
@missing-elements/h5p-cmi5 |
Makes a page holding the element a cmi5 assignable unit |
| H5P Preview | Plays .h5p files in VS Code, and from Open VSX in Cursor and VSCodium |
Four skills for Claude Code and other agents that can read files and run commands, installed with skills:
npx skills add missing-elements/h5p-offline-playerh5p-author
turns a prompt or a lesson into H5P content of any of 28 types, chosen with you first, checks it
in the real player and opens it for you to play. h5p-player-setup puts H5P on a site,
h5p-normalize fixes video that will not stream, and h5p-verify checks that a package plays.
None needs a provider API key or an H5P server.
A pnpm workspace: see CONTRIBUTING.md, docs/development.md and AGENTS.md with design/, which hold the design and the reasons behind it. Report vulnerabilities privately, as SECURITY.md describes.
The player is MIT (LICENSE);
its Service Worker bundles zip.js, BSD-3-Clause
(NOTICE.md).
The H5P runtime the frame loads, the H5P core from h5p-php-library
and the script that boots it, is GPL-3.0 and published separately as
@missing-elements/h5p-runtime;
the player talks to it only over HTTP and postMessage, and its
NOTICE.md
says what serving it entails. Whether the copyleft reaches the page around the player is a legal
question this README does not answer.