Optimize Matter.js Dense Pachinko Pin Collisions
Simulating a ball falling through a dense field of Pachinko pins in Matter.js can quickly cause frame rate drops if the physics engine spends too much time evaluating potential impacts. Because Pachinko layouts feature hundreds or thousands of static obstacles, default collision detection routines perform unnecessary calculations. You can achieve smooth performance by properly configuring collision filtering, selecting the right broadphase algorithm, simplifying geometries, and adjusting solver iterations.
1. Eliminate Pin-to-Pin Checks with Collision Filters
By default, Matter.js evaluates whether any body might collide with any other body. In a Pachinko game, pins are stationary and will never collide with one another. Allowing the engine to test pin-against-pin collisions creates massive, wasted overhead.
Assign bitmasks using collisionFilter to ensure pins
only test collisions against balls:
const BALL_CATEGORY = 0x0001;
const PIN_CATEGORY = 0x0002;
// Ball configuration
const ball = Bodies.circle(x, y, radius, {
collisionFilter: {
category: BALL_CATEGORY,
mask: PIN_CATEGORY // Only collide with pins
}
});
// Pin configuration
const pin = Bodies.circle(x, y, pinRadius, {
isStatic: true,
collisionFilter: {
category: PIN_CATEGORY,
mask: BALL_CATEGORY // Only collide with balls
}
});Using these masks skips non-relevant body pairs before narrowphase calculations occur.
2. Optimize the Broadphase Configuration
Matter.js relies on a broadphase algorithm to generate a list of candidate pairs for collision detection. For dense, static grids like Pachinko boards, the standard broadphase can struggle if bucket sizes are improperly sized.
- Tune the Grid: If using
Matter.Grid, set the grid bucket dimensions (engine.grid.bucketWidthandengine.grid.bucketHeight) so that they roughly match the average spacing between pins plus the ball's diameter. If buckets are too small, bodies occupy too many buckets; if buckets are too large, each bucket contains too many candidates. - Keep Pins Static: Always ensure pins are explicitly
marked with
isStatic: true. Static bodies are managed differently during collision pair generation and will not waste cycles calculating movement or internal mass properties.
3. Use Native Circle Geometries Exclusively
Polygonal collision checks require vertex projection via the Separating Axis Theorem (SAT), which scales with the number of vertices. Circles, however, only require a Euclidean distance check between two centers:
\[\text{distance}^2 \le (r_1 + r_2)^2\]
Create every pin and ball using Matter.Bodies.circle().
Do not approximate circular pins with polygonal approximations or
multi-sided regular polygons.
4. Enable Engine Sleeping
When balls settle into catchers or temporarily move out of range of certain pins, their physics calculations should halt. Enable sleeping on the engine level:
engine.enableSleeping = true;When bodies come to rest, Matter.js removes them from active broadphase updates, freeing CPU resources.
5. Reduce Iteration Counts
If the ball moves at reasonable speeds and pins are placed close together, the default iteration counts in Matter.js might be higher than necessary. You can lower the constraint and position solver iterations to trade negligible accuracy for improved framerates:
engine.positionIterations = 4; // Default is 6
engine.velocityIterations = 2; // Default is 4To prevent balls from tunneling through pins due to lower iteration
counts, set the ball's bullet: true property, which
enforces continuous collision detection (CCD) only on the moving
sphere.
6. Hybrid Approach: Mathematical Grid Lookup
If the pin count exceeds several thousand, bypassing Matter.js bodies entirely for the pins yields the highest performance:
- Keep only the moving ball inside the Matter.js simulation.
- Store pin locations in a 2D spatial array or mathematical grid layout (e.g., staggered rows).
- On every
beforeUpdatetick, calculate which pin coordinates the ball is currently closest to based on the ball's \((x, y)\) position. - If the distance to the nearest pin center is less than the sum of
their radii, manually apply a reflection vector or impulse directly to
the ball using
Body.setVelocity()orBody.applyForce().
This removes static bodies from the physics world entirely, maintaining optimal frames per second regardless of pin density.