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:

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:

  1. It queries the updated bounds of all immediate child bodies.
  2. It recursively queries bounds from child composites.
  3. It derives an encompassing min/max coordinate box encompassing all elements.
  4. 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:

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.