How can I make this calculation or is there any built-in function?

I’ve previously asked this question.

Now my problem gets a bit tricky as the solution works but not in this new case.

I’m creating a knob so I need to do approximately this ( more detailed formula below )

let radian = Math.atan2( cursor.y, cursor.x );

to get the angle of the mouse compared to the center of the knob ( I’m using RingGeometry to build the knob );

now depends of the computed values I get different angles based on how it is placed or rotated.

At this point the computed mouse values I have are normalized mouse and world mouse position

the common way of getting the angle in radian is to do something like

let radian = Math.atan2( cursor.y - Scene1.knob1.position.y,  Scene1.knob1.position.x - cursor.x );

if ( radian < 0 ) {
  radian = (Math.PI * 2) + radian;

How could I achieve this the most straigthforward and Three.js manner?


Cube_Angle.htm (5.0 KB)

Many thanks for your example but are you sure it works if the object is rotated or center moves? At this point I can’t see an angle related to the object but more of the viewport. Thnaks in advance.

Correct, I thought you could enjoy the challenge of solving the rest.
If that is not the case, then: Cube_Angle.htm (5.6 KB)

@rrrr_rrrr Again Thank you for your time.
There’s a slight problem here, my main point is to find a solution to my problem, I’m already able to do the calculation for an angle based on viewport.
I’ve looked at your example and maybe I misunderstand something or the explanation of my problem was unclear but when I rotate the object and place the mouse at the to the same place it was on the object before I rotated it the angle is not the same. Do you have any other clues. I’m sorry taking your time but at this point in my project I only have few time to learn back trigonometry from start :slight_smile: I hope you’ll understand.

The second attachment does what was designed to do.
As to why is not working on your environment, make sure the geometry is centered.

If you want to discuss this any further,

  • Any Error Log
  • A video or image
  • A JSFiddle with skeleton coding

is required.


Again thanks

So before diving into examples and video, I must say I don’t get the same result in JSFiddle and in my editor ( I will need to inspect why but the radian is inverted ). But appart from that the problem remains the same.
When I move my mouse and intersect with some object I can retrieve the intersection point inside the intersectObject and then convert it to local which apparently works as it is following the rotation and translation. Now my problem is I can’t really use this variable all along as when user clicks and start going out of the knob scope the intersetion points do not exist anymore ( as there is no intersection anymore ) and I don’t know how to keep the same conversion so my knob value keeps moving accordingly.

Here’s the Fiddle

And here’s a video on my app

knob problem


The problem here is twofold:

  • you use the ray casting from the camera
  • you use the ray casting to the mouse coordinates

Notice how the object itself is missing as the ray destination and how using the camera as the origin of the ray is only producing results if the object is on the camera to mouse coordinates direction (again, with no real connection to the object itself).

You have multiple solutions to this. One that is to create an large enough double-sided invisible plane that “contains” the entire object or on which the object “sits on”), use the existing ray to detect the intersection with that plane, then cast a second ray from the point of intersection with the plane to, say, the center of the object in order to detect the desired intersection. Another is to just project the ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 mouse coordinates vector on a similar plane via Vector3.projectOnPlane() in order to cast a ray from the projection point to your object’s center.

Or even simpler, don’t cast a second ray at all and just compute the angle made by the first intersect point, the center of the object, and a point corresponding to where your ring “starts” aka its theta start angle (I guess it could be a copy of the position vector of one of the vertices of the object that’s positioned at its start point as well, assuming you’re doing an .applyMatrix4(yourobject.matrixWorld) to account for the object’s global transform), followed by properly setting the tinted ring to that angle:

Or, there may be a better or similer idea that I didn’t think of…

1 Like

Many thanks for your time and explanations I think I got it.

My problem with your solution is I need to add an invisible plane or maybe I’m wrong and you call plane a vector calculation without adding any extra objects to scene.

if that’s not the case and you need to add an additional object, If user touches ( I’m talking tactile and fingertouch ) other objects the invisible plane may come first or even worst if multiple objects have other planes then it is going to be a mess. If Camera moves and the plane is too small then it may not work anymore. I see a lot of possible issues and conception UX problems with my app.

If that’s the case and it’s only maths could you add code or pseudo code please so I’ll be able to reproduce the logic for other objects.

Your explanation and time you spent to make the graphic is insane, I’ll understand if you wan’t me solving the rest of the problem on my own.


No, all the variants were meant to be alternatives. Meaning that the plane variant is not the same as projecting a vector on an “imaginary” plane represented by its normal. That being said, in theory (and discarding various disadvantages like the ones you mentioned), they would achieve the same thing: get a suited origin for the second raycaster so that a ray from it is able to intersect the object and give you the needed point.

