How Does Friction Affect Contacting Surfaces in Planck.js?

This article provides a quick overview of how friction operates between contacting surfaces in Planck.js, a 2D physics engine for JavaScript games. It explains the mechanics behind friction simulation, how friction coefficients are calculated and combined when two fixtures collide, and how developers can manipulate these values to achieve realistic physical behavior in their applications.


Understanding Friction in Planck.js

In Planck.js (which is a JavaScript port of the Box2D physics engine), friction is a force that resists the relative lateral motion of two contacting surfaces. Its primary purpose is to simulate the natural roughness of real-world materials, causing sliding objects to slow down and allowing objects to roll or grip each other without infinitely sliding.

Friction in Planck.js is simulated using Coulomb friction, which means the frictional force is proportional to the normal force (the force pushing the two surfaces together).

The Friction Coefficient

Every fixture (Fixture) in Planck.js has a friction property, typically defined via its FixtureDef during creation.

How Contact Friction is Calculated

When two fixtures collide, Planck.js must determine a single, unified friction value for the contact point. It does this by combining the friction coefficients of both interacting fixtures using a geometric mean formula:

\[\mu = \sqrt{\mu_1 \times \mu_2}\]

Where:

This specific formula ensures that if even one of the surfaces has a friction coefficient of 0.0, the resulting contact will be completely frictionless, mimicking real-world logic (e.g., if you put a rough block on frictionless ice, it will still slide flawlessly).

Modifying Friction Dynamically

While you set the initial friction value when configuring a fixture, Planck.js allows you to override this behavior dynamically during gameplay. This is particularly useful for mechanics like ice patches, oil slicks, or speed-boosting conveyor belts.

To change friction on the fly, you can listen to contact events using a contact listener:

  1. Intercept the collision in the BeginContact or PreSolve lifecycle hooks.
  2. Access the Contact object.
  3. Use the setFriction() method to manually inject a new friction coefficient for that specific frame’s physics calculation.

Note: Changes made via setFriction() in PreSolve only persist for the current time step. If you want a permanent change for that ongoing contact, you must set it during every PreSolve step until the contact ends.