# Using r3f to update camera rotation/lookAt value

**URL:** <https://discourse.threejs.org/t/using-r3f-to-update-camera-rotation-lookat-value/67734>\
**Category:** Questions\
**Tags:** r3f, perspective-camera\
**Created:** [July 4, 2024, 9:38pm UTC](https://discourse.threejs.org/t/using-r3f-to-update-camera-rotation-lookat-value/67734 "2024-07-04T21:38:17Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![SGMSA](https://avatars.discourse-cdn.com/v4/letter/s/b3f665/32.png) [@SGMSA](https://discourse.threejs.org/u/SGMSA)\
**Post date:** [July 4, 2024, 9:38pm UTC](https://discourse.threejs.org/t/using-r3f-to-update-camera-rotation-lookat-value/67734/1 "2024-07-04T21:38:17Z")

</div>

I’m pretty new to r3f and ThreeJS in general, I’m trying to achieve an effect where the camera starts on the outside of the model at `[0,50,50]` for instance, then animates to the centre of point at `[0,20,0]`. From here it will be able to spin around the centre point and look at different points on the model which is a ring around it. I have got the position animation down but for some reason I cannot figure out how to change the rotation/lookAt value (im not even 100% on which one I should be using), it always seems to be set to `[0,0,0]`.

You’ll see at the start i’m logging the positon and rotation to see where the camera is, this returns correct values that i’ve used to test it like [30,0,0] just to offset it and get a different results but there is no visible change. I feel like this is something I’m either approaching wrong or missing a trick here. Eventually I want to add in mouse tracking to look around but I seem to be running into a similar issue where nothing is updated despite values being logged.

```import
import { useThree, useFrame } from '@react-three/fiber';
import { OrbitControls, PerspectiveCamera } from '@react-three/drei';
import gsap from 'gsap';
import * as THREE from 'three';

function CameraController({ rotation, lookAtPosition, shouldAnimate, onAnimationComplete }) {
  const cameraRef = useRef();
  const controlsRef = useRef();
  const { set } = useThree();

  useEffect(() => {
    const lookAtVector = new THREE.Vector3(lookAtPosition);
    cameraRef.current.lookAt(...lookAtVector);
    console.log(cameraRef.current.position);
    console.log(cameraRef.current.rotation);
  }, []);

  useEffect(() => {
    if (cameraRef.current) {
      set({ camera: cameraRef.current });
      const lookAtVector = new THREE.Vector3(...lookAtPosition);
      cameraRef.current.lookAt(lookAtVector);
      cameraRef.current.updateProjectionMatrix();

      console.log(cameraRef.current.rotation); 
    }
  }, [set, lookAtPosition]);

  useEffect(() => {
    if (shouldAnimate && cameraRef.current) {
      gsap.to(cameraRef.current.position, {
        duration: 2,
        x: 0,
        y: 20,
        z: 20,
        ease: 'power2.out',
        onUpdate: () => {
          cameraRef.current.updateProjectionMatrix();
        },
        onComplete: () => {
          const lookAtVector = new THREE.Vector3(0, 20, 20);
          cameraRef.current.lookAt(lookAtVector);
          cameraRef.current.updateProjectionMatrix();
          console.log(cameraRef.current.rotation); 
          if (onAnimationComplete) onAnimationComplete();
        },
      });
    }
  }, [shouldAnimate, onAnimationComplete]);

  useFrame(() => {
    if (controlsRef.current) {
      controlsRef.current.update();
    }
  });

  return (
    <>
      <perspectiveCamera ref={cameraRef} position={[0,25, 0]}/>
      {cameraRef.current && (
        <OrbitControls
          ref={controlsRef}
          args={[cameraRef.current]}
          enableZoom={false}
          enablePan={true}
          enableRotate={true}
          minDistance={10}
          maxDistance={100}
          dampingFactor={0.2}
          rotateSpeed={0.5}
        /> 
      )} 
    </>
  );
}

export default CameraController;

```

---

<div class="post-metadata">

**Author:** ![AshhadDevLab](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/ashhaddevlab/32/44111_2.png) [@AshhadDevLab](https://discourse.threejs.org/u/AshhadDevLab)\
**Post date:** [July 5, 2024, 6:25am UTC](https://discourse.threejs.org/t/using-r3f-to-update-camera-rotation-lookat-value/67734/2 "2024-07-05T06:25:43Z")

</div>

You don’t need to assign a `ref` to the camera component. Instead import the `camera` as an object using the [`useThree`](https://docs.pmnd.rs/react-three-fiber/api/hooks#usethree) hook.  
Also in React for better functionality create `States` which makes the scene re-render every time it changes and hence update the changes. For Example for `lookAtVector` instead of doing:

```javascript
const lookAtVector = new THREE.Vector3(lookAtPosition)

```

you can do

```javascript
const [lookAtVector, setLookAtVector] = useState(new THREE.Vector3(looAtPosition));

```

Keep in mind that when you create a new `Canvas` using R3F it automatically adds a `PerspectiveCamera` to the scene, so when the camera object is imported using the `useThree` hook it automatically targets the pre added camera. So now if you want to change the position of the camera all you have to do is:

```js
const { set, camera } = useThree();

useEffect(() => {
      camera.position.set(0,25,0);
      camera.updateProjectionMatrix();
}, [])

```

This way you can make the code formatting better with respect to React, also even after this the issue still persist than I would suggest to force render a frame.

---

<div class="post-metadata">

**Author:** ![drcmda](https://avatars.discourse-cdn.com/v4/letter/d/91b2a8/32.png) [@drcmda](https://discourse.threejs.org/u/drcmda)\
**Post date:** [July 5, 2024, 6:39am UTC](https://discourse.threejs.org/t/using-r3f-to-update-camera-rotation-lookat-value/67734/3 "2024-07-05T06:39:23Z")

</div>

I use camera controls for this, there is a drei component for it as well, here’s an example [Enter portals - CodeSandbox](https://codesandbox.io/s/9m4tpc)

Animating the camera with an existing control is not trivial because the control animates the camera every frame, if something writes to the camera it’s a race condition. C-c fixes that because you can animate through the control.

In your case for instance you have a camera controlled by orbit controls. If gsap now animates the camera o-c will fight against it.

Ps. [GitHub - yomotsu/camera-controls: A camera control for three.js, similar to THREE.OrbitControls yet supports smooth transitions and more features.](https://github.com/yomotsu/camera-controls) It’s a wonderful library, complete replacement of orbit controls and much more capable.

---

<div class="post-metadata">

**Author:** ![AshhadDevLab](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/ashhaddevlab/32/44111_2.png) [@AshhadDevLab](https://discourse.threejs.org/u/AshhadDevLab)\
**Post date:** [July 5, 2024, 6:48am UTC](https://discourse.threejs.org/t/using-r3f-to-update-camera-rotation-lookat-value/67734/4 "2024-07-05T06:48:13Z")

</div>

You should definitely hear what this guy has to say. He’s the goat of R3F!

---

<div class="post-metadata">

**Author:** ![SGMSA](https://avatars.discourse-cdn.com/v4/letter/s/b3f665/32.png) [@SGMSA](https://discourse.threejs.org/u/SGMSA)\
**Post date:** [July 5, 2024, 10:50am UTC](https://discourse.threejs.org/t/using-r3f-to-update-camera-rotation-lookat-value/67734/5 "2024-07-05T10:50:23Z")

</div>

Thanks for all the help here guys, some really useful points. I rewrote the code and managed to fix quite a bit of it, but I’ve also updated my component to use the camera object directly within Three, removed the `OrbitControls` and setup the starting code that I’ll update to move the camera around using mouse tracking.

I’m still curious to know if this is better achieved in CameraControls though, as at the moment I’m directly manipulating the perspectiveCamera. If I wanted to add damping effects to the yaw and pitch or a raycaster (I intend on adding both), is that going to be an issue with this current method?

```javascript
import { useEffect, useState } from 'react';
import { useThree, useFrame } from '@react-three/fiber';
import gsap from 'gsap';
import * as THREE from 'three';

function CameraController({ shouldAnimate }) {
  const { set, gl, camera } = useThree();
  const [isAnimating, setIsAnimating] = useState(false);
  const [targetLookAt, setTargetLookAt] = useState(new THREE.Vector3(0, 10, -30));
  useEffect(() => {
    console.log()
    if (camera) {
      camera.position.set(-30, 30, 30);
      camera.lookAt(0, 0, 0);
      camera.updateProjectionMatrix();
    }
  }, [set]);

  useEffect(() => {
    if (shouldAnimate && camera) {
      setIsAnimating(true);

      const finalPosition = { x: 0, y: 10, z: 0 };

      const timeline = gsap.timeline({
        onUpdate: () => {
          camera.updateProjectionMatrix();
        },
        onComplete: () => {
          setIsAnimating(false);
          camera.lookAt(targetLookAt);
          document.addEventListener('mousemove', handleMouseMove);
        },
      });

      // Animate the camera position
      timeline.to(camera.position, {
        duration: 2,
        x: finalPosition.x,
        y: finalPosition.y,
        z: finalPosition.z,
        ease: 'power2.out',
        onUpdate: () => {
          const currentPos = new THREE.Vector3(
            camera.position.x,
            camera.position.y,
            camera.position.z
          );
          const targetQuat = new THREE.Quaternion().setFromRotationMatrix(
            new THREE.Matrix4().lookAt(currentPos, targetLookAt, new THREE.Vector3(0, 1, 0))
          );
          camera.quaternion.slerp(targetQuat, 0.1);
        }
      });

    }

    return () => {
      gsap.killTweensOf(camera.position);
      document.removeEventListener('mousemove', handleMouseMove);
    };
  }, [shouldAnimate]);

  const handleMouseMove = (event) => {
    if (isAnimating) return;

    const movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
    const movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;

    camera.rotation.order = 'YXZ';
    camera.rotation.y -= movementX * 0.002; // Yaw (horizontal rotation)
    camera.rotation.x -= movementY * 0.002; // Pitch (vertical rotation)

    camera.rotation.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, camera.rotation.x));
  };

  useFrame(() => {
    if (isAnimating) {
      // If using controls, update them here
    }
  });

  return (
    <perspectiveCamera
      fov={75}
      aspect={gl.domElement.clientWidth / gl.domElement.clientHeight}
      near={0.1}
      far={200}
    />
  );
}

export default CameraController;

```

---

<div class="post-metadata">

**Author:** ![AshhadDevLab](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/ashhaddevlab/32/44111_2.png) [@AshhadDevLab](https://discourse.threejs.org/u/AshhadDevLab)\
**Post date:** [July 5, 2024, 11:36am UTC](https://discourse.threejs.org/t/using-r3f-to-update-camera-rotation-lookat-value/67734/6 "2024-07-05T11:36:31Z")

</div>

No it will be totally fine, you should take a look at the documentation there you can find more helpful hooks and methods to make your workflow faster.

---

<div class="post-metadata">

**Author:** ![SGMSA](https://avatars.discourse-cdn.com/v4/letter/s/b3f665/32.png) [@SGMSA](https://discourse.threejs.org/u/SGMSA)\
**Post date:** [July 5, 2024, 1:51pm UTC](https://discourse.threejs.org/t/using-r3f-to-update-camera-rotation-lookat-value/67734/7 "2024-07-05T13:51:34Z")

</div>

Cool thanks, I was able to get the mouse tracking working and add a raycaster so I can update the content when certain meshes come into view, adding my code here for anyone who needs to use a similar solution.

```javascript
import { useEffect, useRef } from 'react';
import { useThree, useFrame } from '@react-three/fiber';
import gsap from 'gsap';
import * as THREE from 'three';

function CameraController({ shouldAnimate }) {
  const { gl, camera, scene } = useThree();
  const isAnimating = useRef(false);
  const targetLookAt = useRef(new THREE.Vector3(0, 10, -30));
  const mousePosition = useRef(new THREE.Vector2(0, 0));
  const velocity = useRef(new THREE.Vector2(0, 0));
  const dampingFactor = 0.9; // Damping factor for smooth slowing down
  const accelerationFactor = 0.02; // Acceleration factor for responsiveness
  const deadZoneSize = 0.1; // Define the size of the dead zone as a percentage of screen width/height

  const raycaster = useRef(new THREE.Raycaster());

  useEffect(() => {
    if (camera) {
      // Set initial camera position and look at the target
      camera.position.set(-30, 30, 30);
      camera.lookAt(0, 0, 0);
      camera.updateProjectionMatrix();
    }
  }, [camera]);

  useEffect(() => {
    const handleMouseMove = (event) => {
      const rect = gl.domElement.getBoundingClientRect();
      mousePosition.current.x = (event.clientX - rect.left) / rect.width * 2 - 1;
      mousePosition.current.y = -((event.clientY - rect.top) / rect.height * 2 - 1);
    };

    if (shouldAnimate && camera) {
      isAnimating.current = true;

      const finalPosition = { x: 0, y: 20, z: 0 };

      const timeline = gsap.timeline({
        onUpdate: () => {
          camera.updateProjectionMatrix();
        },
        onComplete: () => {
          isAnimating.current = false;
          camera.lookAt(targetLookAt.current);
          document.addEventListener('mousemove', handleMouseMove);
        },
      });

      // Animate the camera position
      timeline.to(camera.position, {
        duration: 2,
        x: finalPosition.x,
        y: finalPosition.y,
        z: finalPosition.z,
        ease: 'power2.out',
        onUpdate: () => {
          const currentPos = new THREE.Vector3(
            camera.position.x,
            camera.position.y,
            camera.position.z
          );
          const targetQuat = new THREE.Quaternion().setFromRotationMatrix(
            new THREE.Matrix4().lookAt(currentPos, targetLookAt.current, new THREE.Vector3(0, 1, 0))
          );
          camera.quaternion.slerp(targetQuat, 0.1);
        },
      });
    }

    return () => {
      gsap.killTweensOf(camera.position);
      document.removeEventListener('mousemove', handleMouseMove);
    };
  }, [shouldAnimate, camera, gl.domElement]);

  useFrame(() => {
    if (!isAnimating.current) {
      const { x, y } = mousePosition.current;

      // Calculate the dead zone bounds
      const deadZoneMinX = -deadZoneSize;
      const deadZoneMaxX = deadZoneSize;
      const deadZoneMinY = -deadZoneSize;
      const deadZoneMaxY = deadZoneSize;

      // Calculate the velocity based on mouse position
      if (x < deadZoneMinX) {
        velocity.current.x = (x + deadZoneSize) * accelerationFactor;
      } else if (x > deadZoneMaxX) {
        velocity.current.x = (x - deadZoneSize) * accelerationFactor;
      } else {
        velocity.current.x = 0;
      }

      if (y < deadZoneMinY) {
        velocity.current.y = (y + deadZoneSize) * accelerationFactor;
      } else if (y > deadZoneMaxY) {
        velocity.current.y = (y - deadZoneSize) * accelerationFactor;
      } else {
        velocity.current.y = 0;
      }

      // Apply damping to the velocity
      velocity.current.x *= dampingFactor;
      velocity.current.y *= dampingFactor;

      // Update camera rotation based on velocity
      camera.rotation.order = 'YXZ';
      camera.rotation.y -= velocity.current.x; // Yaw (horizontal rotation)
      camera.rotation.x += velocity.current.y; // Pitch (vertical rotation) - INVERTED HERE

      // Clamp the pitch rotation
      camera.rotation.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, camera.rotation.x));
    }

    // Update the raycaster direction based on the camera
    raycaster.current.setFromCamera(mousePosition.current, camera);

    // Get the list of objects the raycaster intersects
    const intersects = raycaster.current.intersectObjects(scene.children, true);

    if (intersects.length > 0) {
      console.log('Looking at:', intersects[0].object);
    }
  });

  return (
    <perspectiveCamera
      fov={75}
      aspect={gl.domElement.clientWidth / gl.domElement.clientHeight}
      near={0.1}
      far={200}
    />
  );
}

export default CameraController;

```

---

<div class="post-metadata">

**Author:** ![AshhadDevLab](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/ashhaddevlab/32/44111_2.png) [@AshhadDevLab](https://discourse.threejs.org/u/AshhadDevLab)\
**Post date:** [July 6, 2024, 7:49am UTC](https://discourse.threejs.org/t/using-r3f-to-update-camera-rotation-lookat-value/67734/8 "2024-07-06T07:49:08Z")

</div>

Instead of making a `useEffect` just for getting the `mousePosition`, import the `pointer` object from `useThree` which does the same thing for you but in a more effective way.

so instead of doing:

```javascript
useEffect(() => {
    const handleMouseMove = (event) => {
      const rect = gl.domElement.getBoundingClientRect();
      mousePosition.current.x = (event.clientX - rect.left) / rect.width * 2 - 1;
      mousePosition.current.y = -((event.clientY - rect.top) / rect.height * 2 - 1);
    };
)

```

You can simply do:

```javascript
const {set, raycaster, pointer} = useThree();
// Here `pointer` is a `Vector2` which contains the `x` and the `y` position of the mouse

console.log(pointer.x, pointer.y)

```
