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, and no service of ours or H5P Group's between your learners and your content.
Live player demo · Offline app · H5P to SCORM · Lovable example · VS Code extension
Version 1 since October 2026; Compatibility says what a version number promises. Every week a job rebuilds the library pack from the H5P hub and the interface strings from Moodle's translations, opens a pull request when either changed, and plays every content type in the pack, trusted and untrusted.
- 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 the 53 content types 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 zip: SCORM 1.2, checked in Moodle, ILIAS, OpenOLAT, Chamilo, SCORM Cloud and Frappe LMS, or 2004, checked in Moodle and SCORM Cloud.
- No tracking: no account, cookies or telemetry, and nothing it stores leaves the learner's browser. The library pack comes from jsDelivr unless you serve a copy (privacy).
- Works offline, in the content's language: an installable app, and H5P's own buttons in English and 56 other languages.
- Tested in Chromium, Firefox and WebKit on every change, and in Safari on macOS and iOS by hand (browsers).
- Comes with tools:
h5p-verifychecks that a package plays,h5p-normalizemakes its video stream, andh5p-authorbuilds one from a short JSON source, which a Moodle site accepts as an upload too.
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.
With Vite or webpack 5; both packages ship TypeScript declarations:
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 files, 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. A package that
carries its libraries plays as it is; one exported without them, as h5p.com exports, needs
libraries="pack".
Packages you did not make, or a site with signed-in users: play every package untrusted, or
embed the player from a domain of its own, as
Which setup explains.
Setting up the player also covers a page without a bundler, Rollup and
esbuild, and the one-prompt install on Lovable;
the element's API lists every attribute, property and event.
- 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 |
Five skills for Claude Code and other agents that can read files and run commands, installed
with skills; add --skill <name> for one of them. None needs a provider API
key or an H5P server.
npx skills add missing-elements/h5p-offline-player| Skill | What it does |
|---|---|
h5p-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-verify |
Checks that a package plays, and opens it in your browser for you to try |
h5p-to-scorm |
Turns a package into a SCORM zip that an LMS plays, grades and resumes, and says what to set in yours |
h5p-player-setup |
Puts H5P on a website, from the player on the site itself or framed from a domain of your own |
h5p-normalize |
Rewrites a package whose video will not stream |
A chat that cannot install a skill can be given h5p-author as a link instead:
missing-elements.github.io/h5p/skill/ lists each
of its files at its full address, and each file its build fetches where npm cannot be reached,
from the release the build page runs.
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.