How to Implement a Mouse Joint in Planck.js?

This article provides a step-by-step guide on how to implement a Mouse Joint in planck.js, a 2D physics engine for JavaScript. You will learn how to set up the joint, update its target position based on user input (like mouse or touch movements), and properly destroy it when the interaction ends. By the end of this guide, you will be able to allow users to click and drag physics bodies smoothly within your simulation.

Understanding the Mouse Joint

In planck.js (a rewrite of Box2D), a MouseJoint is used to manipulate a physics body using the cursor or a touch point. Instead of instantly teleporting an object to the mouse coordinates—which breaks physics laws and causes collisions to glitch—the Mouse Joint acts like a stiff spring. It applies forces to pull the target body toward the cursor’s position while maintaining realistic physical interactions with other objects.

Step 1: Initial Setup and Requirements

To create a Mouse Joint, you need a reference to your planck.js World instance, a “ground” or dummy body (usually an empty static body), and the dynamic body that the user wants to drag.

// A static ground body is required as the first body for the joint
const groundBody = world.createBody();

// Variable to store the active joint
let mouseJoint = null;

Step 2: Creating the Joint on Mouse Down

When the user clicks or touches a physics body, you need to calculate the world coordinates of the click and initialize the MouseJointDef.

function onMouseDown(mouseWorldX, mouseWorldY, targetBody) {
  if (mouseJoint) return; // Already dragging something

  const jointDef = {
    bodyA: groundBody, // The static ground body
    bodyB: targetBody,  // The dynamic body to drag
    target: planck.Vec2(mouseWorldX, mouseWorldY),
    maxForce: 1000.0 * targetBody.getMass(), // Force scale based on mass
    frequencyHz: 5.0,                        // Oscillation frequency (stiffness)
    dampingRatio: 0.7                        // Prevents endless bouncing
  };

  mouseJoint = world.createJoint(planck.MouseJoint(jointDef));
  targetBody.setAwake(true); // Ensure the body is active
}

Step 3: Updating the Target Position on Mouse Move

As the user moves their cursor across the screen, you must continuously update the target vector of the Mouse Joint. This tells the physics engine where to pull the body next.

function onMouseMove(mouseWorldX, mouseWorldY) {
  if (mouseJoint) {
    // Update the joint's target to the new cursor position
    mouseJoint.setTarget(planck.Vec2(mouseWorldX, mouseWorldY));
  }
}

Step 4: Destroying the Joint on Mouse Up

When the user releases the mouse button or lifts their finger, you must destroy the joint to release the object. If you omit this step, the object will remain tethered to that final coordinate indefinitely.

function onMouseUp() {
  if (mouseJoint) {
    world.destroyJoint(mouseJoint);
    mouseJoint = null;
  }
}