Skip to main content

API Reference

A list of components and hooks provided by xrift-world-components.

Components

Interactable

Creates an object that can be clicked/interacted with.

import { Interactable } from '@xrift/world-components';

<Interactable id="my-button" onInteract={() => console.log('clicked!')}>
<mesh>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="hotpink" />
</mesh>
</Interactable>

Props

PropTypeDefaultDescription
idstring-Unique identifier (Required)
onInteract() => void-Callback on interaction
childrenReactNode-Object to be interacted with

Mirror

Creates a real-time reflective surface.

import { Mirror } from '@xrift/world-components';

<Mirror position={[0, 1, -5]} />

VideoScreen

Creates a screen that plays synchronized video.

import { VideoScreen } from '@xrift/world-components';

<VideoScreen src="/videos/intro.mp4" position={[0, 2, -3]} />

VideoPlayer

A video player with UI controls based on VideoScreen. It features VR-compatible control UIs such as play/pause buttons, progress bar, and volume bar.

import { VideoPlayer } from '@xrift/world-components';

<VideoPlayer
id="my-video"
url="https://example.com/video.mp4"
position={[0, 2, -5]}
width={4}
/>

Props

PropTypeDefaultDescription
idstring-Unique ID for the screen (Required)
position[number, number, number][0, 2, -5]Position of the screen
rotation[number, number, number][0, 0, 0]Rotation of the screen
widthnumber4Width of the screen (Height is automatically calculated at 16:9)
urlstring-URL of the video
playingbooleantrueInitial playback state
volumenumber1Initial volume (0-1)
sync'global' | 'local''global'Sync mode

Features

  • URL Input Button: Clicking the 🔗 icon displays a URL input overlay, allowing dynamic switching of the video source.
  • Play/Pause Button: Toggle playback state with the ▶/|| icon.
  • Progress Bar: A progress bar divided into 20 segments. Click to return to the beginning of the video.
  • Volume Bar: Adjusts from 0-100% in increments of 10. Displays mute status with 🔈/🔇 icons.
  • VR Support: Supports VR controller operation using Interactable.
Sync Mode

You can select the sync mode with the sync property:

  • 'global': Synchronize playback state across all users (Default)
  • 'local': Each user controls playback independently

LiveVideoPlayer

A video player that supports live streaming playback such as HLS/DASH. While having similar UI controls to VideoPlayer, it is optimized for live streaming.

import { LiveVideoPlayer } from '@xrift/world-components';

<LiveVideoPlayer
id="my-live"
url="https://example.com/live/stream.m3u8"
position={[0, 2, -5]}
width={4}
/>

Props

PropTypeDefaultDescription
idstring-Unique ID for the screen (Required)
position[number, number, number][0, 2, -5]Position of the screen
rotation[number, number, number][0, 0, 0]Rotation of the screen
widthnumber4Width of the screen (Height is automatically calculated at 16:9)
urlstring-Stream URL (HLS/DASH supported)
playingbooleantrueInitial playback state
volumenumber1Initial volume (0-1)

Features

  • URL Input Button: Clicking the 🔗 icon displays a URL input overlay, allowing dynamic switching of the stream source.
  • Play/Pause Button: Toggle playback state with the ▶/|| icon.
  • Volume Bar: Adjusts from 0-100% in increments of 10. Displays mute status with 🔈/🔇 icons.
  • VR Support: Supports VR controller operation using Interactable.
Difference from VideoPlayer

Since LiveVideoPlayer is designed for live streaming, it does not have a progress bar (seek function). Please use VideoPlayer for playing recorded videos.


ScreenShareDisplay

Displays the screen sharing video as a screen in the 3D space. It retrieves video and status from ScreenShareContext.

import { ScreenShareDisplay } from '@xrift/world-components';

<ScreenShareDisplay id="screen-1" position={[0, 2, -5]} />

Props

