Scanat uses a 3D Gizmo for moving, rotating, and positioning virtual objects. Axis handles control translation, arcs control rotation, and the center area supports planar movement.
This interaction works well with a mouse, but not when transferred directly to phones and iPads. Gizmo handles are visually small and assume a precise pointer. On touch screens, users can miss the intended handle, hit a neighboring one, or trigger an operation while simply moving across the scene.
The goal was not to invent a separate mobile control system. It was to preserve the desktop interaction model while adapting the input mechanics for touch.
The main difference between touch and mouse input is not visual. It is the amount of precision and intent information available before an action begins.
| Dimension | Touch | Mouse |
|---|---|---|
| Target precision | Low; fingertip contact is broad | High; pointer location is precise |
| Hover | No stable hover state | Targets can be highlighted before click |
| Intent | Scene movement and handle drag can conflict | Pressing a target usually signals intent clearly |
| Multi-point input | Pinch and other gestures share the same surface | Wheel and buttons can separate actions |
The mobile problem therefore came down to two things: making the target easier to hit, and reducing ambiguity between passing over the screen and intentionally dragging a handle.
After testing several interaction models, the final version stayed close to the desktop behavior: long-press a handle for 260ms, then drag it directly.
Compared with selecting first and dragging elsewhere on the screen, or adding a separate control pad, this keeps the interaction easier to understand. The user still manipulates the visible Gizmo itself; touch simply adds a short intent-confirmation step.
Scanat is not a professional modeling tool, so introducing a separate mobile interaction language for this operation would add more complexity than value.
The most important mobile adjustment was not making the Gizmo visually larger. It was separating the rendered handle size from the interactive hit area.
Each handle keeps its original visual scale, while a larger invisible geometry is used for hit testing. The visual can stay light and precise in the 3D scene, while the finger gets a much more forgiving target.
When hit areas overlap, a priority rule decides which target should respond. For example, an axis-end arrow can take priority over the axis body or planar region.
The 260ms delay came from repeated testing on real devices rather than from a preset standard.
A shorter delay caused more accidental activation while moving across the scene. A longer one made the control feel slow to respond. The useful value was the point where both problems became acceptable in actual use.
This delay only applies to touch. A mouse does not need the same intent check and can begin dragging immediately.
Input capability is also more useful than user-agent detection for choosing the interaction. A media query such as pointer: fine describes the active pointer more directly than asking whether the device happens to be an iPad or desktop computer.
The interactive prototype exposed two issues that were difficult to see in static designs.
First, dimension editing originally opened on a simple tap. On touch devices, passing over the label could trigger the editor unintentionally. Changing this to a long-press reduced the problem.
Second, the timing for showing and hiding dimension labels became clearer only after repeatedly dragging the object in the working prototype. The states were easy to describe individually, but harder to specify correctly as a continuous touch interaction.
These are both examples of parameters that depend on a real input surface. Hit areas, delay thresholds, and accidental activation are difficult to validate from static mockups alone.
The prototype was useful because it exposed questions that only appear once the interaction runs on a real touch device.
Static designs are good at defining structure, states, and visual hierarchy. They are less reliable for accidental touches, hold delays, and drag feel. For this kind of 3D interaction, the working prototype is part of the design process rather than a final presentation artifact.
Hit Testing. Invisible geometry expands the interactive target while keeping the rendered handle small. Raycaster still treats these objects as normal scene geometry, and priority values resolve overlaps.
const hitSphere = new THREE.Mesh(
new THREE.SphereGeometry(0.2, 16, 16),
new THREE.MeshBasicMaterial({
transparent: true,
opacity: 0.001
})
)
hitSphere.userData.priority = 80Pointer Capability.
if (window.matchMedia('(pointer: fine)').matches) {
// high-precision pointer
} else {
// touch-first interaction
}Interaction State. The scene keeps explicit fields such as selected, active, hover, and operationStart. Canceling an operation restores the starting snapshot instead of maintaining a separate undo stack.
React / Three.js Boundary. A small imperative API bridges the React UI and the Three.js scene so declarative UI state and imperative 3D operations remain separated.