Anyway, making this an easier to solve planar problem is more convenient. I’ll see what I can do on your fiddle, but I don’t guarantee anything. This will be a good oportunity to work with the ring geometry and raycasting from the mouse coordinates for the first time (I bet you didn’t know that, hehe), though I’m pretty sure a solution can be found. I might not be the most proficient in terms of Three.js methods or knowing right away what some matrix / quaternion operation will do, but I’m persistent and have plenty of ideas.

Of course I’ll try to help you more on that, since you put things that way. :blush:

1 Like

Many thanks that’s great.
Yes so if my understanding is correct you mentionned a solution with an imaginary plan?
If you are able to make this work this would be great.
Can’t wait to hear more from you…


Alright, so the best I could do is to allow “intersections” even if the raycster is not over the object, and that was surprisingly easy. What I couldn’t do, unfortunately, is make the angle accurate for a rotated object.

What happens is that both the fiddle here and here (the first variant doesn’t use raycaster at all, by the way) yield the correct angle to draw the second ring … but only when the object is not rotated at all. This is despite using, in the second variant, the “imaginary” (as in, not an Object3D, but a math class) plane that I rotated precisely as the object in order to get the right y and x from the intersection for the atan2() to yield an accurate angle on that plane (instead of the plane of the screen, that is).

Not sure why this happens, but at least you have the mouse position producing results even if the mouse is not over the object. And, of course, it will work perfectly if you comment the group’s rotation. So, you only have the angle problem left to deal with.

P.S. I took the liberty to adjust the camera distance in both fiddles, and add a “contour” object that I had some plans with (irrelevant now, since the angle thing is persistent no matter what) in the second fiddle, to help me visualize the thing better. For my usage, I reformatted the code a bit, since I’m not a big fan of Mr.doob’s all over the place style - hope you don’t mind.

1 Like

Sorry for the late reply but I was messing with your code seeing if I could make it work without any chance unfortunately. The plane object you created is awesome as it is not even added to the scene which really avoid messing up with the whole logic of it.
As I don’t have enough knowledge I can’t even debug your code see if you missed something somewhere.
@rrrr_rrrr do you have any clue?
I really want to thank you for your time and I’m sure it’s doable but there’s a reason why I’ve asked the question :).
If you think of something else don’t hesitate if not I will probably give a chance to stackoverflow.


Yes, I’m sure it’s doable as well, I felt I was an inch close to the solution. There might be a solution - or at least some possibilities - related to the angle thing, and I will continue to explore them:

  • aspect ratios
  • perspective distorsions
  • adjusting ray directions

What’s clear is that there are some patterns in how angles are not entirely matched, like going ahead or behind what they are supposed to be except for the PI / 2 multiples, or a bigger difference on the part of the object / plane that is closer to the viewer, etc. That is consistent with either a factor they would need to be multiplied with, or a different scale needed in some areas.

For now I also added a plane helper to the whole thing and it helped me realize some slight inconsistencies. Now, if one of those is the cause for not completely matching angles, then problem solved. :wink:

Yeah, the Plane class is useful, apparently, and has the benefit of not bothering the view and being infinite, but the rays from the raycaster still need to be casted from a safe enough distance from the plane, so that they can intersect it :slight_smile:


on my side as you can see in the original code I guess the ‘point’ property of intersectObject is converted from worldToLocal and this works do you have any idea how we could get the same coordinate system as the ‘point’ property but for the intersection with the plane then perhaps perform worldToLocal conversion?
I’ve tried it without success but maybe you have a clearer view


As a matter of fact, you’re absolutely right! And @prisoner849 as well, as the one providing you with the initial advice. :+1:

I tried various other variants before finally realizing that this was the issue. Thing is, because Plane is just a math class and not an Object3D, one can’t use .worldToLocal for the said vector on the plane. So, I rotated “backwards” the vector with the inverse of the group’s rotation (apparently, I kind of start to like quaternions, after all) so that the point on the original rotated plane is “translated” into a point as if it was on a plane coplanar with the world’s X and Y axis (basically, the unrotated plane). When that happens, the X and Y of the vector / point are the equivalent of its local coordinates on the original rotated plane.

The whole change could have been done by adding an .applyQuaternion(group.quaternion.clone().invert()); to the cursor variable, but I added and fixed some things in the (forked) fiddle in the meantime, added a plane helper to make it more obvious, and included some comments that hopefully can help you understand better how it is done. Feel free to remove what you don’t need, in your final version.

Cheers and enjoy! :beers:

1 Like

Wow this is amazing.
I’m not sure to understand everyhting you’ve made.

But that is amazing again I’ll implement this on my side for my project.

No problem, glad to help. You can ask, if you didn’t understand something - I can explain.
I’m not an expert in Three. js, at least not yet, but I never do something before I first try to understand its principle. So, I can safely explain all that’s in there.

In fact I’m sorry but if you move the object from the original position (0,0,0) it doesn’t work any more. Any ideas( I’ll leave you alone after that haha)

Ouch! Bad, bad object :))
Joking aside, will see what I can do later on.

1 Like