Skip to main content

Getting started

This walkthrough uses the three.js adapter, the default and standalone choice: no other framework is required, and it runs equally well in a browser tab as in a headset.

Install an adapter

npm install @realitycollective/threejs-interactions three

The adapter re-exports the whole core package, so this is the only interaction package the app depends on. The stable release is what a bare npm install resolves; see Releases and versions for the dist-tags.

Create the runtime

import { createThreeInteractions } from "@realitycollective/threejs-interactions";

const interactions = createThreeInteractions({
xr: renderer.xr,
camera,
domElement: renderer.domElement,
});

Describe a button

An interactable is described as data: an id and the behaviours it carries. press is the simplest behaviour, a mechanical button.

interactions.register(
{ id: "button", behaviours: [{ kind: "press" }] },
buttonMesh,
);

Subscribe to events

The core never calls into your code. It emits events, and the app subscribes.

interactions.runtime.onEvent((event) => {
if (event.type === "actuated") console.log("pressed");
});

Run the frame loop

let last = performance.now();
renderer.setAnimationLoop(() => {
const now = performance.now();
const dt = Math.min(0.1, (now - last) / 1000);
last = now;
interactions.update(dt);
renderer.render(scene, camera);
});

Run on desktop with the mouse fallback

When no immersive session is live, the three.js adapter falls back to a mouse ray cast from the camera through the cursor, so the same scene is testable without a headset. A mouse has no grip position of its own, so the adapter synthesises one along the ray, at desktopGripDistance metres out (1 metre by default). Set this near the distance of the things you are manipulating. The mouse source reports handedness: "none", which is not a real hand: gate any hand-driven mechanics, such as a throw, on handedness !== "none".

What changes for the other adapters

Every adapter follows the same shape: a one-call setup function, a register(descriptor, object) method, and events through interactions.runtime.onEvent. Babylon takes a scene and an xr experience instead of a renderer. IWSDK drives its own render loop, so there is no update(dt) call to make. XR Blocks is set up inside a Script's init() rather than at module scope.

More information