Google-map type scroll towards mouse (2D)

Hello everyone,
I have a 2D grid with a PerspectiveCamera on top and I’ve been trying for days to figure out how to properly zoom towards the mouse cursor, just like Google Maps does. Basically, the cursor is on top of an X/Y coordinate and as I zoom in & out I need it to remain on top of that coordinate.

I’ve tried to use the normalized positioning of the cursor but with no success. I’ve tried multiple cameras; I’ve tried to pre-calculate where the cursor is going once I scroll and to be next and adjust the camera positioning with that delta; I’ve tried to calculate zoom & position ratio relationships. Now I’ve exhausted my options.
I feel this is a mathematical challenge rather than a programming one.

I’ve also searched extensively online but no solution comes close to what I am trying to achieve.
Has anyone faced the same challenge?

Thanks in advance!

It’s pretty easy to do it in 2D, but gets a little tricky with 3D.
For 2D, make sure that the camera is facing (0,0,-1) and up is (0,1,0).
Then for zooming, basic idea is to move the camera to the pointer location xy, perform zooming, then move it back(but this time scale the vector to the ratio of old and new distance to the map).

Hi @repalash, thanks for the help.
I think I understand that approach, and that’s what I tried to do with “calculate zoom & position ratio”, but completely failed at succeeding.

How do I create the vector (assuming from camera to cursor), which vector, and how do “I move it back” as you say?

Thanks so much

This is just a 2D vector from (0,0) to the pointer ndc. Let’s say P.
To move the camera add P to the camera position, then zoom and subtract P * (distance to target/old distance to target).

I will share code for 3D version I wrote in a while.

1 Like

Hi @repalash,
I’m trying this but without any luck:

    const vector = new THREE.Vector3(
      ( event.sourceEvent.clientX / window.innerWidth ) * 2 - 1,   //x
      -( event.sourceEvent.clientY / window.innerHeight ) * 2 + 1,  //y
    const distanceBefore =;
    const distanceAfter = new THREE.Vector3(,, zoomLevel).distanceTo(vector);
    const ratio = distanceAfter/distanceBefore;
    vector.unproject(;;                                 // add P to the camera position,, newZoomLevel); // then zoom
    const pos = ratio ))          // then subtract P * (distance to target / old distance to target)

I’m wondering if what I’m trying to achieve counts as 2D or is actually 3D, as when I zoom in & out, there is an impact on the Z-Axis ofc.

Does this make sense? Could you share your insights on that 3D code?


In your code I, you are taking the 3d distance, it should be just between x, y. Use a Vector2 to calculate the distance and the ratio. If you could share a jsfiddle/codepen, I can help better.

I have uploaded my code on GitHub, you can check it out here: GitHub - repalash/OffsetOrbitControls: Modified three.js OrbitControls with target offset.

In the example there, try to pan the model and then zoom in, it will zoom in and rotate on the model instead of the screen center.

This basically modifies orbit controls to have an offset to the target, so you can zoom at that offset. This, in your case, will be the 2d mouse position.

1 Like

Hi @repalash,
added a mini version of the code here
I’ll be trying to go over it today again.

Thanks for everything!