PropTypeDefaultDescription
idstring-Unique ID for the screen (Required)
position[number, number, number][0, 0, 0]Position of the screen
rotation[number, number, number][0, 0, 0]Rotation of the screen
widthnumber4Width of the screen (Height is automatically calculated at 16:9)
Maintaining Aspect Ratio

The aspect ratio of the video is automatically maintained. Video other than 16:9 will be displayed correctly with black bars.

Limitations

Only one screen can be shared per world. While it is possible to place multiple ScreenShareDisplay components, they will all display the same screen.


SpawnPoint

Specifies the point where players spawn in the world.

import { SpawnPoint } from '@xrift/world-components';

<SpawnPoint />
<SpawnPoint position={[0, 0, 5]} yaw={180} />

Props

PropTypeDefaultDescription
position[number, number, number][0, 0, 0]Spawn position
yawnumber0Orientation at spawn (degrees 0-360)
Development Helper

In the development environment, the spawn position and direction are visualized with a semi-transparent cylinder (gradient transparency from bottom to top) and an arrow. The helper is not displayed in the production build.

SpawnPoint Helper

Multiple SpawnPoints

If multiple SpawnPoint components are placed, the one set last takes effect.


TextInput

A component that enables text input in 3D space. You can customize the appearance freely using the children method.

import { TextInput } from '@xrift/world-components';

<TextInput
id="my-input"
value={inputValue}
onSubmit={handleSubmit}
placeholder="Enter text..."
>
<mesh>
<boxGeometry args={[1, 0.5, 0.1]} />
<meshStandardMaterial color="#333" />
</mesh>
</TextInput>

Props

PropTypeDefaultDescription
idstring-Unique ID for the input field (Required)
childrenReactNode-3D object (Appearance) (Required)
placeholderstring-Placeholder text
maxLengthnumber-Maximum number of characters
valuestring-Current value
onSubmit(value: string) => void-Callback on input completion
interactionTextstring'Click to enter'Text to display on interaction
disabledbooleanfalseWhether to disable input

Mechanism

The TextInput component operates with the following architecture:

  1. TextInput: Displays the 3D object passed as children as a clickable input field.
  2. Overlay Input: Upon clicking, a 2D text input UI is displayed as an overlay to accept actual input.
  3. XRiftContext Integration: world-components requests the overlay display via XRiftContext.
Customizing Appearance

By passing any 3D object to children, you can freely customize the appearance of the input field. You can achieve button-like designs or looks that match the world's atmosphere.

Related Context/Hook

The platform side uses the following APIs to implement TextInput behavior:

  • TextInputContext
  • useTextInputContext
  • TextInputContextValue
  • TextInputRequest

TagBoard

A component that handles tags selected by users locally/globally, providing a board UI (TagSelector) and tag display above each user's head (TagDisplay).

import { TagBoard } from '@xrift/world-components';

<TagBoard
instanceStateKey="main-tag-board"
position={[0, 1.5, -3]}
/>

Props

PropTypeDefaultDescription
tagsTag[]Default tag listTags to display/select
columnsnumber3Number of display columns
titlestring"Select Tag"Title text
instanceStateKeystring-Instance state key (Required, for identification when placing multiple boards)
position[number, number, number][0, 0, 0]Position of the board
rotation[number, number, number][0, 0, 0]Rotation of the board
scalenumber1Overall scale

Tag Type Definition

interface Tag {
id: string; // Unique identifier for the tag
label: string; // Display label
color: string; // Color (HEX format)
}

Default Tag List

If the tags property is omitted, the following tags are used:

[
{ color: "#2ECC71", id: "want-talk", label: "Want to talk" },
{ color: "#3498DB", id: "want-listen", label: "Want to listen" },
{ color: "#95A5A6", id: "silent", label: "Silent" },
{ color: "#1ABC9C", id: "developer", label: "Developer" },
{ color: "#2980B9", id: "student", label: "Student" },
{ color: "#F1C40F", id: "beginner", label: "Beginner" },
{ color: "#9B59B6", id: "dont-know", label: "Don't know anything" },
{ color: "#8BC34A", id: "working", label: "Working" },
{ color: "#BF7B41", id: "away", label: "Away" },
{ color: "#FF9800", id: "cat", label: "Cat" },
]

