
Create and place physics colliders in Blender, export as GLB, and automatically set them up in three.js with cannon-es.
Watch Video Tutorial (Rigging a mechanical claw) Demo showcase app that inspired this addon: Mechanical Claw Machine
This solution includes two tools:
Manually building complex physical rigs vía code can be a headache in the making. This toolset aims to make it a smooth visual experience. Focus on the design aspect in Blender, and let the devs work on the details once the rig is exported. Separate concerns.
You can quicly test your rig here: https://threejs-cannones-rigger.vercel.app Just make sure your glb has a camera and it is in the right angle where you want to focus on (it will use the camera in the file... so if you don't see anything maybe you forgot to export the cameras too)
Install from disk → threejs-cannones-addon.py
| Blender → Preferences | Add-ons → Install (.py) | New Object Panel |
|---|---|---|
![]() | ![]() | ![]() |
After installing, when you select an object in the scene inside of blender, you should see new expandable box appear in the Object's tab.
To create the colliders you work with Empty objects. Their scale is used to define the size of the collider. Sphere colliders can only have uniform scale. Typically you will/should use a Cube or Sphere empty for colliders, and any shape you want for the constraints (since their transform won't be used anyways just their data)
In Blender, when you select an object you will see on the right a panel titled "ThreeJs / Cannon-es (Physics)" From there, create empty objects or boxes or spheres and assign the corresponding options.
.glbwhen you export as GLB make sure you tick the option "include custom properties"
npm install threejs-cannones-rigger
See
src/main.tsfor a usage example.
import { World } from 'cannon-es';
import { ThreeJsCannonEsSceneRigger } from 'threejs-cannones-rigger';
// Assume you have a Cannon world and a loaded Three.js scene
const world = new World({ gravity: new Vec3(0, -10, 0) });
const scene = new THREE.Scene();
// Create the rigger and rig the scene
const rigger = new ThreeJsCannonEsSceneRigger(world, scene);
new GLTFLoader().load(url, file => {
scene.add(file.scene)
rigger.rigScene(file.scene); // *** this is where the magic happens ***
});
// To update physics and sync with Three.js in your animation loop:
function yourRenderLoop() {
// ... update world, render scene, etc.
rigger.update(deltaTime);
//...
}
ThreeJsCannonEsSceneRiggerconstructor(world: World, scene?: Object3D)
world: The Cannon-es physics world.scene (optional): The Three.js scene or root object to rig immediately.rigScene(scene: Object3D): Scans the scene and creates physics bodies and constraints based on object userData.clear(): Removes all created bodies and constraints from the world and clears internal state.getBodyByName(name: string): Returns a Cannon body by the name (the name in userData.name )
Use a default Cube or UV Sphere. Scale and rotate as needed. Only spheres must be scaled uniformly; boxes can be stretched freely.
rigger.getBodyByName(name) //-> CANNON.Body
Assign this to an empty. All children will be glued into one collider/Body. The children should be empty boxes with your desire scale, rotation and positioning.
Creates a flexible cable using threejs-cannones-tube. Add two child empties to the constraint object — one for the head, one for the tail. A and B can optionally anchor the ends.
Material : If the constraint body is a mesh (like a Box) it will use whatever material that mesh has and assign it to the mesh of the tube.
rigger.getCableConstraint(name) //-> CableConstraint
rigger.getCableConstraint(name).cable //-> CannonTubeRig

In all cases, when you call get___Constraint( name ) the expected name is the name of the object as you read it in blender. Which is automatically put in userData.name when you export to glb.
Connect two colliders (A & B) so they behave as a single rigid body. Creates a LockConstraint...
rigger.getLockConstraint(name) //-> CANNON.LockConstraint
Assign to an empty. Local Z axis defines the hinge (like a door axis).
rigger.getHingeConstraint(name) //-> CANNON.HingeConstraint
Links A and B via a shared point. Each keeps its relative position to it.
rigger.getPointConstraint(name) //-> CANNON.PointToPointConstraint
Maintains the initial distance between A and B, allowing movement but preserving separation.
rigger.getDistanceConstraint(name) //-> CANNON.DistanceConstraint
Use this on a visible object (e.g. mesh) to match the position & rotation of a physics collider.
rigger.getSyncConstraint(name) //-> SyncConstraint
When a contraints references other objects, lines will appear to easily see their relationships with the currently selected object. You can toogle thouse lines on and off using the "Show Overlays" button.
The panel will show two key properties for physics-based collision management when you select a collider's body type. You can select 1 or many (clicking and holding SHIFT)

collisionGroup: "I'm in group...". Bitmask. Defines which group(s) the selected object belongs to. This is a single group index (0–31) that identifies the object's collision category.collisionMask: "I collide with...".Bitmask. Specifies which group(s) the object collides with, using a 32-bit boolean array (checkboxes). Each checkbox corresponds to one of the 32 possible collision groups.When Collisions Don’t Happen
A collision between two objects won’t occur if their Collision Masks don’t include each other’s Collision Group. For example, if Object A’s mask doesn’t check Group 2, and Object B is in Group 2, they won’t collide, even if they touch in the physics simulation.
In blender you can select "Custom Constraint" and pass a custom id (an arbitraty string of your choosing) then in javascript side, you define it like so:
rigger.registerCustomConstraint("myCustomID", config => {
// this function will be called when parsing the scene and your custom id is detected...
return yourConstraintInstance;
});
rigger.getCustomConstraint(name) //=>yourConstraintInstance
Your constraint should implement IConstraint.
The config you get has this shape:
| Property | Description |
|---|---|
obj | The Three.js object that defines the constraint in the scene. |
collisionGroup | (Optional) Bitmask, If you define it in blender side... |
collisionMask | (Optional) Bitmask, If you define it in blender side... |
A | (Optional) The Cannon body referenced as A (if set in Blender). |
B | (Optional) The Cannon body referenced as B (if set in Blender). |
To get a reference to your constrain after creation call:
rigger.getCustomConstraint("objectName") as MySuperCoolCustomConstraint
MIT