text: var LibraryWebVR = {
    $WebVR: {
      DEVICE_TYPE_UNKNOWN: 0,
      DEVICE_TYPE_HMD: 1,
      DEVICE_TYPE_SENSOR: 2,
  
      EYE_LEFT: 0,
      EYE_RIGHT: 1,
  
      initialized: false,
      ready: false,
      devices: [],
      deviceHardwareIds: {},
      nextHardwareDeviceId: 1,
      getDevicesPromise: null,
  
      selectedHMD: null,
      selectedHMDId: 0,
  
      init: function() {
        if (WebVR.initialized) return;
  
        WebVR.initialized = true;
  
        if (!navigator.getVRDevices) {
          WebVR.ready = true;
          WebVR.devices = [];
          return;
        }
  
        navigator.getVRDevices().then(
          function(devs) {
            WebVR.ready = true;
            WebVR.devices = devs;
          },
          function() {
            console.log("Emscripten WebVR getVRDevices() hit error callback");
            WebVR.ready = true;
            WebVR.devices = [];
          }
        );
      },
  
      getDeviceByID: function(deviceId) {
        if (deviceId < 1 || deviceId > WebVR.devices.length) {
          console.log("library_vr getDeviceByID invalid device id at: " + stackTrace());
          return null;
        }
  
        return WebVR.devices[deviceId-1];
      }
    },
  
    emscripten_vr_init: function() {
      WebVR.init();
    },
  
    emscripten_vr_ready: function() {
      return WebVR.ready ? 1 : 0;
    },
  
    emscripten_vr_count_devices: function() {
      return WebVR.devices.length;
    },
  
    emscripten_vr_get_device_id: function(deviceIndex) {
      if (deviceIndex < 0 || deviceIndex >= WebVR.devices.length) {
        return -1;
      }
  
      // we're doing to treat the device ID the same as the index + 1
      return deviceIndex + 1;
    },
  
    emscripten_vr_get_device_hwid: function(deviceId) {
      var dev = WebVR.getDeviceByID(deviceId);
      if (!dev) return -1;
  
      if (!WebVR.deviceHardwareIds[dev.hardwareUnitId]) {
        WebVR.deviceHardwareIds[dev.hardwareUnitId] = WebVR.nextHardwareDeviceId++;
      }
      return WebVR.deviceHardwareIds[dev.hardwareUnitId];
    },
  
    emscripten_vr_get_device_name: function(deviceId) {
      var dev = WebVR.getDeviceByID(deviceId);
      var buffer, devName;
      devName = dev ? dev.deviceName : "";
      var len = lengthBytesUTF8(devName);
      buf = _malloc(len + 1);
      stringToUTF8(devName, buf, len + 1);
      return buf;
    },
  
    emscripten_vr_get_device_type: function(deviceId) {
      var dev = WebVR.getDeviceByID(deviceId);
      if (!dev) return -1;
  
      if (dev instanceof HMDVRDevice) {
        return WebVR.DEVICE_TYPE_HMD;
      }
      if (dev instanceof PositionSensorVRDevice) {
        return WebVR.DEVICE_TYPE_SENSOR;
      }
      return WebVR.DEVICE_TYPE_UNKNOWN;
    },
  
    emscripten_vr_select_hmd_device: function(deviceId) {
      if (deviceId == 0) {
        WebVR.selectedHMD = null;
        WebVR.selectedHMDId = 0;
        return 1;
      }
  
      var dev = WebVR.getDeviceByID(deviceId);
      if (!dev || !(dev instanceof HMDVRDevice)) {
        console.log("Trying to call emscripten_vr_select_hmd_device on invalid or non-HMD device ID!");
        return 0;
      }
      WebVR.selectedHMD = dev;
      WebVR.selectedHMDId = deviceId;
      return 1;
    },
  
    emscripten_vr_get_selected_hmd_device: function() {
      return WebVR.selectedHMDId;
    },
  
    emscripten_vr_hmd_get_eye_parameters: function(deviceId, whichEye, eyeParamsPtr) {
      if (!eyeParamsPtr) return 0;
      var dev = WebVR.getDeviceByID(deviceId);
      if (!dev) return 0;
      var params = dev.getEyeParameters(whichEye == WebVR.EYE_LEFT ? "left" : "right");
  
      {{{ makeSetValue('eyeParamsPtr', C_STRUCTS.WebVREyeParameters.minimumFieldOfView.upDegrees, 'params.minimumFieldOfView.upDegrees', 'double') }}};
      {{{ makeSetValue('eyeParamsPtr', C_STRUCTS.WebVREyeParameters.minimumFieldOfView.downDegrees, 'params.minimumFieldOfView.downDegrees', 'double') }}};
      {{{ makeSetValue('eyeParamsPtr', C_STRUCTS.WebVREyeParameters.minimumFieldOfView.leftDegrees, 'params.minimumFieldOfView.leftDegrees', 'double') }}};
      {{{ makeSetValue('eyeParamsPtr', C_STRUCTS.WebVREyeParameters.minimumFieldOfView.rightDegrees, 'params.minimumFieldOfView.rightDegrees', 'double') }}};
  
      {{{ makeSetValue('eyeParamsPtr', C_STRUCTS.WebVREyeParameters.maximumFieldOfView.upDegrees, 'params.maximumFieldOfView.upDegrees', 'double') }}};
      {{{ makeSetValue('eyeParamsPtr', C_STRUCTS.WebVREyeParameters.maximumFieldOfView.downDegrees, 'params.maximumFieldOfView.downDegrees', 'double') }}};
      {{{ makeSetValue('eyeParamsPtr', C_STRUCTS.WebVREyeParameters.maximumFieldOfView.leftDegrees, 'params.maximumFieldOfView.leftDegrees', 'double') }}};
      {{{ makeSetValue('eyeParamsPtr', C_STRUCTS.WebVREyeParameters.maximumFieldOfView.rightDegrees, 'params.maximumFieldOfView.rightDegrees', 'double') }}};
  
      {{{ makeSetValue('eyeParamsPtr', C_STRUCTS.WebVREyeParameters.recommendedFieldOfView.upDegrees, 'params.recommendedFieldOfView.upDegrees', 'double') }}};
      {{{ makeSetValue('eyeParamsPtr', C_STRUCTS.WebVREyeParameters.recommendedFieldOfView.downDegrees, 'params.recommendedFieldOfView.downDegrees', 'double') }}};
      {{{ makeSetValue('eyeParamsPtr', C_STRUCTS.WebVREyeParameters.recommendedFieldOfView.leftDegrees, 'params.recommendedFieldOfView.leftDegrees', 'double') }}};
      {{{ makeSetValue('eyeParamsPtr', C_STRUCTS.WebVREyeParameters.recommendedFieldOfView.rightDegrees, 'params.recommendedFieldOfView.rightDegrees', 'double') }}};
  
      {{{ makeSetValue('eyeParamsPtr', C_STRUCTS.WebVREyeParameters.eyeTranslation.x, 'params.eyeTranslation.x', 'double') }}};
      {{{ makeSetValue('eyeParamsPtr', C_STRUCTS.WebVREyeParameters.eyeTranslation.y, 'params.eyeTranslation.y', 'double') }}};
      {{{ makeSetValue('eyeParamsPtr', C_STRUCTS.WebVREyeParameters.eyeTranslation.z, 'params.eyeTranslation.z', 'double') }}};
      {{{ makeSetValue('eyeParamsPtr', C_STRUCTS.WebVREyeParameters.eyeTranslation.w, 'params.eyeTranslation.w', 'double') }}};
  
      {{{ makeSetValue('eyeParamsPtr', C_STRUCTS.WebVREyeParameters.currentFieldOfView.upDegrees, 'params.currentFieldOfView.upDegrees', 'double') }}};
      {{{ makeSetValue('eyeParamsPtr', C_STRUCTS.WebVREyeParameters.currentFieldOfView.downDegrees, 'params.currentFieldOfView.downDegrees', 'double') }}};
      {{{ makeSetValue('eyeParamsPtr', C_STRUCTS.WebVREyeParameters.currentFieldOfView.leftDegrees, 'params.currentFieldOfView.leftDegrees', 'double') }}};
      {{{ makeSetValue('eyeParamsPtr', C_STRUCTS.WebVREyeParameters.currentFieldOfView.rightDegrees, 'params.currentFieldOfView.rightDegrees', 'double') }}};
  
      {{{ makeSetValue('eyeParamsPtr', C_STRUCTS.WebVREyeParameters.renderRect.x, 'params.renderRect.x', 'i32') }}};
      {{{ makeSetValue('eyeParamsPtr', C_STRUCTS.WebVREyeParameters.renderRect.y, 'params.renderRect.y', 'i32') }}};
      {{{ makeSetValue('eyeParamsPtr', C_STRUCTS.WebVREyeParameters.renderRect.width, 'params.renderRect.width', 'i32') }}};
      {{{ makeSetValue('eyeParamsPtr', C_STRUCTS.WebVREyeParameters.renderRect.height, 'params.renderRect.height', 'i32') }}};
  
      return 1;
  
    },
  
    emscripten_vr_hmd_set_fov: function(deviceId, leftFovPtr, rightFovPtr, zNear, zFar) {
      if (!leftFovPtr || !rightFovPtr) return 0;
      var dev = WebVR.getDeviceByID(deviceId);
      if (!dev) return 0;
      var leftFov = {
        upDegrees: {{{ makeGetValue('leftFovPtr', C_STRUCTS.WebVRFieldOfView.upDegrees, 'double') }}},
        downDegrees: {{{ makeGetValue('leftFovPtr', C_STRUCTS.WebVRFieldOfView.downDegrees, 'double') }}},
        leftDegrees: {{{ makeGetValue('leftFovPtr', C_STRUCTS.WebVRFieldOfView.leftDegrees, 'double') }}},
        rightDegrees: {{{ makeGetValue('leftFovPtr', C_STRUCTS.WebVRFieldOfView.rightDegrees, 'double') }}}
      };
      var rightFov = {
        upDegrees: {{{ makeGetValue('rightFovPtr', C_STRUCTS.WebVRFieldOfView.upDegrees, 'double') }}},
        downDegrees: {{{ makeGetValue('rightFovPtr', C_STRUCTS.WebVRFieldOfView.downDegrees, 'double') }}},
        leftDegrees: {{{ makeGetValue('rightFovPtr', C_STRUCTS.WebVRFieldOfView.leftDegrees, 'double') }}},
        rightDegrees: {{{ makeGetValue('rightFovPtr', C_STRUCTS.WebVRFieldOfView.rightDegrees, 'double') }}}
      };
      dev.setFieldOfView(leftFov, rightFov, zNear, zFar);
      return 1;
    },
  
    emscripten_vr_sensor_get_state: function(deviceId, immediate, statePtr) {
      if (!statePtr) return 0;
      var dev = WebVR.getDeviceByID(deviceId);
      if (!dev) return 0;
      var state = immediate ? dev.getImmediateState : dev.getState();
  
      {{{ makeSetValue('statePtr', C_STRUCTS.WebVRPositionState.timeStamp, 'state.timeStamp', 'double') }}};
  
      {{{ makeSetValue('statePtr', C_STRUCTS.WebVRPositionState.hasPosition, 'state.hasPosition ? 1 : 0', 'i32') }}};
      if (state.hasPosition) {
        {{{ makeSetValue('statePtr', C_STRUCTS.WebVRPositionState.position.x, 'state.position.x', 'double') }}};
        {{{ makeSetValue('statePtr', C_STRUCTS.WebVRPositionState.position.y, 'state.position.y', 'double') }}};
        {{{ makeSetValue('statePtr', C_STRUCTS.WebVRPositionState.position.z, 'state.position.z', 'double') }}};
        {{{ makeSetValue('statePtr', C_STRUCTS.WebVRPositionState.linearVelocity.x, 'state.linearVelocity.x', 'double') }}};
        {{{ makeSetValue('statePtr', C_STRUCTS.WebVRPositionState.linearVelocity.y, 'state.linearVelocity.y', 'double') }}};
        {{{ makeSetValue('statePtr', C_STRUCTS.WebVRPositionState.linearVelocity.z, 'state.linearVelocity.z', 'double') }}};
        {{{ makeSetValue('statePtr', C_STRUCTS.WebVRPositionState.linearAcceleration.x, 'state.linearAcceleration.x', 'double') }}};
        {{{ makeSetValue('statePtr', C_STRUCTS.WebVRPositionState.linearAcceleration.y, 'state.linearAcceleration.y', 'double') }}};
        {{{ makeSetValue('statePtr', C_STRUCTS.WebVRPositionState.linearAcceleration.z, 'state.linearAcceleration.z', 'double') }}};
      }
  
      {{{ makeSetValue('statePtr', C_STRUCTS.WebVRPositionState.hasOrientation, 'state.hasOrientation ? 1 : 0', 'i32') }}};
      if (state.hasOrientation) {
        {{{ makeSetValue('statePtr', C_STRUCTS.WebVRPositionState.orientation.x, 'state.orientation.x', 'double') }}};
        {{{ makeSetValue('statePtr', C_STRUCTS.WebVRPositionState.orientation.y, 'state.orientation.y', 'double') }}};
        {{{ makeSetValue('statePtr', C_STRUCTS.WebVRPositionState.orientation.z, 'state.orientation.z', 'double') }}};
        {{{ makeSetValue('statePtr', C_STRUCTS.WebVRPositionState.orientation.w, 'state.orientation.w', 'double') }}};
        {{{ makeSetValue('statePtr', C_STRUCTS.WebVRPositionState.angularVelocity.x, 'state.angularVelocity.x', 'double') }}};
        {{{ makeSetValue('statePtr', C_STRUCTS.WebVRPositionState.angularVelocity.y, 'state.angularVelocity.y', 'double') }}};
        {{{ makeSetValue('statePtr', C_STRUCTS.WebVRPositionState.angularVelocity.z, 'state.angularVelocity.z', 'double') }}};
        {{{ makeSetValue('statePtr', C_STRUCTS.WebVRPositionState.angularVelocity.w, 'state.angularVelocity.w', 'double') }}};
        {{{ makeSetValue('statePtr', C_STRUCTS.WebVRPositionState.angularAcceleration.x, 'state.angularAcceleration.x', 'double') }}};
        {{{ makeSetValue('statePtr', C_STRUCTS.WebVRPositionState.angularAcceleration.y, 'state.angularAcceleration.y', 'double') }}};
        {{{ makeSetValue('statePtr', C_STRUCTS.WebVRPositionState.angularAcceleration.z, 'state.angularAcceleration.z', 'double') }}};
        {{{ makeSetValue('statePtr', C_STRUCTS.WebVRPositionState.angularAcceleration.w, 'state.angularAcceleration.w', 'double') }}};
      }
  
      return 1;
    },
  
    emscripten_vr_sensor_zero: function(deviceId) {
      var dev = WebVR.getDeviceByID(deviceId);
      if (!dev) return 0;
      dev.resetSensor();
      return 1;
    }
  };
  
  autoAddDeps(LibraryWebVR, '$WebVR');
  mergeInto(LibraryManager.library, LibraryWebVR);
  