Prevent Stale Bounds with Matter.Composite.setModified
In Matter.js, composite structures optimize performance by caching
calculated bounding boxes rather than recomputing them on every frame.
When bodies within a composite are moved, scaled, added, or removed
outside standard lifecycle events, this internal cache fails to refresh
automatically, leading to stale bounds and inaccurate collision or
rendering calculations. Invoking
Matter.Composite.setModified explicitly marks the composite
and its hierarchy as altered, invalidating the cached dimensions and
forcing Matter.js to recalculate the bounding box during the next engine
update.
The Role of Bounds Caching in Matter.js
A composite in Matter.js is a container that holds bodies, constraints, and other nested composites. Calculating the total axis-aligned bounding box (AABB) of a composite requires iterating through every child body, reading its vertices, and determining the minimum and maximum spatial coordinates.
Because tree traversal is computationally expensive for complex scenes, Matter.js retains the result in an internal cache. Subsystems such as renderers, camera controllers, and broadphase collision routines rely on this cached bounding box to determine visibility and spatial partitioning without traversing every individual vertex on every tick.
Why Stale Caches Occur
Stale bounds occur when the spatial properties of elements inside a composite change without triggering the composite's internal change listeners. Common causes include:
- Direct Coordinate Manipulation: Manually setting
body.position,body.angle, or altering vertices directly instead of applying forces or velocities. - Deep Hierarchy Alterations: Modifying a child body deeply nested inside sub-composites without informing ancestor composites.
- Direct Array Manipulation: Pushing or splicing
elements directly into
composite.bodiesrather than usingMatter.Composite.addorMatter.Composite.remove.
Under these conditions, the engine assumes the composite's geometry
is unchanged. Any routine querying composite.bounds
continues to receive coordinates based on the previous state of the
bodies.
How
Matter.Composite.setModified Resolves Stale Bounds
The Matter.Composite.setModified function directly
manipulates the isModified flag within the composite data
structure. Its signature is:
Matter.Composite.setModified(composite, isModified, updateChildren, updateParents);When called, the function alters the state via the following mechanisms:
1. Invalidation Flagging
Passing true as the isModified parameter
tells Matter.js that the internal contents of the composite are dirty.
The next time the engine or user code calls functions that evaluate
bounds—such as internal broadphase updates or
Matter.Composite.bounds(composite)—the engine detects the
dirty state and discards the existing cache.
2. Cache Recalculation
With the cache invalidated, the engine recalculates the boundary:
- It queries the updated bounds of all immediate child bodies.
- It recursively queries bounds from child composites.
- It derives an encompassing min/max coordinate box encompassing all elements.
- It stores this fresh calculation back into the cache and resets the modification state.
3. Bidirectional Hierarchy Propagation
Bounds issues frequently originate in nested configurations. The
updateParents parameter (which defaults to
true when invoked internally) is critical for bounds
accuracy:
updateParents: Bubbles the modification flag upward to all ancestor composites up to the rootengine.world. This guarantees that high-level queries on the entire world reflect the localized changes of a nested body.updateChildren: Propagates the flag downward through sub-composites, ensuring all descending bounds are invalidated when a parent transform occurs.
Practical Implementation
Whenever programmatic adjustments are made directly to bodies within
a composite, call Matter.Composite.setModified immediately
afterward:
// Manually adjust the position of a body inside a nested composite
Matter.Body.setPosition(myNestedBody, { x: 500, y: 300 });
// Force the parent composite and all ancestor composites to invalidate their bounds
Matter.Composite.setModified(myParentComposite, true, false, true);
// Bounds are now guaranteed to reflect the updated position
const updatedBounds = Matter.Composite.bounds(myParentComposite);By ensuring the modification flag propagates to ancestor containers, you eliminate desynchronization between physics calculations, broadphase collision pairs, and viewport rendering.