Create a Slider Prismatic Joint in Matter.js

This guide explains how to implement a slider (prismatic) joint in Matter.js. Because Matter.js lacks a native prismatic joint class like Box2D, creating a slider mechanism requires constraining an object's movement along a single linear axis while eliminating unwanted degrees of freedom. You will learn the standard approach combining locked rotation with an engine update constraint to create a smooth, functional linear slider.

The Challenge with Native Constraints

Matter.js provides Matter.Constraint, which acts as a distance-based spring or pin joint (revolute joint). It does not natively support 1-degree-of-freedom prismatic joints that allow free translation along an axis while restricting rotation and perpendicular translation.

To create an effective slider, you must:

  1. Prevent the sliding body from rotating.
  2. Clamp the body's movement to a single axis (horizontal, vertical, or angled).
  3. Optionally define travel boundaries (minimum and maximum displacement).

Method: Axis-Locked Simulation Using Engine Events

The most stable and common way to achieve a prismatic joint in Matter.js is by disabling rotation on the sliding body and fixing its secondary coordinate during the simulation's update cycle.

1. Create the Bodies

Set the inertia of the slider body to Infinity to prevent it from rotating upon collision or applied forces.

const { Engine, Render, Runner, Bodies, Composite, Events } = Matter;

const engine = Engine.create();
const world = engine.world;

// Fixed track or base
const trackY = 300;
const minX = 150;
const maxX = 650;

// Slider body (constrained to move along X)
const slider = Bodies.rectangle(400, trackY, 60, 40, {
  inertia: Infinity, // Disables rotation
  friction: 0.05,
  frictionAir: 0.01,
  restitution: 0.2
});

Composite.add(world, slider);

2. Lock the Axis and Set Travel Limits

Attach a callback to the beforeUpdate event. This hook continuously locks the slider to the specified linear track (trackY) and clamps the linear travel distance between minX and maxX.

Events.on(engine, 'beforeUpdate', () => {
  // Lock the vertical position and vertical velocity
  Matter.Body.setPosition(slider, {
    x: slider.position.x,
    y: trackY
  });
  Matter.Body.setVelocity(slider, {
    x: slider.velocity.x,
    y: 0
  });

  // Clamp within the horizontal bounds
  if (slider.position.x < minX) {
    Matter.Body.setPosition(slider, { x: minX, y: trackY });
    Matter.Body.setVelocity(slider, { x: Math.max(0, slider.velocity.x), y: 0 });
  } else if (slider.position.x > maxX) {
    Matter.Body.setPosition(slider, { x: maxX, y: trackY });
    Matter.Body.setVelocity(slider, { x: Math.min(0, slider.velocity.x), y: 0 });
  }
});

Alternative: Physical Guide Rails

If you prefer a purely rigid-body collision approach without manual coordinate overrides, you can place two static rectangular bodies above and below the slider to form a physical groove.

  1. Create a top and bottom boundary with isStatic: true.
  2. Set friction: 0 on both the static rails and the slider body to prevent binding.
  3. Set inertia: Infinity on the slider body to prevent rotation inside the channel.

While simpler to visualize, this approach is slightly more computationally expensive and can occasionally cause jitter at high velocities compared to the programmatic beforeUpdate lock.