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:
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 inpairs.tableusing a unique, canonical string key generated byPair.id(bodyA, bodyB). The key standardizes the order of body IDs (typicallybodyA.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.pairs.list(Dynamic Array): Parallel to the hash table,pairs.liststores an array of activePairreferences. 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:
contacts: A dictionary/array of individual contact points detected between the two polygonal or circular shapes, tracking vertex-edge or edge-edge contact locations.collision: The current geometric collision data, including the collision normal, penetration depth, and reference axes.isActive: A boolean flag indicating whether the collision persists in the current engine tick.- Warm-Starting Impulses: Persisted normal and tangential impulses calculated from previous frames, allowing the iterative impulse solver to converge rapidly without jitter.
Manifold Lifecycle and Updates
During each physics step,
Pairs.update(pairs, collisions, timestamp) updates the
structures:
- Mark Phase: Existing entries in
pairs.listhave theirisActiveflags temporarily cleared. - Lookup and Retention: For each collision returned
by the narrowphase collision detector (
Matter.SATorMatter.Detector), the engine calculates the pair ID and checkspairs.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 newPairis allocated, keyed inpairs.table, and pushed topairs.list. - 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.tableand spliced out ofpairs.list, ensuring that memory usage remains tightly bound to active interactions.