# What Is the XRServer and How Does WebXR/OpenXR Integration Work in Godot?

> Master Godot's XRServer for seamless WebXR and OpenXR integration. Learn how this core component manages AR/VR functionality, trackers, and frame coordination for your projects.

- Repository: [Godot Engine/godot](https://github.com/godotengine/godot)
- Tags: deep-dive
- Published: 2026-02-26

---

**The XRServer is a global singleton in Godot Engine that manages all XR (AR/VR) functionality, acting as a central registry for XRInterface implementations like OpenXR and WebXR, while handling tracker data, world scaling, and frame lifecycle coordination.**

The `XRServer` serves as the backbone of Godot’s XR architecture within the `godotengine/godot` repository, abstracting platform-specific runtimes into a unified API. Whether targeting native headsets via OpenXR or browser-based VR through WebXR, the XRServer orchestrates interfaces, trackers, and rendering hooks to deliver consistent spatial computing capabilities across platforms.

## XRServer Architecture and Core Responsibilities

### Singleton Pattern and Global Access

The XRServer operates as a thread-safe singleton accessible via `XRServer::get_singleton()`. This global object exposes the entire XR subsystem to the engine, eliminating the need for manual instantiation while ensuring centralized state management.

### Interface Registry and Primary Selection

Concrete XR implementations derive from the abstract `XRInterface` class defined in [`servers/xr/xr_interface.h`](https://github.com/godotengine/godot/blob/main/servers/xr/xr_interface.h). The server maintains these interfaces in `Vector<Ref<XRInterface>> interfaces` and designates one as the **primary interface** via `set_primary_interface()`. Only the primary interface supplies view-projection matrices and eye textures to the renderer.

### Tracker Management System

Spatial data flows through `XRPositionalTracker` objects representing headsets, controllers, hands, and body tracking. The server registers trackers via `add_tracker()` and stores them in a `Dictionary trackers` keyed by name. Each frame, `XRServer::_process()` updates these trackers with fresh pose data from the active interface.

### World Scale and Origin Control

Godot’s XR coordinate system supports dynamic scaling through `world_scale` and spatial reorientation via `world_origin`. The server propagates these values to the rendering thread using `_set_render_world_scale()` and related helpers, ensuring consistent spatial mapping between physics and visual representations.

### Frame Lifecycle Coordination

The XRServer drives the per-frame pipeline through three critical hooks:
- `_process()` – Updates tracker poses and interface state
- `pre_render()` – Allows the interface to submit frame data
- `end_frame()` – Finalizes the frame and signals completion

## WebXR Integration via WebXRInterfaceJS

The WebXR backend, implemented in [`modules/webxr/webxr_interface_js.h`](https://github.com/godotengine/godot/blob/main/modules/webxr/webxr_interface_js.h) and [`modules/webxr/webxr_interface_js.cpp`](https://github.com/godotengine/godot/blob/main/modules/webxr/webxr_interface_js.cpp), bridges Godot to browser-based VR/AR capabilities.

### Browser Session Initialization

`WebXRInterfaceJS::initialize()` initiates an XR session through Emscripten-generated JavaScript callbacks. The method registers input sources and allocates color and depth textures that the WebXR compositor populates each frame. Session modes—`immersive-vr`, `immersive-ar`, or `inline`—map to Godot’s configuration via `set_session_mode()`.

### Input Source Handling and Textures

The interface maintains an array of `InputSource` structs (`input_sources[16]`) storing controller state. JavaScript events trigger `_update_input_source()`, which synchronizes button presses, axes, and pose data with Godot’s tracker system. Rendering utilizes `get_color_texture()` and `get_depth_texture()` to retrieve WebGL textures supplied by the browser’s XR compositor.

### Per-Frame Processing Pipeline

Each frame, `WebXRInterfaceJS::process()` retrieves the latest head pose from the browser, updates the `head` tracker via `XRPositionalTracker`, and synchronizes all active input source trackers. This data flows through `XRServer` to the rendering system, which draws the stereo views into the WebXR-provided textures.

## OpenXR Integration Architecture

The OpenXR backend in `modules/openxr/` provides native support for SteamVR, Oculus, Windows Mixed Reality, and other compliant runtimes.

### Session and Action Set Management

`OpenXRInterface::initialize()` loads an `OpenXRActionMap` resource through `_load_action_map()`, creating OpenXR action sets, actions, and interaction profiles. These map Godot’s input system to hardware-specific controls. The method then calls `openxr_api->initialize_session()` to establish the runtime connection.

### Tracker Creation and Hand Tracking

The interface creates runtime trackers via `openxr_api->tracker_create()` and wraps them in `XRControllerTracker` objects registered with `XRServer`. Hand tracking support, implemented in [`modules/openxr/extensions/openxr_hand_tracking_extension.h`](https://github.com/godotengine/godot/blob/main/modules/openxr/extensions/openxr_hand_tracking_extension.h), exposes finger joint data as additional trackers when the runtime supports the `XR_EXT_hand_tracking` extension.

### Rendering and Swap-Chain Handling

`OpenXRInterface` manages swap-chain images through `OpenXRAPI`. Methods `get_color_texture()` and `get_depth_texture()` return texture RIDs corresponding to the runtime’s swap-chain. `pre_draw_viewport()` and `post_draw_viewport()` handle synchronization between Godot’s rendering and the OpenXR compositor, supporting Vulkan, OpenGL, and Metal backends.

### Thread Safety and Render State

Because OpenXR requires thread-aware operation, `OpenXRInterface` copies pose data into a per-frame `RenderState` structure. It pushes these updates to the rendering thread via `RenderingServer::call_on_render_thread()`, preventing race conditions between the game loop and GPU submission.

## Practical Implementation in GDScript

The following example demonstrates initializing XR for both OpenXR and WebXR, accessing tracker data, and configuring environment blend modes:

```gdscript

# Enable XR (works for both OpenXR and WebXR)

var xr_server = XRServer.get_singleton()
var xr_interface = xr_server.find_interface("OpenXR")   # Use "WebXR" for web builds

if xr_interface and not xr_interface.is_initialized():
    xr_interface.initialize()               # starts the XR session

    xr_server.set_primary_interface(xr_interface)  # make it the active renderer

# Get the headset transform each frame (e.g. in _process())

func _process(delta):
    var head_tracker = xr_server.get_tracker("head")
    if head_tracker:
        var hmd_transform = head_tracker.get_pose("default")   # Returns Transform3D

        # Use hmd_transform to drive a camera, AR/VR avatar, etc.

        $XRCamera3D.global_transform = hmd_transform

# Query supported play-area mode and switch to room-scale if possible

if xr_interface.get_supported_environment_blend_modes().has(XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND):
    xr_interface.set_environment_blend_mode(XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND)

# Example for WebXR: start an immersive-VR session from a UI button

func _on_StartVR_pressed():
    var webxr = xr_server.find_interface("WebXR")
    if webxr:
        webxr.set_session_mode("immersive-vr")
        webxr.initialize()

```

## Summary

- **XRServer** is the global singleton in [`servers/xr/xr_server.h`](https://github.com/godotengine/godot/blob/main/servers/xr/xr_server.h) that centralizes all XR operations, managing interfaces, trackers, and world-space transformations.
- **XRInterface** implementations like `WebXRInterfaceJS` and `OpenXRInterface` abstract platform-specific runtimes into a common API registered with the XRServer.
- **WebXR** integration uses Emscripten-generated JavaScript callbacks to bridge browser-based VR/AR sessions, storing controller state in `InputSource` arrays and utilizing WebGL textures for rendering.
- **OpenXR** integration provides native support through `OpenXRAPI`, handling action sets, swap-chain management, and hand-tracking extensions while maintaining thread safety via `RenderState` structures.
- **Tracker objects** (`XRPositionalTracker`, `XRControllerTracker`) registered via `add_tracker()` provide unified access to head, controller, and hand data regardless of the underlying runtime.

## Frequently Asked Questions

### What is the difference between XRServer and XRInterface?

**XRServer** is the global singleton that manages all XR functionality across the engine, while **XRInterface** is an abstract base class that specific implementations (OpenXR, WebXR) inherit from. The server maintains a list of interfaces and designates one as primary to handle rendering, but the interface itself contains the platform-specific logic for session management and hardware communication.

### How does Godot handle controller tracking for both OpenXR and WebXR?

Both implementations create `XRControllerTracker` objects registered with `XRServer` via `add_tracker()`. **WebXR** maps browser input sources to an internal `InputSource` array, updating tracker poses through `_update_input_source()` when JavaScript events fire. **OpenXR** creates trackers via `openxr_api->tracker_create()` and updates them each frame through `handle_tracker()`, reading pose data directly from the OpenXR runtime's action system.

### Can I use multiple XR interfaces simultaneously?

Godot's architecture supports multiple registered interfaces, but only **one primary interface** can be active for rendering at any given time, set via `set_primary_interface()`. You can query available interfaces with `find_interface()` and switch between them (for example, falling back from OpenXR to WebXR), but simultaneous rendering to multiple XR runtimes is not supported by the current architecture.

### Where are the XRServer source files located in the Godot repository?

The core **XRServer** implementation resides in [`servers/xr/xr_server.h`](https://github.com/godotengine/godot/blob/main/servers/xr/xr_server.h) and [`servers/xr/xr_server.cpp`](https://github.com/godotengine/godot/blob/main/servers/xr/xr_server.cpp). The abstract **XRInterface** base class is defined in [`servers/xr/xr_interface.h`](https://github.com/godotengine/godot/blob/main/servers/xr/xr_interface.h). Platform-specific implementations are located in `modules/webxr/` for browser-based XR and `modules/openxr/` for native OpenXR support, with the latter containing `openxr_interface.h/cpp` and the low-level `openxr_api.h/cpp` wrapper.