Matter.js Composite allBodies Recursive Extraction
This article explains how the Matter.Composite.allBodies
function navigates deeply nested scene graphs in Matter.js to retrieve
physics bodies. It details the hierarchical tree structure of Matter.js
composites, the recursive depth-first traversal mechanism employed by
the method, and the performance implications of flattening nested
composite structures into a single array.
The Composite Tree Structure in Matter.js
In Matter.js, the scene graph is managed using instances of
Matter.Composite. The root scene—usually referenced as
engine.world—is itself a top-level composite. A composite
serves as an organizational container that can hold three primary
collections:
bodies: An array of individual rigid bodies (Matter.Body).constraints: An array of constraints/joints (Matter.Constraint).composites: An array of child composites (Matter.Composite).
Because a composite can contain other composites, developers can create deeply nested hierarchies, such as representing a ragdoll, a vehicle with wheels, or a multi-part machine as an isolated composite nested inside a larger scene.
The Recursive Extraction Mechanism
Matter.Composite.allBodies(composite) is designed to
retrieve every Body within a composite and all of its
descendants, returning them as a flat, single-dimensional array.
The function operates using a standard depth-first search (DFS) pattern. When invoked with a target composite:
- Initial Collection: The function reads the
bodiesarray directly attached to the current composite and copies them into a working array. - Child Traversal: The function inspects the
compositesarray of the current composite. - Recursive Step: If child composites exist, the
function iterates through each child and recursively calls
Matter.Composite.allBodies(child). - Flattening and Concatenation: The bodies returned from each recursive call are concatenated into the accumulating array.
- Base Case: When a composite has an empty
compositesarray (a leaf node in the tree), no further recursion occurs, and only its directbodiesare returned.
In simplified pseudocode, the logic functions as follows:
Matter.Composite.allBodies = function(composite) {
let list = [].concat(composite.bodies);
for (let i = 0; i < composite.composites.length; i++) {
list = list.concat(Matter.Composite.allBodies(composite.composites[i]));
}
return list;
};Reference Integrity and Duplication
The recursive function extracts references to the original
Matter.Body objects; it does not clone or mutate the bodies
themselves. Each unique body instance contained within the nested tree
structure will appear in the resulting array. However, developers must
ensure that cyclic references (e.g., a composite accidentally containing
a reference to an ancestor composite) are avoided, as this would cause a
stack overflow error during traversal.
Performance and Allocation Considerations
Because Matter.Composite.allBodies creates and
concatenates new arrays at every level of recursion, calling it
repeatedly inside an active render or update loop (e.g., within
requestAnimationFrame or beforeUpdate) incurs
continuous memory allocation and garbage collection overhead.
For performance-critical code paths, it is optimal to call this method only when the composite hierarchy has changed (signaled by Matter.js via composite mutation flags) and cache the resulting array rather than re-traversing the tree on every frame.