Physics Joints

Connect rigid bodies with constraints. Learn about fixed, spherical (ball), revolute (hinge), and prismatic (slider) joints with limits and motors.

The Joint component connects two rigid bodies with a constraint. Joints restrict relative motion between bodies, enabling ragdolls, vehicles, doors, chains, and other mechanical systems. Web Engine supports Fixed, Spherical, Revolute, and Prismatic joints with configurable limits and motors.

Joint Types#

Fixed (Type 0)

Locks two bodies together with no relative motion. Used for compound objects and attachments.

Spherical (Type 1)

Ball-and-socket joint allowing rotation in all directions. Used for ragdoll shoulders and hips.

Revolute (Type 2)

Hinge joint rotating around single axis. Used for doors, wheels, and rotating mechanisms.

Prismatic (Type 3)

Slider joint moving along single axis. Used for pistons, drawers, and sliding doors.

Component Properties#

PropertyTypeDefaultDescription
typeenum0 (Fixed)Joint type: 0=Fixed, 1=Spherical, 2=Revolute, 3=Prismatic
connectedEntityentity0Entity ID of the second body
anchor1vec3[0,0,0]Local anchor point on first body
anchor2vec3[0,0,0]Local anchor point on second body
axis1vec3[1,0,0]Local rotation/slider axis on first body
axis2vec3[1,0,0]Local rotation/slider axis on second body
minfloat-1Minimum angle/distance limit
maxfloat1Maximum angle/distance limit
stiffnessfloat0Spring stiffness (0=rigid)
dampingfloat0Spring damping (0=no damping)

Fixed Joints (Type 0)#

Fixed joints lock two bodies together with zero relative motion. They maintain a fixed distance and orientation between the connected bodies.

// Fixed joint connecting two boxes
// Entity A: First box (has Joint component)
// Entity B: Second box
// Joint component on Entity A:
{
type: 0, // Fixed
connectedEntity: entityB_id,
anchor1: [0, 0, 0], // Center of first box
anchor2: [0, 0, 0], // Center of second box
// No axis needed for fixed joints
}
  • Locks bodies together (zero relative motion)
  • Maintains fixed distance and orientation
  • Good for: compound objects, attachments, connected structures
  • Example: attaching a weapon to a character's hand

Alternative to Fixed Joints

For static attachments, consider using parent-child hierarchy instead of joints. Fixed joints are best when you need dynamic bodies that remain connected during simulation.

Spherical Joints (Ball Socket) - Type 1#

Spherical joints allow rotation in all directions around a single point, like a ball-and-socket joint. Perfect for ragdoll shoulders, hips, and chains.

// Ragdoll shoulder joint
{
type: 1, // Spherical
connectedEntity: armEntity_id,
anchor1: [0, 0.5, 0], // Top of torso (shoulder)
anchor2: [0, -0.3, 0], // Top of arm
// Spherical joints don't use axis
}
// Chain link (multiple spherical joints)
{
type: 1, // Spherical
connectedEntity: nextLink_id,
anchor1: [0, -0.5, 0], // Bottom of this link
anchor2: [0, 0.5, 0], // Top of next link
}
  • Rotates freely in all directions
  • Single connection point
  • Good for: ragdoll joints, chains, rope segments, pendulums
  • Example: shoulder, hip, neck, wrecking ball

Spherical Joint Limits#

Spherical joints support cone limits to restrict rotation angle. The min and max properties define the cone angle in radians.

// Shoulder with 90-degree cone limit
{
type: 1, // Spherical
connectedEntity: armEntity_id,
anchor1: [0, 0.5, 0],
anchor2: [0, -0.3, 0],
min: 0,
max: Math.PI / 2, // 90 degrees
}

Revolute Joints (Hinge) - Type 2#

Revolute joints rotate around a single axis, like a door hinge or wheel axle. They support angle limits and motors for powered rotation.

