When Should I Use Each Joint Type in Box3D? A Complete Guide to Revolute, Prismatic, Spherical, Wheel, Distance, Weld, Motor, and Parallel Joints
Select Box3D joints based on required degrees of freedom: use revolute for single-axis hinges, prismatic for linear sliders, spherical for ball-socket rotation, wheel for vehicle suspension, distance for rope-like constraints, weld for rigid bonding, motor for active velocity control, and parallel for keeping axes aligned during translation.
Box3D, the 3D physics engine maintained by Erin Catto, provides eight specialized joint primitives that constrain rigid-body motion through specific mechanical relationships. Knowing when to use each joint type is critical for building stable simulations ranging from robotic arms to vehicle dynamics. This guide explains the specific use cases, degrees of freedom (DoF), and implementation details for each joint type according to the erincatto/box3d source code.
Revolute Joint: Single-Axis Rotation
Use the revolute joint when you need pure rotation around a fixed axis with no translation. This joint provides 1 rotational DoF and acts like a hinge or pin.
Typical applications include doors, swinging robot arms, and piston rods that rotate. In src/revolute_joint.c, the b3PrepareRevoluteJoint function computes the hinge axis and axial mass, while b3SolveRevoluteJoint enforces angular limits and optional spring motors. You can constrain the rotation range using lowerAngle and upperAngle parameters.
Prismatic Joint: Linear Translation
Choose the prismatic joint for motion constrained to a single axis of translation. This joint provides 1 translational DoF, functioning as a slider or linear actuator.
Apply this joint to model railway cars, hydraulic pistons, or drawer slides. The implementation in src/prismatic_joint.c handles the linear constraint through b3PreparePrismaticJoint, which calculates the sliding axis and effective mass. Enable limits with lowerTranslation and upperTranslation to define the allowable travel range.
Spherical Joint: Ball-Socket Freedom
Implement the spherical joint when you need full rotational freedom in three dimensions. This joint provides 3 rotational DoF (swing and twist), functioning as a ball-and-socket connection.
This is the ideal choice for shoulder joints in ragdolls, free-spinning connectors, or any mechanism requiring unrestricted rotation. According to src/spherical_joint.c, you can optionally limit the motion using cone limits (coneAngle) for swing and twist limits (lowerTwistAngle, upperTwistAngle) to prevent unnatural rotations.
Wheel Joint: Vehicle Suspension and Steering
Select the wheel joint for vehicle simulations requiring suspension travel, wheel spin, and steering capabilities. This joint provides 1 translational DoF (suspension travel) plus 1 rotational DoF (wheel spin), with an optional third axis for steering.
Use this for car wheels that must maintain contact with terrain while absorbing bumps. The src/wheel_joint.c implementation manages suspension springs through suspensionHertz and damping parameters, while enableSteering allows you to set the wheel orientation relative to the chassis.
Distance Joint: Fixed Separation Constraints
Apply the distance joint to maintain a specific scalar distance between two points on different bodies. This joint removes 1 translational DoF by constraining the distance between anchor points.
This joint excels for rope-like linkages, cables, or spring-damper connections where bodies must stay connected but can otherwise move freely. In src/distance_joint.c, the constraint preserves the target length between anchors, with optional spring physics controlled by hertz and dampingRatio parameters.
Weld Joint: Rigid Bonding
Use the weld joint to fuse two bodies into a single rigid unit. This joint provides 0 DoF, locking both relative position and orientation.
Apply this when you need bodies to behave as one object, such as permanently attaching fixtures or creating complex shapes from multiple rigid bodies. The implementation in src/weld_joint.c constrains all six degrees of freedom (three linear, three angular) through the b3PrepareWeldJoint and b3SolveWeldJoint functions.
Motor Joint: Active Velocity Control
Implement the motor joint when you need to actively drive a body toward specific linear or angular velocities rather than constraining its position. This joint prescribes motion with optional force/torque limits.
Use this for moving platforms, controlled rotors, or animation-driven physics where you want bodies to follow a target velocity. According to src/motor_joint.c, you set linearVelocity and angularVelocity targets, then clamp the applied forces using maxVelocityForce and maxVelocityTorque.
Parallel Joint: Axis Alignment During Translation
Choose the parallel joint when two bodies must maintain parallel orientation axes while translating relative to each other. This joint constrains the relative rotation to keep two local axes aligned.
This is essential for telescopic linkages, parallel shaft couplings, or mechanisms where orientation must remain constant during linear motion. The src/parallel_joint.c implementation uses localAxisA and localAxisB to define the directions that must stay parallel, with optional translation limits similar to the prismatic joint.
Solver Architecture and Joint Lifecycle
All Box3D joints follow a consistent pipeline defined in the solver architecture. Understanding this lifecycle helps debug constraint behavior and optimize performance.
The process begins with a joint definition (b3JointDef) where you specify anchor frames, limits, and motor parameters. Calling b3World_CreateJoint allocates a b3JointSim and selects the concrete joint structure (such as b3RevoluteJoint or b3PrismaticJoint).
During each simulation step, the solver executes four phases:
- Preparation: Functions like
b3PrepareRevoluteJointcompute world-space anchors, Jacobian matrices, and effective masses. - Warm-starting: The system reapplies previous frame impulses (
b3WarmStart*Joint) to improve solver convergence. - Solving:
b3Solve*Jointenforces constraints, applying impulses to satisfy limits, springs, and motor targets while respecting softness parameters set viab3MakeSoft. - Debug Rendering: Each joint implements
b3Draw*Jointfor visualizing constraint axes and limits.
The core physics solver resides in src/solver.c, which orchestrates these callbacks across all joint types uniformly.
Code Examples for Each Joint Type
Below are minimal C snippets demonstrating how to create and configure each joint type using the public API (box3d.h). All examples assume you have a b3World* world and two bodies bodyA and bodyB.
Revolute Joint
b3RevoluteJointDef jd = b3DefaultRevoluteJointDef();
jd.bodyA = bodyA;
jd.bodyB = bodyB;
jd.localAnchorA = (b3Vec3){0.0f, 0.0f, 0.0f};
jd.localAnchorB = (b3Vec3){0.0f, 0.0f, 0.0f};
jd.enableLimit = true;
jd.lowerAngle = -0.5f * B3_PI;
jd.upperAngle = 0.5f * B3_PI;
b3JointId joint = b3World_CreateJoint(world, &jd);
Prismatic Joint
b3PrismaticJointDef jd = b3DefaultPrismaticJointDef();
jd.bodyA = bodyA;
jd.bodyB = bodyB;
jd.localAxisA = (b3Vec3){1.0f, 0.0f, 0.0f};
jd.enableLimit = true;
jd.lowerTranslation = -2.0f;
jd.upperTranslation = 2.0f;
b3JointId joint = b3World_CreateJoint(world, &jd);
Spherical Joint
b3SphericalJointDef jd = b3DefaultSphericalJointDef();
jd.bodyA = bodyA;
jd.bodyB = bodyB;
jd.enableConeLimit = true;
jd.coneAngle = 0.5f * B3_PI;
jd.enableTwistLimit = true;
jd.lowerTwistAngle = -0.25f * B3_PI;
jd.upperTwistAngle = 0.25f * B3_PI;
b3JointId joint = b3World_CreateJoint(world, &jd);
Wheel Joint
b3WheelJointDef jd = b3DefaultWheelJointDef();
jd.bodyA = chassis;
jd.bodyB = wheel;
jd.enableSuspension = true;
jd.suspensionHertz = 5.0f;
jd.enableSteering = true;
jd.steeringHertz = 2.0f;
b3JointId joint = b3World_CreateJoint(world, &jd);
Distance Joint
b3DistanceJointDef jd = b3DefaultDistanceJointDef();
jd.bodyA = bodyA;
jd.bodyB = bodyB;
jd.length = 1.0f;
jd.enableSpring = true;
jd.hertz = 4.0f;
jd.dampingRatio = 0.7f;
b3JointId joint = b3World_CreateJoint(world, &jd);
Weld Joint
b3WeldJointDef jd = b3DefaultWeldJointDef();
jd.bodyA = bodyA;
jd.bodyB = bodyB;
b3JointId joint = b3World_CreateJoint(world, &jd);
Motor Joint
b3MotorJointDef jd = b3DefaultMotorJointDef();
jd.bodyA = bodyA;
jd.bodyB = bodyB;
jd.linearVelocity = (b3Vec3){0.0f, 2.0f, 0.0f};
jd.angularVelocity = (b3Vec3){0.0f, 0.0f, 5.0f};
jd.maxVelocityForce = 100.0f;
jd.maxVelocityTorque = 50.0f;
b3JointId joint = b3World_CreateJoint(world, &jd);
Parallel Joint
b3ParallelJointDef jd = b3DefaultParallelJointDef();
jd.bodyA = bodyA;
jd.bodyB = bodyB;
jd.localAxisA = (b3Vec3){0.0f, 1.0f, 0.0f};
jd.localAxisB = (b3Vec3){0.0f, 1.0f, 0.0f};
jd.enableLimit = true;
jd.lowerTranslation = -1.0f;
jd.upperTranslation = 1.0f;
b3JointId joint = b3World_CreateJoint(world, &jd);
Summary
- Revolute (
src/revolute_joint.c) provides single-axis rotation for hinges and swinging mechanisms. - Prismatic (
src/prismatic_joint.c) constrains motion to linear sliding along a fixed axis. - Spherical (
src/spherical_joint.c) allows full 3D rotation with optional cone and twist limits. - Wheel (
src/wheel_joint.c) combines suspension travel, steering, and spinning for vehicle simulations. - Distance (
src/distance_joint.c) maintains fixed separation between points with optional spring physics. - Weld (
src/weld_joint.c) rigidly bonds bodies by constraining all relative motion. - Motor (
src/motor_joint.c) actively drives target velocities with force and torque limits. - Parallel (
src/parallel_joint.c) keeps two local axes aligned while allowing relative translation.
Frequently Asked Questions
Can I combine multiple joints between the same two bodies?
Yes, Box3D allows you to create multiple joints connecting the same pair of bodies to build compound constraints. For example, you might combine a revolute joint with a distance joint to create a hinged linkage with length limits. The solver in src/solver.c processes all constraints simultaneously during the resolution phase.
What is the difference between the Motor joint and enabling a motor on a Revolute joint?
The Motor joint (src/motor_joint.c) drives a body toward specific world-space linear and angular velocities without constraining position, making it ideal for free-body control like moving platforms. In contrast, enabling a motor on a Revolute joint drives the relative angular velocity between two bodies around the hinge axis while maintaining the joint's positional constraints. Use the Revolute motor for actuator joints and the Motor joint for velocity-based animation or AI control.
Should I use a Weld joint or merge fixtures into a single body?
Use a Weld joint when you need to combine bodies dynamically at runtime or preserve the option to break them apart later using joint destruction. If the parts never separate and creation-time performance is critical, merge the fixtures into a single body with multiple shapes instead, as this reduces solver overhead by eliminating constraint calculations entirely.
When does the Wheel joint outperform a Spherical joint with limits?
The Wheel joint outperforms a Spherical joint when modeling vehicle suspension because it provides dedicated parameters for spring frequency (suspensionHertz) and steering axes that align with vehicle coordinate systems. While you could approximate wheel behavior using a Spherical joint combined with separate spring forces, the Wheel joint handles the specific constraint mathematics more robustly in src/wheel_joint.c, ensuring stable contact response and proper suspension dynamics under high load.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →