How to show hands in WebXR in visionOS?

Hi, I’m working on a WebXR Demo which attempts to show LumaAI 3DGS in visionOS,but I can’t make Natural Input work.

  1. After “Enter VR”,if I allow all permissions, I can’t see hands in VR. But if I don’t allow hand-tracking permission, I can see my hands but can’t control anything. similar to this post: Reddit - Dive into anything
  2. Safari Web Inspector currently has a bug so I can’t see log, thus I don’t know if my code work.
import * as THREE from 'three';
import { LumaSplatsThree } from "@lumaai/luma-web";
import { VRButton } from "three/examples/jsm/webxr/VRButton.js";
import { DemoProps } from ".";

export function DemoVR(props: DemoProps) {
	let { renderer, camera, scene, controls, gui } = props;

	renderer.xr.enabled = true;

	let vrButton = VRButton.createButton(renderer);
	let canvas = renderer.getContext().canvas as HTMLCanvasElement;
	canvas.parentElement!.append(vrButton);


	let cameraGroup = new THREE.Group();
	scene.add(cameraGroup);

	cameraGroup.add(camera);
 


	const onSessionStart = () => {
	
		cameraGroup.position.set(0.9, -0.15, -0.3);
		cameraGroup.scale.set(0.1, 0.1, 0.1);

	};

    let controller1 = renderer.xr.getController(0);
    let controller2 = renderer.xr.getController(1);
    scene.add(controller1);
    scene.add(controller2);

    const lineMaterial = new THREE.LineBasicMaterial({ color: 0xff0000 });
    const geometry = new THREE.BufferGeometry().setFromPoints([new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, -1)]);
    const line = new THREE.Line(geometry, lineMaterial);
    line.name = 'line';
    line.scale.z = 5; 

    controller1.add(line.clone());
    controller2.add(line.clone());

    const movementScale = 0.1; 

    let isMoving = false; 

    function updateCameraPosition(controller) {
        const direction = new THREE.Vector3(0, 0, -1);
        direction.applyQuaternion(controller.quaternion);
        const offset = direction.multiplyScalar(-movementScale);
        cameraGroup.position.add(offset);
    }

    function handleSelectStart(event) {
        const controller = event.target;
        controller.userData.targetRayMode = event.data.targetRayMode;
		isMoving = true;
    }

    function handleSelectEnd() {
        isMoving = false;
    }

    controller1.addEventListener('selectstart', handleSelectStart);
    controller1.addEventListener('selectend', handleSelectEnd);
    controller2.addEventListener('selectstart', handleSelectStart);
    controller2.addEventListener('selectend', handleSelectEnd);

    renderer.setAnimationLoop(() => {
        if (isMoving) {
            updateCameraPosition(controller1);
            updateCameraPosition(controller2);
        }
    });
	
	renderer.xr.addEventListener('sessionstart', onSessionStart);
    
	let splats = new LumaSplatsThree({
		// Kind Humanoid @RyanHickman
		source: 'https://lumalabs.ai/capture/xxxx',
		// disable three.js shader integration for performance
		enableThreeShaderIntegration: false,
	});

	scene.add(splats);

	return {
		dispose: () => {
			splats.dispose();
			vrButton.remove();
			//renderer.xr.removeEventListener('sessionstart', onSessionStart);
		}
	}
}