Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.


Inherits: XRInterface < RefCounted < Object

XR interface using WebXR.


WebXR is an open standard that allows creating VR and AR applications that run in the web browser.

As such, this interface is only available when running in Web exports.

WebXR supports a wide range of devices, from the very capable (like Valve Index, HTC Vive, Oculus Rift and Quest) down to the much less capable (like Google Cardboard, Oculus Go, GearVR, or plain smartphones).

Since WebXR is based on JavaScript, it makes extensive use of callbacks, which means that WebXRInterface is forced to use signals, where other XR interfaces would instead use functions that return a result immediately. This makes WebXRInterface quite a bit more complicated to initialize than other XR interfaces.

Here's the minimum code required to start an immersive VR session:

extends Node3D

var webxr_interface
var vr_supported = false

func _ready():
    # We assume this node has a button as a child.
    # This button is for the user to consent to entering immersive VR mode.

    webxr_interface = XRServer.find_interface("WebXR")
    if webxr_interface:
        # WebXR uses a lot of asynchronous callbacks, so we connect to various
        # signals in order to receive them.

        # This returns immediately - our _webxr_session_supported() method
        # (which we connected to the "session_supported" signal above) will
        # be called sometime later to let us know if it's supported or not.

func _webxr_session_supported(session_mode, supported):
    if session_mode == 'immersive-vr':
        vr_supported = supported

func _on_button_pressed():
    if not vr_supported:
        OS.alert("Your browser doesn't support VR")

    # We want an immersive VR session, as opposed to AR ('immersive-ar') or a
    # simple 3DoF viewer ('viewer').
    webxr_interface.session_mode = 'immersive-vr'
    # 'bounded-floor' is room scale, 'local-floor' is a standing or sitting
    # experience (it puts you 1.6m above the ground if you have 3DoF headset),
    # whereas as 'local' puts you down at the XROrigin.
    # This list means it'll first try to request 'bounded-floor', then
    # fallback on 'local-floor' and ultimately 'local', if nothing else is
    # supported.
    webxr_interface.requested_reference_space_types = 'bounded-floor, local-floor, local'
    # In order to use 'local-floor' or 'bounded-floor' we must also
    # mark the features as required or optional.
    webxr_interface.required_features = 'local-floor'
    webxr_interface.optional_features = 'bounded-floor'

    # This will return false if we're unable to even request the session,
    # however, it can still fail asynchronously later in the process, so we
    # only know if it's really succeeded or failed when our
    # _webxr_session_started() or _webxr_session_failed() methods are called.
    if not webxr_interface.initialize():
        OS.alert("Failed to initialize")

func _webxr_session_started():
    $Button.visible = false
    # This tells Godot to start rendering to the headset.
    get_viewport().use_xr = true
    # This will be the reference space type you ultimately got, out of the
    # types that you requested above. This is useful if you want the game to
    # work a little differently in 'bounded-floor' versus 'local-floor'.
    print ("Reference space type: " + webxr_interface.reference_space_type)

func _webxr_session_ended():
    $Button.visible = true
    # If the user exits immersive mode, then we tell Godot to render to the web
    # page again.
    get_viewport().use_xr = false

func _webxr_session_failed(message):
    OS.alert("Failed to initialize: " + message)

There are a couple ways to handle "controller" input:

  • Using XRController3D nodes and their XRController3D.button_pressed and XRController3D.button_released signals. This is how controllers are typically handled in XR apps in Godot, however, this will only work with advanced VR controllers like the Oculus Touch or Index controllers, for example.

  • Using the select, squeeze and related signals. This method will work for both advanced VR controllers, and non-traditional input sources like a tap on the screen, a spoken voice command or a button press on the device itself.

You can use both methods to allow your game or app to support a wider or narrower set of devices and input methods, or to allow more advanced interactions with more advanced devices.

















get_available_display_refresh_rates ( ) const


get_display_refresh_rate ( ) const


get_input_source_target_ray_mode ( int input_source_id ) const


get_input_source_tracker ( int input_source_id ) const


is_input_source_active ( int input_source_id ) const


is_session_supported ( String session_mode )


set_display_refresh_rate ( float refresh_rate )


display_refresh_rate_changed ( )

Emitted after the display's refresh rate has changed.

reference_space_reset ( )

Emitted to indicate that the reference space has been reset or reconfigured.

When (or whether) this is emitted depends on the user's browser or device, but may include when the user has changed the dimensions of their play space (which you may be able to access via XRInterface.get_play_area) or pressed/held a button to recenter their position.

See WebXR's XRReferenceSpace reset event for more information.

select ( int input_source_id )

Emitted after one of the input sources has finished its "primary action".

Use get_input_source_tracker and get_input_source_target_ray_mode to get more information about the input source.

selectend ( int input_source_id )

Emitted when one of the input sources has finished its "primary action".

Use get_input_source_tracker and get_input_source_target_ray_mode to get more information about the input source.

selectstart ( int input_source_id )

Emitted when one of the input source has started its "primary action".

Use get_input_source_tracker and get_input_source_target_ray_mode to get more information about the input source.

session_ended ( )

Emitted when the user ends the WebXR session (which can be done using UI from the browser or device).

At this point, you should do get_viewport().use_xr = false to instruct Godot to resume rendering to the screen.

session_failed ( String message )

Emitted by XRInterface.initialize if the session fails to start.

message may optionally contain an error message from WebXR, or an empty string if no message is available.

session_started ( )

Emitted by XRInterface.initialize if the session is successfully started.

At this point, it's safe to do get_viewport().use_xr = true to instruct Godot to start rendering to the XR device.

session_supported ( String session_mode, bool supported )

Emitted by is_session_supported to indicate if the given session_mode is supported or not.

squeeze ( int input_source_id )

Emitted after one of the input sources has finished its "primary squeeze action".