I am looking for a solution that will allow the user in the browser to look around using the mouse and click buttons.
In the hotspots sample I can move the camera and still click on the hotspots, but that is orbit, third person view.
I also tried the First Person Controller with all movement speeds set to 0, but then as soon as I click the left mouse button the mouse can only be used for looking around, I can no longer click buttons.
I noticed that when I press escape to get out of the first person mode I can click the button. But that is not user-friendly.
In XR I have achieved what I want using the XR Controller and there also settting the movement speed to 0. I can then click the button using the trigger of the right controller.
How can I achieve the same behaviour in the browser?
Replace the FirstPersonController with a custom mouselook script that uses pointer lock only on right mouse button. Left click stays free for buttons.
In Needle Engine, the built-in FirstPersonController always locks the pointer on left mouse. Instead, write a lightweight controller:
import { Behaviour, Input } from "@needle-tools/engine";
export class RightMouseLook extends Behaviour {
private isLocked = false;
update() {
if (Input.getMouseButtonDown(2)) { // right button
document.body.requestPointerLock();
this.isLocked = true;
}
if (this.isLocked && Input.getKeyDown("Escape")) {
document.exitPointerLock();
this.isLocked = false;
}
if (this.isLocked) {
const delta = Input.mouseDelta;
// rotate the camera / rig using delta.x and delta.y
this.gameObject.rotation.y -= delta.x * 0.002;
// ...
}
}
}
Attach it to your camera rig. Left click now works for any Needle UI or HTML buttons without conflict.
If you need the speed of the FirstPersonController, check its inspector – some versions support a “Lock Button” property (set to Right). Otherwise, the custom script above is the cleanest fix.
If buttons are inside the 3D scene (Needle UI), they’ll work; if they’re HTML overlays, they’ll also work because left click isn’t captured.
The getMouseButtonDown, getKeyDown, and mouseDelta are part of Needle Engine’s Input class. Make sure you import it correctly:
import { Input } from "@needle-tools/engine";
If you used Input from three.js or wrote window.Input, that won’t work. Also confirm your Needle Engine version is up to date (3.32.x+). If you’re in a plain HTML project (no TypeScript build step), you can access the Input via the NeedleEngine namespace.
If the errors persist, share the exact error text or a minimal reproduction at engine.needle.tools/new.
This was a typo in my copy of the text. I can not copy the actual error mesage, Visual Studio does not allow it. In the code the method names are in the right camel case. The code is as in the script that was suggested in the first post.
Fair enough — mouseDelta isn’t the one to rely on. Try this.context.input.getMouseDelta(); if that’s missing too, your @needle-tools/engine version is just older than the docs snippet that mentions it.
For a look-around camera you don’t need the Input class at all. Request pointer lock and read raw deltas:
Then a plain listener on document while document.pointerLockElement is set, using evt.movementX / evt.movementY. That’s version-independent and gives smooth mouse-look.
Post your engine version here and I’ll confirm the exact API — the input methods changed between releases. See the Handle User Input guide [9].