API
new Latch(options?)
| Option | Default | Meaning |
|---|---|---|
reach |
3 |
Default maximum actor-to-hit surface distance, world units; finite and nonnegative. |
aimFar |
Infinity |
Maximum intersection distance from the aim origin; nonnegative. This is separate from actor reach. |
focusTolerance |
0.015 |
Keep current focus when its nearest eligible surface is within this many world units of the nearest aim hit. Finite and nonnegative. |
options is a read-only normalized snapshot. size is the current target count.
register(options): Target
| Field | Default | Meaning |
|---|---|---|
id |
required | Unique nonempty string. |
root |
required | Three.Object3D; an individual root can be registered once. |
label |
id |
Prompt text. |
mode |
'press' |
'press' or 'hold'. |
holdDuration |
1 |
Duration in seconds; finite and at least 0.000001. Used for hold targets. |
reach |
controller reach | Per-target finite nonnegative world-space reach. |
condition |
always available | Pure synchronous predicate, described below. |
The returned frozen Target has the normalized fields above, including condition when supplied. Configuration is copied; scene objects remain host-owned references. To change registration settings, remove and register the target again. Re-registering the same id creates a new target and cancels an old hold on the next update.
condition({ context, actorPosition, target, hit }) returns true to permit activation, false for a generic “Unavailable” reason, or a string describing the block. An empty string also produces “Unavailable”. The predicate receives a copy of actor position and a shallow intersection copy with a copied hit point. Treat all referenced host objects as read-only. Conditions may run multiple times per update, including immediately before completion. Do not consume inventory, change scene state, register/remove targets, or call update() from a condition. Reentrant controller mutation throws; predicate exceptions propagate to the host. If an update throws, Latch restores its prior interaction state, so a retry does not consume a press or silently lose a cancellation. Host-side predicate or custom-raycast side effects cannot be rolled back.
Target and occluder lifecycle
| Method | Result |
|---|---|
remove(id) |
Returns whether a target was removed. Pending cancel/blur events are returned at the next update. |
clear() |
Unregisters all targets; cancellation/blur appears at the next update. Occluders and input history remain. |
setOccluders(roots = []) |
Copies and deduplicates an array of Object3D roots; returns the controller. An empty array disables explicit occlusion. |
Removing a target never removes, hides, or disposes the corresponding scene object. Merely detaching an object from its scene does not unregister it: call remove, hide it, or clear its registration. To dispose a controller, stop sending updates and release it; it owns no GPU resources or DOM listeners. Release host geometry, materials, and listeners separately.
update(frame): InteractionResult
| Frame field | Requirement |
|---|---|
dt |
Finite, nonnegative elapsed seconds since the previous update. |
aimRay |
Three.Ray with finite origin and nonzero Vector3 direction. Direction is copied and normalized. |
actorPosition |
Finite Three.Vector3 in world coordinates. |
pressed |
Boolean current button state; a press means a false-to-true transition. |
suspended |
Optional boolean, default false. Cancels work and clears focus. |
context |
Optional host data for conditions; not copied or modified. |
camera |
Optional Three.Camera for view-dependent raycasting. |
| Result field | Meaning |
|---|---|
focus |
Focus snapshot or null. |
progress |
Hold fraction from 0 to 1; resets to 0 after activation or cancellation. |
holding |
Whether a hold is currently in progress. |
requiresRelease |
A held input cannot start another action until an update observes pressed: false. |
events |
Fresh ordered, frozen array of interaction events. Process after the update returns. |
Focus includes id, root, label, mode, holdDuration, point, distance, available, and reason. point is a copy in world space; distance is actor-to-hit distance. reason is null when available. Snapshot objects are shallow-frozen; Three vectors and host object references are not deep-frozen. Changing a snapshot's point does not change raycast geometry or the controller's hit point, but may change other references to that same snapshot.
Events
Every event includes type and a target: Focus snapshot.
| Type | Additional data | When returned |
|---|---|---|
focus |
— | A new reachable target is selected. |
blur |
reason |
Previous focus is lost, removed, or suspended. |
start |
— | A fresh press begins a hold. |
cancel |
reason |
An in-progress hold is interrupted. |
activate |
— | A press or a completed hold passes its final availability check. |
blocked |
reason |
A fresh press attempts an unavailable target, or a press fails its completion check. |
Cancellation reasons are 'focus-lost', 'removed', 'suspended', 'released', and 'unavailable'. Blur uses the first three. A target switch returns cancel if holding, then blur, then focus. A new hold returns start; a successful press returns activate. A hold that loses availability returns cancel, with the current blocked reason also available on result.focus.reason. No progress events are emitted; read the result once per frame.