I’m working on a 3D robotics construction (Robotic) platform for kids (ages 8-14) using Three.js. The project has been in development for a while, and I recently joined the team to implement new features.
The platform has various structural pieces (I, L, T, U shapes and flat plates). Each piece contains multiple holes - main hexagonal holes and smaller secondary holes between them. Some pieces are “interlocking” where main and secondary holes connect, others are non-interlocking where they remain separate. Flat plates have a front side (hexagonal recess) and back side (simple circle).
Important: In every GLB file we load, the 3D artist has already placed empty Object3D at exact positions of every hole. So each hole is a real Object3D child of the piece mesh with correct position and rotation.
My Task
I need to implement an intelligent magnetic snapping system. When a child drags one piece near another:
The system should detect nearby pieces and check all possible hole-to-hole connections between them
Valid connections require:
Correct distance between holes (main-main or main-secondary)
Holes facing each other
Both holes are not blocked by other pieces
Path between holes is clear
If at least one valid connection exists: piece turns GREEN and snaps to the nearest valid position
If no valid connection exists: piece turns RED and cannot be placed
My Question
Since I already have every hole as an empty Object3D with world position available, my first instinct is to:
When dragging, get all nearby pieces
Loop through every hole in piece A and every hole in piece B
Check distance, check normals (dot product), and then use Raycaster to:
Check if hole entrances are blocked (short ray outward)
Check if path between holes is clear (ray from hole A to hole B)
But I’m terrified about performance. With 40-50 pieces on screen and each piece having 3-8 holes, we’re talking about tens of thousands of raycasts per frame.
Is Raycaster the right tool here given that I already have exact hole positions as Object3D? Are there smarter ways to validate hole connections without heavy raycasting? Should I precompute something? Or am I worrying too much and Raycaster can handle this with proper optimization?
I need a solution that runs smoothly on mid-range devices and Chromebooks. Any advice would be greatly appreciated.
If I understand the problem. You need a way to know if pieces are fixed together and if a fixing point is valid/possible. Basically a valid path trough a grid of nodes, each linked to a piece.
For this I would use grid-based 3D pathfinding, using a “labyrinth” invisible to the user, it change depending on the pieces on screen (same for collisions checks). In theory, it could be done without raycasting, only using matrices/arrays. libraries already exist to help with this, check if you can make use of them.
First type (connectors): Built from 10×10×10 blocks. Each block = one grid cell.
Second type (flat plates): Width/length are 10 * 10, but thickness is 2 units. They still snap to the same 10×10 grid on X and Z axes, only Y axis differs.
So the grid system is consistent for pathfinding and hole alignment
If I had to do this, I’d split all checks into steps - first the quick steps, and at the end - the slowest steps. For example:
Check only when a piece is moved, not every frame
Check only against the moved piece (the active one), for two stationary pieces there is no need to check anything
Find nearby pieces and check only a few of the closest – if they are up to 5, this is OK, anything above 5 would be uncomfortable for the user
Find which of the 5 pieces can be joined with the active piece by checking orientation of holes and holes arrangements. This does not need raycasting at all.
Make quick raycasting between the center of active piece and the centers of the pieces left from step 4.
If the raycast says “clear”, then raycast the corresponding holes. Note, that raycasting can be made faster in (at least) 4 ways:
add maximal distance for raycasting;
define what object to check for intersection;
use bvh for much faster raycasting;
use custom fast “raycasting” that utilizes the fact that shapes are made of AABB elements – this option would be my favourite and most likely I’d implement it as it is not complex and does not require the complexity a raycaster needs
In my experience this would be enough. If not, you could “precompile” things - i.e. in a separate tool check all possible arrangements of pieces and check which one to which can be joined in which orientation and shift. Store this info, then use it for quick evaluation whether snapping is possible or not. This tool does not need to run fast, you run it only once.
But before diving into code, please, study all possible configurations and special cases. For example, if you only raycast from hole to hole, this might not be enough. See the picture - the green configuration is snappable, the red is not.
Good advice so far, although it depends how generalistic the functionality needs to be, this may not need ray casting at all…
if each of the empty object 3D’s that are coupled with each hole are labeled in a logical way it could be as simple as placing a box3 with a size set to a required tolerance at each of those points, on moving a part check if any of the nearby box3’s has the same logical label, if so check if the same labeled box3 intersects or contains its counterpart, if it does then you could just snap the moving part to the counterpart point to point directly using those empty objects positions…
Edit: if you needed a constant snapping radius you could use Spheres instead of Box3’s