Socket
A THREE.Group subclass representing a connection point on a Snappable.
It tracks interaction state, manages category-based compatibility, and animates its material colour through a priority-based state machine.
Warning
Meant to be used with ComponentMgr only.
Getting started
const socket = new Socket({ object3d })
// add to scene directly (to avoid, just to illustrate)
scene.add(socket)
- Finds the first mesh child →
_mesh(snap surface) - Finds any child with
userData.type = "up"→_upIndicator - Averages vertex normals of the mesh to compute
_normal - Sets
_upNormalfrom the up indicator's position, or defaults to(0, 1, 0) - Parses
userData.category/userData.catagoryinto_categoriesarray (comma-split, trimmed) - Applies a fresh
MeshStandardMaterialto the mesh for state-driven colouring
State Machine
Sockets have four independently tracked boolean states:
| State | Meaning |
|---|---|
isHovered | Cursor is over this socket |
isClicked | Mouse button is held down over this socket |
isCompatible | Current item has a socket that can connect here |
isBlocked | Placement at this socket would cause a collision |
Additionally, locked is a separate property (not in userData.state) that indicates a snapped connection.
Why is locked separate?
locked is kept separate because it has a fundamentally different lifecycle from the four interaction states.
Interaction states are transient (they are set and cleared every frame by tick() based on the current raycast result. locked is persistent)
It is set once when two blocks snap together and remains set until one of the blocks is removed from the scene.
Mixing it into userData.state would require tick() to explicitly avoid overwriting it on every frame, which would be fragile.
As a standalone property with its own getter/setter, locking and unlocking is self-contained and bidirectional: setting locked = false on one socket automatically clears the partner's reference too.
Colour Priority
When multiple states are active simultaneously, the colour with the highest priority wins:
| Priority | State | Default colour | Rationale |
|---|---|---|---|
| 1 | locked | #ff0000 | A locked socket is occupied and cannot accept another connection — this is the most important thing to communicate, so it overrides everything else |
| 2 | blocked | #333333 | Placement would cause a collision; the user should not click even though the cursor is hovering — takes priority over hover and click colours |
| 3 | isClicked | #00ff00 | Mouse button is held; placement is imminent — more specific than hover, so it wins |
| 4 | isHovered | #0000ff | Cursor is over this socket — should show clearly but yields to any actionable state above it |
| 5 | isCompatible | #ffff00 | A block is being previewed that could connect here — background highlight to guide the user, lowest active state |
| 6 | default | #ffffff | Socket is visible but no interaction is occurring |
1 = Highest, 6 = Lowest
The priority order is fixed in code and is not configurable at runtime.
To change which colour is shown for a given state, use componentMgr.setSocketColors() — but the order in which states override each other cannot be changed without modifying Socket.tick().
API
Constructor
Constructor initialises the new Socket instance
Parameters
object3d: The socket group from GLTF (must contain at least one mesh child)debug: Iftrue, adds arrow helpers showing the normal and up direction
Methods
canConnectTo(otherSocket) → boolean
Checks category compatibility between this socket and otherSocket using OR logic.
Parameters
otherSocket: The socket to check for compatibility
Returns
trueif either socket has an empty category list (wildcard)trueif any category string is shared between both socketsfalseif both have non-empty category lists with no overlap
setState(type, value) → this
Sets a state flag and dispatches the corresponding event if the value changed.
Parameters
type: Sets the state of the socket, one of'isHovered','isClicked','isCompatible','isBlocked'value: Sets the value of the state flag,trueorfalse
getState(type?) → boolean | object
Returns the value of a single state flag, or the full state object if type is omitted.
Parameters
type: Gets the state of the socket, one of'isHovered','isClicked','isCompatible','isBlocked'
Returns
boolean: The value of the state flagobject: The full state object
tick()
Lerps the socket mesh material colour toward the target colour determined by state priority (see Colour Priority below).
Also lerps opacity. Called automatically by Snappable.tick().
zFightFix(cameraPosition?)
Nudges the socket mesh along its normal by a distance proportional to camera distance, preventing z-fighting with the block surface.
Parameters
cameraPosition: The position of the camera
clone(recursive?) → Socket
Clones the original socket group and creates a new Socket from it.
Parameters
recursive: Whether to clone the object recursively
dispose()
Removes arrow helpers, disposes the mesh geometry and material.
Properties
normal
Socket forward direction (from averaged vertex normals), read-only
upNormal
Up reference direction (from up indicator position or (0,1,0)), read-only
categories
Parsed category strings, read-only
mesh
The socket's snap surface mesh, read-only
locked
get/set — when set to false, also unlocks the lockedPartner and clears both references
lockedPartner
get/set — setting a partner automatically locks this socket; clearing it unlocks both
debug
get/set — toggles arrow helpers for normal and up direction
Static property
Socket.updateColors(colors)
Updates the shared static colour configuration for all socket instances. Takes effect immediately on the next tick().
Parameters
colors: An object with the following properties:default: The default colour of the sockethovered: The colour of the socket when hoveredclicked: The colour of the socket when clickedcompatible: The colour of the socket when compatiblelocked: The colour of the socket when lockedblocked: The colour of the socket when blocked
Call via componentMgr.setSocketColors(colors) to use the public API.
Events
Events are dispatched via THREE.EventDispatcher when state changes:
isHovered
Hover state change
Parameters
socket: The socket that is hoveredname: The name of the socketvalue: The value of the state flag
isClicked
Click state change
Parameters
socket: The socket that is clickedname: The name of the socketvalue: The value of the state flag
isCompatible
Compatible state change
Parameters
socket: The socket that is compatiblename: The name of the socketvalue: The value of the state flag
isBlocked
Blocked state change
Parameters
socket: The socket that is blockedname: The name of the socketvalue: The value of the state flag
locked
Lock state change
Parameters
socket: The socket that is lockedname: The name of the socketvalue: The value of the state flag