Usage Example

Using Custom Tags
import { TagBoard, type Tag } from '@xrift/world-components';

const customTags: Tag[] = [
{ id: "frontend", label: "Frontend", color: "#61DAFB" },
{ id: "backend", label: "Backend", color: "#68A063" },
{ id: "design", label: "Design", color: "#FF6B6B" },
{ id: "pm", label: "PM", color: "#9B59B6" },
];

export const MyWorld = () => {
return (
<TagBoard
tags={customTags}
columns={2}
title="What is your role?"
instanceStateKey="role-tag-board"
position={[0, 1.5, -3]}
rotation={[0, 0, 0]}
scale={1.2}
/>
);
};
Placing Multiple TagBoards

instanceStateKey must be unique within the same world. If placing multiple TagBoards, specify a different instanceStateKey for each.

Dependencies
  • UsersContext is required (used for retrieving user information).
  • Uses useInstanceState hook internally (synchronization of tag selection state).

Hooks

useInstanceState

Synchronizes state across all users in the instance. It has the same interface as React's useState.

import { useInstanceState } from '@xrift/world-components';

function Counter() {
const [count, setCount] = useInstanceState('counter', 0);

return (
<mesh onClick={() => setCount(count + 1)}>
{/* count is synchronized across all users */}
</mesh>
);
}

Arguments

ArgumentTypeDescription
keystringUnique identifier for the state
initialValueTInitial value

Return Value

[value: T, setValue: (newValue: T) => void] - Same format as useState


useScreenShareContext

A hook to retrieve the state of screen sharing.

import { useScreenShareContext } from '@xrift/world-components';

function MyComponent() {
const { videoElement, isSharing, startScreenShare, stopScreenShare } = useScreenShareContext();

return (
<button onClick={isSharing ? stopScreenShare : startScreenShare}>
{isSharing ? 'Stop Sharing' : 'Start Sharing'}
</button>
);
}

Return Value

PropertyTypeDescription
videoElementHTMLVideoElement | nullVideo element to display
isSharingbooleanWhether the user is sharing
startScreenShare() => voidStart sharing
stopScreenShare() => voidStop sharing

useSpawnPoint

A hook for the platform side to retrieve spawn point information.

import { useSpawnPoint } from '@xrift/world-components';

function MyPlatform() {
const spawnPoint = useSpawnPoint();
// spawnPoint: { position: [x, y, z], yaw: number }
}

Return Value

PropertyTypeDescription
position[number, number, number]Spawn position
yawnumberOrientation at spawn (degrees)
Usage

This hook is intended for use on the xrift-frontend (platform) side. World developers should use the SpawnPoint component.


useUsers

A hook to retrieve information and location of users participating in the world. You can access information about yourself (local user) and other participants (remote users).

import { useUsers } from '@xrift/world-components';

function ParticipantCount() {
const { localUser, remoteUsers, getMovement, getLocalMovement } = useUsers();

const totalCount = (localUser ? 1 : 0) + remoteUsers.length;

return (
<div>
<p>Participants: {totalCount}</p>
</div>
);
}

Return Value

PropertyTypeDescription
localUserUser | nullInformation about yourself
remoteUsersUser[]Array of information about other participants
getMovement(id: string) => PlayerMovement | undefinedRetrieve location information of a specific user
getLocalMovement() => PlayerMovementRetrieve your own location information

User Type

interface User {
id: string; // User ID
displayName: string; // Display name
avatarUrl: string | null; // Avatar icon URL
isGuest: boolean; // Whether the user is a guest
}

PlayerMovement Type

