The contracts every adapter speaks.
WebXR Input describes XR input in plain TypeScript types: where a hand or controller is pointing, what it is touching, and what the device can actually do. It has no dependency on any 3D engine and no runtime dependencies at all, and a test enforces both. That is what lets the Interactions and UI Extensions families share one description of input, so an engine adapter is written once and feeds both.
How it is built
Every Reality Toolkit WebXR family has the same shape. The logic lives in an engine-free core that carries the tests. A thin adapter binds that core to one engine, depends on it, and re-exports it, so your app installs one package. Where families need to agree on a shape, they share plain contracts rather than importing each other.
Your app
Owns the content: meshes, panels, placement, physics and sound files. Installs one adapter per family and wires families together with subscriptions in the one place where both halves are in scope.
One adapter per engine
The only place an engine name appears. It reads the engine each frame, feeds the core, applies the core's results back, and reports anything the engine cannot do instead of failing silently.
The engine-free core
All the logic, as plain TypeScript with no engine import. An architecture test fails the build the moment one lands, which is what lets the same behaviour run unchanged on every engine, and be tested without a headset.
Where it sits in the stack
Four families, one rule: no package references a sibling family, not even a type. What crosses a boundary is a subscription your app makes, such as playing an Environment cue when an Interactions press fires.
The Service Framework for the web sits beside these four and keeps its own site: serviceframework.realitycollective.net. The layering rule explains the boundaries in full.
Why you would use it
One description of input
A ray, a grip pose, a fingertip, select and squeeze as numbers from 0 to 1. Plain tuples, no engine types anywhere, so a pose means the same thing to every family.
Capabilities from the live session
What a provider can deliver is derived from the session that is actually running, and consumers gate behaviour on it with satisfies and unmetRequirements.
Conformance as data
The provider contract ships as a list of named cases, not as tests. An adapter loops over them in its own test runner and proves it conforms.
Who implements it
Apps never install this package. Every adapter in the Interactions and UI Extensions families implements the provider interface and re-exports the contracts, so an app reads these types through the adapter it already has.
Interactions adapters
threejs-, babylon-, iwsdk-, xrblocks-interactionsEach implements InputProvider over its engine and hands the samples to the interaction core.
four adaptersRead more →UI Extensions adapters
iwsdk-, xrblocks-uiextensionsDrive windows and controls through the shared pointer contract, so one input stack serves both families.
two adaptersRead more →Your own adapter
implement InputProviderPull-based sample(), capability-change events, optional haptics and presence. Run inputProviderContractCases() to prove it.
guideRead more →Who does what
Read the ladder top to bottom. The middle column is the stage every engine goes through; the core owns the left, the adapter you install owns the right.
Five words you will meet
Where to next
Welcome to WebXR Input →Requirements, packages, use cases and a quickstart on one page.Basics →The concepts, one page per topic, in the same order as the Service Framework basics.Features →The additional capabilities that ship with the family, and the design decisions behind it.Host integrations →One page per package: the core and the adapter for each engine, with what it cannot do and why.Examples →The shipped demos and examples, broken down piece by piece.API reference →Generated from the TSDoc in every package of the family.Source and issues: GitHub. Questions go to the Reality Collective Discord.