How Matter.Pairs Tracks Contact Manifolds in Matter.js

In the Matter.js 2D physics engine, maintaining persistent contact manifolds across simulation frames is essential for stable stacking, accurate friction, and smooth collision resolution. The Matter.Pairs module achieves this persistence using a hybrid data structure composed of a hash table (a JavaScript object dictionary) alongside flat arrays for sequential processing. This structure enables both \(O(1)\) lookup for matching existing contact states between colliding body pairs and high-performance linear traversal during constraint solving.

The Core Data Structure: pairs.table and pairs.list

When a pairs manager is instantiated via Pairs.create(), it initializes two primary data structures to track contacts:

  1. pairs.table (Hash Table / Object Dictionary): The primary data structure for tracking persistent manifolds is a plain JavaScript object functioning as a hash map. Every colliding pair of rigid bodies is mapped to an entry in pairs.table using a unique, canonical string key generated by Pair.id(bodyA, bodyB). The key standardizes the order of body IDs (typically bodyA.id < bodyB.id ? 'A' + bodyA.id + 'B' + bodyB.id : ...), ensuring that collision lookups are symmetric regardless of which body is reported first by the broadphase detector.

  2. pairs.list (Dynamic Array): Parallel to the hash table, pairs.list stores an array of active Pair references. While the hash table allows constant-time insertion, retrieval, and deletion of specific pairs, iterating over JavaScript object keys is slower. The flat array allows the physics engine to iterate over all active collision pairs sequentially during the collision resolution, position correction, and velocity solving stages.

Anatomy of a Persistent Pair

Each entry stored in pairs.table is a Pair object containing the contact manifold data. Key properties include:

Manifold Lifecycle and Updates

During each physics step, Pairs.update(pairs, collisions, timestamp) updates the structures:

  1. Mark Phase: Existing entries in pairs.list have their isActive flags temporarily cleared.
  2. Lookup and Retention: For each collision returned by the narrowphase collision detector (Matter.SAT or Matter.Detector), the engine calculates the pair ID and checks pairs.table[pairId]. If found, the existing manifold is updated with new geometry while retaining previous contact impulses to preserve contact continuity. If it does not exist, a new Pair is allocated, keyed in pairs.table, and pushed to pairs.list.
  3. Sweep and Eviction: Pairs that did not receive an update during the current step are flagged as inactive. Inactive pairs that exceed the engine's persistence threshold or separation distance are removed from pairs.table and spliced out of pairs.list, ensuring that memory usage remains tightly bound to active interactions.