interface PlayerMovement {
position: { x: number; y: number; z: number };
direction: { x: number; z: number };
horizontalSpeed: number;
verticalSpeed: number;
rotation: { yaw: number; pitch: number };
isGrounded: boolean;
isJumping: boolean;
isInVR?: boolean;
vrTracking?: VRTrackingData;
}

Retrieving Position in useFrame

getMovement() and getLocalMovement() can be called every frame within useFrame. These functions allow retrieving the latest position information without triggering re-renders.

import { useUsers } from '@xrift/world-components';
import { useFrame } from '@react-three/fiber';
import { useRef } from 'react';
import { Group } from 'three';

function FollowCamera() {
const groupRef = useRef<Group>(null);
const { getLocalMovement } = useUsers();

useFrame(() => {
const movement = getLocalMovement();
if (!groupRef.current) return;

// Place an object slightly above your position
groupRef.current.position.set(
movement.position.x,
movement.position.y + 3,
movement.position.z
);
});

return (
<group ref={groupRef}>
<pointLight intensity={1} />
</group>
);
}

Use Cases

Display HUD above User's Head
import { useUsers } from '@xrift/world-components';
import { useFrame } from '@react-three/fiber';
import { useRef } from 'react';
import { Group } from 'three';
import { Text } from '@react-three/drei';

function UserHUD({ user, getMovement }) {
const groupRef = useRef<Group>(null);

useFrame(() => {
const movement = getMovement(user.id);
if (!movement || !groupRef.current) return;

// Place above the user's head
groupRef.current.position.set(
movement.position.x,
movement.position.y + 2,
movement.position.z
);
});

return (
<group ref={groupRef}>
<Text fontSize={0.2}>{user.displayName}</Text>
</group>
);
}

function UserHUDs() {
const { remoteUsers, getMovement } = useUsers();

return (
<>
{remoteUsers.map(user => (
<UserHUD key={user.id} user={user} getMovement={getMovement} />
))}
</>
);
}
Detect Nearby Users
import { useUsers } from '@xrift/world-components';
import { useFrame } from '@react-three/fiber';
import { useState } from 'react';

function ProximityDetector() {
const { remoteUsers, getMovement, getLocalMovement } = useUsers();
const [nearbyUsers, setNearbyUsers] = useState<string[]>([]);

useFrame(() => {
const myPos = getLocalMovement().position;
const nearby: string[] = [];

remoteUsers.forEach(user => {
const movement = getMovement(user.id);
if (!movement) return;

const distance = Math.sqrt(
Math.pow(myPos.x - movement.position.x, 2) +
Math.pow(myPos.y - movement.position.y, 2) +
Math.pow(myPos.z - movement.position.z, 2)
);

if (distance < 5) {
nearby.push(user.displayName);
}
});

// Update only if the array content changes
if (JSON.stringify(nearby) !== JSON.stringify(nearbyUsers)) {
setNearbyUsers(nearby);
}
});

return null;
}
Calculate Distance Between Users
import { useUsers } from '@xrift/world-components';
import { useFrame } from '@react-three/fiber';
import { useRef } from 'react';
import { Line } from '@react-three/drei';

function DistanceLine({ targetUser, getMovement, getLocalMovement }) {
const lineRef = useRef<any>(null);

useFrame(() => {
const myPos = getLocalMovement().position;
const targetMovement = getMovement(targetUser.id);
if (!targetMovement || !lineRef.current) return;

lineRef.current.geometry.setPositions([
myPos.x, myPos.y + 1, myPos.z,
targetMovement.position.x, targetMovement.position.y + 1, targetMovement.position.z
]);
});

return (
<Line
ref={lineRef}
points={[[0, 0, 0], [0, 0, 0]]}
color="yellow"
lineWidth={2}
/>
);
}
Performance Hint

getMovement() and getLocalMovement() are safe to call every frame within useFrame. They return internally cached values, so the performance impact is minimal.

remoteUsers Update Timing

The remoteUsers array is updated only when users join or leave. Changes in user positions do not trigger re-renders. Always use getMovement() to retrieve position information.