// Door hinge (rotates around Y axis)
{
type: 2, // Revolute
connectedEntity: doorFrame_id,
anchor1: [-1, 0, 0], // Left edge of door
anchor2: [0, 0, 0], // Door frame position
axis1: [0, 1, 0], // Y axis (vertical hinge)
axis2: [0, 1, 0],
min: 0, // Fully closed
max: Math.PI / 2, // 90 degrees open
}
// Wheel axle (free rotation)
{
type: 2, // Revolute
connectedEntity: chassis_id,
anchor1: [0, 0, 0], // Wheel center
anchor2: [1, 0, 0], // Chassis attachment point
axis1: [1, 0, 0], // X axis rotation
axis2: [1, 0, 0],
// No limits = free rotation
}
  • Rotates around single axis
  • Supports angle limits
  • Can add motor for powered rotation
  • Good for: doors, wheels, pendulums, rotating platforms

Angle Limits#

MinMaxBehaviorUse Case
No limitsNo limitsFree 360° rotationWheels, fans, windmills
0π/2 (90°)Quarter turnDoor, hatch
-π/4π/4 (±45°)Swing both waysPendulum, gate
0π (180°)Half rotationLid, laptop screen
// Door that opens 90 degrees (one direction)
{
type: 2, // Revolute
min: 0,
max: Math.PI / 2, // 90 degrees
}
// Gate that swings both ways (±90 degrees)
{
type: 2, // Revolute
min: -Math.PI / 2, // -90 degrees
max: Math.PI / 2, // +90 degrees
}

Motors#

Add a motor to a revolute joint to create powered rotation. Use stiffness and damping to control motor strength and smoothness.

// Powered wheel (motor)
{
type: 2, // Revolute
connectedEntity: chassis_id,
axis1: [1, 0, 0],
axis2: [1, 0, 0],
stiffness: 1000, // Motor strength
damping: 50, // Motor smoothness
}
// Script to control motor
export default (api) => {
const motorSpeed = 10.0; // rad/s
return (dt) => {
if (api.input.forward) {
// Set motor target velocity
api.joint.targetVelocity = motorSpeed;
} else if (api.input.backward) {
api.joint.targetVelocity = -motorSpeed;
} else {
api.joint.targetVelocity = 0;
}
};
};

Prismatic Joints (Slider) - Type 3#

Prismatic joints allow linear motion along a single axis, like a drawer or piston. They support distance limits and motors for powered sliding.

// Sliding door (moves along X axis)
{
type: 3, // Prismatic
connectedEntity: doorFrame_id,
anchor1: [0, 0, 0], // Door center
anchor2: [0, 0, 0], // Frame center
axis1: [1, 0, 0], // Slide along X axis
axis2: [1, 0, 0],
min: 0, // Closed position
max: 2.0, // Open position (2m slide)
}
// Piston (vertical motion)
{
type: 3, // Prismatic
connectedEntity: cylinder_id,
anchor1: [0, 0, 0],
anchor2: [0, 0, 0],
axis1: [0, 1, 0], // Slide along Y axis
axis2: [0, 1, 0],
min: -0.5, // Extended down
max: 0.5, // Extended up
}
  • Linear motion along single axis
  • Supports distance limits
  • Can add motor for powered sliding
  • Good for: sliding doors, drawers, pistons, elevators

Spring Behavior#

Any joint type can have spring-like behavior using stiffnessand damping. Springs pull the joint back to a rest position.

// Soft spring (suspension)
{
type: 3, // Prismatic
stiffness: 100, // Spring force
damping: 10, // Resistance to motion
}
// Stiff spring (almost rigid)
{
type: 3, // Prismatic
stiffness: 10000,
damping: 100,
}
// No spring (pure constraint)
{
type: 3, // Prismatic
stiffness: 0,
damping: 0,
}
StiffnessDampingBehaviorUse Case
00Free motion (no spring)Free joints
10010Soft springSuspension, wobble
100050Medium springDoor closer, recoil
10000100Stiff springAlmost rigid, small flex

Common Patterns#

Ragdoll#

