Verwendung
- Installationsbefehl kopieren und das Quellpaket vor der Installation in der eigenen Agentenumgebung prüfen.
- Benötigte Werkzeuge, Zugangsdaten und Hosts im Original prüfen.
- Eingabe und gewünschtes Ergebnis beschreiben und die Ausgabe mit den Quellen abgleichen.
npx skills add https://github.com/ssheleg/xr-dev --skill quest-webxrVor dem Start
- Die Installation umfasst weder Modellabo noch API-Kontingent oder Dateizugriffsrechte.
- Sterne und Installationen zeigen Aktivität, keine unabhängige Qualitäts- oder Sicherheitsprüfung.
Aus dem Quell-Repository gerendert; Überschriften, Beispiele, Code, Tabellen, Links und Bilder bleiben erhalten.
WebXR and PWAs on Horizon OS
The web path trades native performance for a deploy that is a git push. It is the right choice for demos, catalogues, tools and anything that must also run outside a headset — and the wrong one when the frame budget is already tight.
Read references/runtime-delivery.md for session/capability negotiation, ordinary-screen fallback, assets, hosting and browser/PWA delivery checks.
For a whole-product roadmap or stage audit, use quest-lifecycle; a single technical task stays with this owner. If absent, identify the current stage, its evidence and the next prerequisite inline.
The order that avoids rework
- Build the site and test it in the headset's Browser first. A packaged PWA
uses the same rendering engine, so every compatibility and performance problem is visible before packaging.
- Debug it remotely — Chrome DevTools from the machine against the device,
over the Android platform tools. The Browser specifications page carries the user-agent string and the supported content sizes.
- Package only when the page is right.
Entering immersive mode like a real app
A PWA launched from the headset's library should not show a 2D landing page. Ask for the session as soon as the page has loaded:
const supported = await navigator.xr?.isSessionSupported('immersive-vr');
if (supported) {
const session = await navigator.xr.requestSession('immersive-vr');
await renderer.xr.setSession(session); // three.js
}A-Frame does the same through scene.enterVR() on renderstart. Meta's own snippets gate this on window.getDigitalGoodsService !== undefined — present inside a packaged PWA, absent in the plain Browser — so the page still behaves when someone opens the URL on a laptop.
Which framework
| Choice | When |
|---|---|
IWSDK (npm create @iwsdk@latest) | Meta's recommended path for both screen-based 3D and immersive WebXR; ships the testing story (IWER on desktop) and build/deploy guides |
| three.js | an existing three.js codebase, full control of the renderer |
| A-Frame | declarative scenes, fastest to a prototype |
Meta also ships the hz-iwsdk-webxr skill for the IWSDK path — use it for IWSDK-specific work; this skill decides whether the web lane is the right one and owns the packaging and distribution seam.
Packaging as a PWA
Step by step, including the Digital Asset Links step that decides whether an immersive PWA launches at all: references/pwa-packaging.md.
npm install --global @meta-quest/bubblewrap-cli # verified at 1.24.1, Node 18+
bubblewrap --versionPrerequisites, in the order Bubblewrap will need them:
- the site served over HTTPS with a valid Web App Manifest;
- an app created in the Developer Dashboard — its **Meta Horizon Application
ID** is required if the PWA uses in-app purchases;
- a signing key, or the details to create one during
init; - a headset in Developer Mode for sideload testing.
On first run Bubblewrap offers to download a matching JDK and Android command-line tools; let it, unless the environment manages those itself. During Quest initialisation it asks whether to include Horizon Billing — answer with the Application ID ready if IAP is in scope, because adding it afterwards means regenerating the project.
Monetisation and platform features
In-app purchases in a WebXR PWA go through Horizon Billing plus the Digital Goods API; achievements, leaderboards and add-ons have server-to-server APIs on the web documentation index. Set IAP up before packaging.
Distribution
A packaged PWA is an Android package and travels the same road as a native build: release channels, review, VRC — see quest-store, and Meta's hz-store-pwa skill for the PWA-specific store steps. 2D (non-immersive) PWAs are judged against a subset of the VRCs, with a list of permissions that trigger automatic rejection.
Performance, honestly
- The browser has its own counters — draw-call metrics in the web docs — and the
native profilers in quest-perf do not see inside the page.
- The frame budget is the same as native (13.9 ms at 72 Hz); the overhead is
not. Measure in the headset, never in a desktop browser window.
requestAnimationFrameon theXRSessionis the frame clock; a page that
keeps a DOM animation running alongside it pays twice.
Where to read
index: https://developers.meta.com/horizon/llmstxt/documentation/web/llms.txt/
page: https://developers.meta.com/horizon/llmstxt/documentation/web/<slug>.mdUseful slugs: pwa-webxr, pwa-packaging, pwa-overview-gs, browser-specs, browser-remote-debugging, 3d-web, ps-iap, ts-webxr-perf-drawcall. IWSDK's own guides live under documentation/iwsdk/guides/.
Gotchas
- A 2D landing page inside a PWA reads as a broken app — request the session
on load.
- `bubblewrap` is Meta's fork (
@meta-quest/bubblewrap-cli), not the Google
package; the Quest-specific prompts only exist in it.
- Billing is a packaging-time decision, not a runtime one.
- Testing in the desktop browser proves logic and lies about performance.
Read from the Meta web documentation (`pwa-webxr`, `pwa-packaging`, `3d-web`) on 2026-09-20; versions there move — re-check `bubblewrap` and IWSDK versions before quoting them.
Triggers
More phrases that route here: "PWA for Quest" / "PWA для Quest", "A-Frame", "bubblewrap", "web app manifest for the headset", "browser on Quest" / "браузер в шлеме", "remote debug the headset browser" / "отладить браузер шлема", "web IAP" / "покупки в вебе".

