From meta-vr
Builds WebXR experiences for Meta Quest using Immersive Web SDK with ECS architecture, Three.js integration, spatial UI, and XR input handling for VR/MR apps in Quest Browser.
npx claudepluginhub meta-quest/agentic-tools --plugin meta-vrThis skill is limited to using the following tools:
Build immersive WebXR experiences for Meta Quest using Meta's Immersive Web
Builds immersive VR/AR experiences using WebXR, Three.js XR, Quest/Meta development, hand tracking, spatial UI/UX, AR anchors, and XR performance optimization. Useful for VR/AR/WebXR projects.
Scaffolds new Meta Quest and Horizon OS projects for Unity, Unreal, Android/Spatial SDK, or WebXR with recommended settings, setup instructions, and decision tree. Use when creating Quest apps from scratch.
Sets up OpenXR for VR/AR/XR apps in Godot 4.3+: XROrigin3D, controllers, hand tracking, passthrough, input handling, Meta Quest deployment.
Share bugs, ideas, or general feedback.
Build immersive WebXR experiences for Meta Quest using Meta's Immersive Web SDK (IWSDK). This skill covers the current package layout, ECS architecture, Three.js integration, spatial UI development, XR input handling, and the recommended Vite-based development loop.
Use this skill when you need to:
PanelUIThis skill applies to all Meta Quest headsets with Quest Browser.
The Immersive Web SDK (IWSDK) is Meta's framework for building WebXR experiences. It provides:
PanelUI, PanelDocument, and
UIKitDocument for in-world and screen-space UIAssetManagerIWSDK runs in the browser via the WebXR Device API. Applications are standard web pages that are usually served by Vite during development and opened in Quest Browser for on-device validation.
These details are important because older pre-release guidance is now wrong:
@iwsdk/corenpm create @iwsdk@latest@iwsdk/vite-plugin-devvite-plugin-mkcert>=20.19.0@iwsdk/core re-exports @iwsdk/xr-input and @iwsdk/locomotornpm create @iwsdk@latest my-project
cd my-project
If you are starting from the official create flow, it can also install dependencies and initialize git for you.
npm install
If you are setting up manually, use @iwsdk/core, three,
@iwsdk/vite-plugin-dev, and vite-plugin-mkcert instead of the old
@meta-quest/iwsdk package.
npm run dev
The standard IWSDK Vite setup gives you a secure local dev server, hot module
replacement, and an emulator/dev browser workflow through
@iwsdk/vite-plugin-dev.
There are two normal validation paths:
Quest Browser testing requires HTTPS. If the app works on desktop but will not enter XR on-device, confirm that the dev server URL is secure and headset reachable.
IWSDK is a strong fit for coding agents because the development loop is already web-native: edit source, reload the page, observe behavior, and iterate.
Recommended loop:
If you are pairing a Quest-native frontend with a host-side coding agent, keep the frontend thin. Let the host machine own the repository, file edits, dependency installs, tests, build steps, and hzdb tool calls.
A typical IWSDK project looks like this:
my-project/
src/
index.ts # Entry point: World.create, scene setup, XR controls
systems/ # Optional ECS systems
movement.ts
components/ # Optional ECS components
velocity.ts
public/
models/ # GLTF / GLB assets
textures/ # Textures and images
audio/ # Audio assets
ui/ # Compiled UIKitML JSON output
ui/
welcome.uikitml # Source UIKitML files
vite.config.ts
package.json
tsconfig.json
import { Types, createComponent } from '@iwsdk/core';
export const Velocity = createComponent(
'Velocity',
{
x: { type: Types.Float32, default: 0 },
y: { type: Types.Float32, default: 0 },
z: { type: Types.Float32, default: 0 },
},
'Linear velocity in meters per second',
);
import { createSystem } from '@iwsdk/core';
import { Velocity } from './components/velocity';
export class MovementSystem extends createSystem({
moving: { required: [Velocity] },
}) {
update(delta: number) {
this.queries.moving.entities.forEach((entity) => {
if (!entity.object3D) {
return;
}
entity.object3D.position.x += Velocity.data.x[entity.index] * delta;
entity.object3D.position.y += Velocity.data.y[entity.index] * delta;
entity.object3D.position.z += Velocity.data.z[entity.index] * delta;
});
}
}
import * as THREE from 'three';
import { Velocity } from './components/velocity';
function spawnCube(world) {
const mesh = new THREE.Mesh(
new THREE.BoxGeometry(0.5, 0.5, 0.5),
new THREE.MeshStandardMaterial({ color: 0x00aaff }),
);
const entity = world.createTransformEntity(mesh);
entity.object3D!.position.set(0, 1.5, -2);
entity.addComponent(Velocity, { x: 0, y: 0, z: 0 });
}
import {
ReferenceSpaceType,
SessionMode,
World,
} from '@iwsdk/core';
const container = document.getElementById('scene-container') as HTMLDivElement;
const world = await World.create(container, {
xr: {
sessionMode: SessionMode.ImmersiveVR,
referenceSpace: ReferenceSpaceType.LocalFloor,
offer: 'none',
},
});
world.registerSystem(MovementSystem);
world.launchXR();
World.create(...) already wires the renderer, core systems, asset loading, and
render loop. Do not teach agents to use new World(), createWorld(),
world.start(), or the older enterXR(...) path.
These are common pitfalls that cause unexpected failures or wasted debugging time when building IWSDK WebXR apps for Quest.
ngrok, a self-signed cert, or the --https flag in Vite to tunnel HTTPS during development.SharedArrayBuffer, certain WebGL2 extensions, and OffscreenCanvas may behave differently or be unavailable. Always test on actual Quest Browser, not just desktop Chrome with the IWER emulator.xrSession.updateTargetFrameRate(90) after session start. Forgetting this is the most common reason a WebXR app feels "sluggish" compared to a native build.requestAnimationFrame vs. XR frame loop -- Once an XR session starts, you must use xrSession.requestAnimationFrame, not window.requestAnimationFrame. Using the wrong one causes the scene to freeze or render at the wrong rate.World.create(...), world.launchXR(), and world.exitXR() as the
runtime control surface@iwsdk/vite-plugin-dev and Quest Browser validation, then layer
in emulator/dev browser workflows for faster iteration