// Ragdoll spine (spherical joints)
// Torso -> Lower Torso
{
type: 1, // Spherical
connectedEntity: lowerTorso_id,
anchor1: [0, -0.5, 0], // Bottom of torso
anchor2: [0, 0.3, 0], // Top of lower torso
min: 0,
max: Math.PI / 4, // 45-degree bend
}
// Shoulder -> Arm (spherical with cone limit)
{
type: 1, // Spherical
connectedEntity: arm_id,
anchor1: [0.5, 0.4, 0], // Shoulder
anchor2: [0, 0.3, 0], // Top of arm
min: 0,
max: Math.PI / 2, // 90-degree cone
}
// Elbow (revolute hinge)
{
type: 2, // Revolute
connectedEntity: forearm_id,
anchor1: [0, -0.3, 0], // Bottom of upper arm
anchor2: [0, 0.25, 0], // Top of forearm
axis1: [1, 0, 0],
axis2: [1, 0, 0],
min: 0,
max: Math.PI * 0.75, // 135-degree bend
}

Automatic Door#

// Door component (revolute with motor)
{
type: 2, // Revolute
connectedEntity: doorFrame_id,
anchor1: [-1, 0, 0], // Hinge on left edge
anchor2: [0, 0, 0],
axis1: [0, 1, 0], // Vertical hinge
axis2: [0, 1, 0],
min: 0,
max: Math.PI / 2, // 90-degree open
stiffness: 500, // Motor strength
damping: 25,
}
// Script to auto-open/close
export default (api) => {
let isOpen = false;
const openAngle = Math.PI / 2;
const closeAngle = 0;
return async (dt) => {
// Detect player nearby
const nearby = await api.raycast(
api.position,
{ x: 0, y: 0, z: 1 },
2.0
);
if (nearby && nearby.eid === playerEntity) {
isOpen = true;
} else {
isOpen = false;
}
// Set motor target
api.joint.targetAngle = isOpen ? openAngle : closeAngle;
};
};

Chain / Rope#

// Create chain with multiple spherical joints
// Each link connects to the next with a spherical joint
// Link 1 -> Link 2
{
type: 1, // Spherical
connectedEntity: link2_id,
anchor1: [0, -0.5, 0], // Bottom of link 1
anchor2: [0, 0.5, 0], // Top of link 2
}
// Link 2 -> Link 3
{
type: 1, // Spherical
connectedEntity: link3_id,
anchor1: [0, -0.5, 0],
anchor2: [0, 0.5, 0],
}
// Continue for all links...

Vehicle Suspension#

// Wheel suspension (prismatic with spring)
{
type: 3, // Prismatic
connectedEntity: chassis_id,
anchor1: [0, 0, 0], // Wheel center
anchor2: [1, 0, 0], // Chassis attachment
axis1: [0, 1, 0], // Vertical suspension
axis2: [0, 1, 0],
min: -0.2, // Compressed
max: 0.2, // Extended
stiffness: 1000, // Suspension stiffness
damping: 50, // Suspension damping
}
// Wheel rotation (revolute around axle)
{
type: 2, // Revolute
connectedEntity: suspensionBody_id,
axis1: [1, 0, 0], // Axle rotation
axis2: [1, 0, 0],
// No limits = free rotation
}

Best Practices#

  • Use anchor points to position joints correctly
  • Set appropriate axis directions for revolute/prismatic joints
  • Add limits to prevent unrealistic motion
  • Use stiffness and damping for spring behavior
  • Keep joint chains short (3-5 links) for stability
  • Use fixed joints sparingly - parent-child hierarchy is often better
  • Test joint stability with different body masses
  • Avoid connecting static bodies with joints (use world anchors instead)

Joint Stability

Very long chains of joints (10+ links) can become unstable. Break them into smaller segments or use constraints instead of physical joints.

Debugging Joints#

// Debug joint in script
export default (api) => {
return (dt) => {
// Log joint angle (revolute)
api.log(`Joint angle: ${api.joint.currentAngle}`);
// Log joint distance (prismatic)
api.log(`Joint distance: ${api.joint.currentDistance}`);
// Check if limit reached
if (api.joint.currentAngle >= api.joint.max) {
api.log("Joint at max limit!");
}
};
};

Visual Debugging

Enable physics debug rendering in the editor to visualize joint anchor points, axes, and limits. This helps identify configuration issues.

Physics | Web Engine Docs | Web Engine Docs