Web Engine Docs
Preparing documentation
Use the search bar to quickly find any topic
Preparing documentation
Use the search bar to quickly find any topic
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.
Locks two bodies together with no relative motion. Used for compound objects and attachments.
Ball-and-socket joint allowing rotation in all directions. Used for ragdoll shoulders and hips.
Hinge joint rotating around single axis. Used for doors, wheels, and rotating mechanisms.
Slider joint moving along single axis. Used for pistons, drawers, and sliding doors.
| Property | Type | Default | Description |
|---|---|---|---|
| type | enum | 0 (Fixed) | Joint type: 0=Fixed, 1=Spherical, 2=Revolute, 3=Prismatic |
| connectedEntity | entity | 0 | Entity ID of the second body |
| anchor1 | vec3 | [0,0,0] | Local anchor point on first body |
| anchor2 | vec3 | [0,0,0] | Local anchor point on second body |
| axis1 | vec3 | [1,0,0] | Local rotation/slider axis on first body |
| axis2 | vec3 | [1,0,0] | Local rotation/slider axis on second body |
| min | float | -1 | Minimum angle/distance limit |
| max | float | 1 | Maximum angle/distance limit |
| stiffness | float | 0 | Spring stiffness (0=rigid) |
| damping | float | 0 | Spring damping (0=no damping) |
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, // FixedconnectedEntity: entityB_id,anchor1: [0, 0, 0], // Center of first boxanchor2: [0, 0, 0], // Center of second box// No axis needed for fixed joints}
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 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, // SphericalconnectedEntity: 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, // SphericalconnectedEntity: nextLink_id,anchor1: [0, -0.5, 0], // Bottom of this linkanchor2: [0, 0.5, 0], // Top of next link}
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, // SphericalconnectedEntity: armEntity_id,anchor1: [0, 0.5, 0],anchor2: [0, -0.3, 0],min: 0,max: Math.PI / 2, // 90 degrees}
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, // RevoluteconnectedEntity: doorFrame_id,anchor1: [-1, 0, 0], // Left edge of dooranchor2: [0, 0, 0], // Door frame positionaxis1: [0, 1, 0], // Y axis (vertical hinge)axis2: [0, 1, 0],min: 0, // Fully closedmax: Math.PI / 2, // 90 degrees open}// Wheel axle (free rotation){type: 2, // RevoluteconnectedEntity: chassis_id,anchor1: [0, 0, 0], // Wheel centeranchor2: [1, 0, 0], // Chassis attachment pointaxis1: [1, 0, 0], // X axis rotationaxis2: [1, 0, 0],// No limits = free rotation}
| Min | Max | Behavior | Use Case |
|---|---|---|---|
| No limits | No limits | Free 360° rotation | Wheels, fans, windmills |
| 0 | π/2 (90°) | Quarter turn | Door, hatch |
| -π/4 | π/4 (±45°) | Swing both ways | Pendulum, gate |
| 0 | π (180°) | Half rotation | Lid, laptop screen |
// Door that opens 90 degrees (one direction){type: 2, // Revolutemin: 0,max: Math.PI / 2, // 90 degrees}// Gate that swings both ways (±90 degrees){type: 2, // Revolutemin: -Math.PI / 2, // -90 degreesmax: Math.PI / 2, // +90 degrees}
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, // RevoluteconnectedEntity: chassis_id,axis1: [1, 0, 0],axis2: [1, 0, 0],stiffness: 1000, // Motor strengthdamping: 50, // Motor smoothness}// Script to control motorexport default (api) => {const motorSpeed = 10.0; // rad/sreturn (dt) => {if (api.input.forward) {// Set motor target velocityapi.joint.targetVelocity = motorSpeed;} else if (api.input.backward) {api.joint.targetVelocity = -motorSpeed;} else {api.joint.targetVelocity = 0;}};};
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, // PrismaticconnectedEntity: doorFrame_id,anchor1: [0, 0, 0], // Door centeranchor2: [0, 0, 0], // Frame centeraxis1: [1, 0, 0], // Slide along X axisaxis2: [1, 0, 0],min: 0, // Closed positionmax: 2.0, // Open position (2m slide)}// Piston (vertical motion){type: 3, // PrismaticconnectedEntity: cylinder_id,anchor1: [0, 0, 0],anchor2: [0, 0, 0],axis1: [0, 1, 0], // Slide along Y axisaxis2: [0, 1, 0],min: -0.5, // Extended downmax: 0.5, // Extended up}
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, // Prismaticstiffness: 100, // Spring forcedamping: 10, // Resistance to motion}// Stiff spring (almost rigid){type: 3, // Prismaticstiffness: 10000,damping: 100,}// No spring (pure constraint){type: 3, // Prismaticstiffness: 0,damping: 0,}
| Stiffness | Damping | Behavior | Use Case |
|---|---|---|---|
| 0 | 0 | Free motion (no spring) | Free joints |
| 100 | 10 | Soft spring | Suspension, wobble |
| 1000 | 50 | Medium spring | Door closer, recoil |
| 10000 | 100 | Stiff spring | Almost rigid, small flex |
// Ragdoll spine (spherical joints)// Torso -> Lower Torso{type: 1, // SphericalconnectedEntity: lowerTorso_id,anchor1: [0, -0.5, 0], // Bottom of torsoanchor2: [0, 0.3, 0], // Top of lower torsomin: 0,max: Math.PI / 4, // 45-degree bend}// Shoulder -> Arm (spherical with cone limit){type: 1, // SphericalconnectedEntity: arm_id,anchor1: [0.5, 0.4, 0], // Shoulderanchor2: [0, 0.3, 0], // Top of armmin: 0,max: Math.PI / 2, // 90-degree cone}// Elbow (revolute hinge){type: 2, // RevoluteconnectedEntity: forearm_id,anchor1: [0, -0.3, 0], // Bottom of upper armanchor2: [0, 0.25, 0], // Top of forearmaxis1: [1, 0, 0],axis2: [1, 0, 0],min: 0,max: Math.PI * 0.75, // 135-degree bend}
// Door component (revolute with motor){type: 2, // RevoluteconnectedEntity: doorFrame_id,anchor1: [-1, 0, 0], // Hinge on left edgeanchor2: [0, 0, 0],axis1: [0, 1, 0], // Vertical hingeaxis2: [0, 1, 0],min: 0,max: Math.PI / 2, // 90-degree openstiffness: 500, // Motor strengthdamping: 25,}// Script to auto-open/closeexport default (api) => {let isOpen = false;const openAngle = Math.PI / 2;const closeAngle = 0;return async (dt) => {// Detect player nearbyconst 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 targetapi.joint.targetAngle = isOpen ? openAngle : closeAngle;};};
// Create chain with multiple spherical joints// Each link connects to the next with a spherical joint// Link 1 -> Link 2{type: 1, // SphericalconnectedEntity: link2_id,anchor1: [0, -0.5, 0], // Bottom of link 1anchor2: [0, 0.5, 0], // Top of link 2}// Link 2 -> Link 3{type: 1, // SphericalconnectedEntity: link3_id,anchor1: [0, -0.5, 0],anchor2: [0, 0.5, 0],}// Continue for all links...
// Wheel suspension (prismatic with spring){type: 3, // PrismaticconnectedEntity: chassis_id,anchor1: [0, 0, 0], // Wheel centeranchor2: [1, 0, 0], // Chassis attachmentaxis1: [0, 1, 0], // Vertical suspensionaxis2: [0, 1, 0],min: -0.2, // Compressedmax: 0.2, // Extendedstiffness: 1000, // Suspension stiffnessdamping: 50, // Suspension damping}// Wheel rotation (revolute around axle){type: 2, // RevoluteconnectedEntity: suspensionBody_id,axis1: [1, 0, 0], // Axle rotationaxis2: [1, 0, 0],// No limits = free rotation}
Joint Stability
Very long chains of joints (10+ links) can become unstable. Break them into smaller segments or use constraints instead of physical joints.
// Debug joint in scriptexport 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 reachedif (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.