Building Match Mania's merge physics in Matter.js
Match Mania looks simple — drop a fruit, let two of a kind touch, watch them merge into the next one up. Cherry, grape, strawberry, all the way to the watermelon. But "two of a kind touch and merge" hides a surprising amount of physics-engine trouble. Here's how it actually works under the hood.
▸ A quick tour of Match Mania in motion — watch it, then read how the merge physics works below.
The whole game runs on Matter.js 0.19, a 2D rigid-body engine, stepped at a fixed 60 frames per second. There's a jar made of three static walls, normal downward gravity, and ten tiers of fruit — from a small Cherry worth 2 points up to a giant Watermelon worth 1024. Merge two Watermelons and they vanish for a big bonus. That part is just a lookup table. The interesting part is everything that happens the instant two fruits collide.
The bug nobody warns you about: the double-merge
Here's the trap. When two fruits touch, Matter.js hands you a collision event with a list of colliding pairs. The obvious thing to do is walk that list and, whenever both bodies are the same tier, merge them. Easy.
Except a single tick can report the same fruit in multiple pairs at once. Drop a cherry into a tight little cluster of cherries and, in one frame, that one cherry is touching two neighbours simultaneously. Handle the pairs naïvely and it tries to merge with both — you destroy a body that's already been destroyed, points fire twice, and fruit quietly disappears. It looks like a haunted jar.
The fix is a one-tick guard. The game keeps a short-lived record of every fruit that has already merged this frame, wipes that record clean at the very start of each frame (right before the physics step), and ignores any collision pair involving a fruit that's already in it. The merge routine adds both fruits to that record the instant it runs, so any later pair in the same tick that touches either of them is simply skipped. One merge per fruit per frame, guaranteed.
Making fruit feel like fruit
A merge engine is only fun if the fruit has weight. The trick was letting density grow with tier, so a tiny Cherry skitters around lightly while a Pineapple sinks and shoves everything aside. Each fruit also gets a modest amount of bounce and friction — enough to feel springy, not enough to turn the jar into a trampoline.
That density choice is doing a lot of quiet work. When every fruit shared the same density, the big ones floated on top of the small ones like beach balls and the stack never settled. Letting mass climb with tier means the heavy fruit migrates downward and the pile compacts the way your eye expects it to.
The pop
When a merge happens, the new fruit appears at the midpoint of the two that combined. Spawning it dead-still felt flat, so each merge gets a tiny upward nudge — and that nudge is scaled to the new fruit's size, so bigger merges pop a little more than small ones.
It's barely visible frame to frame, but turn it off and the whole game feels dead. That's the difference between a merge being a number changing and a merge being a moment.
A danger line that gives you a chance
You lose when the pile crosses the line near the top of the jar. The cheap version is instant: a fruit pokes over the line, game over. It feels awful — fruit wobbles, a stray bounce clips the line for a single frame, and you're dead through no fault of your own.
So the line is on a timer instead. A fruit has to sit above it continuously for a couple of seconds before it ends the run, and the moment it drops back below, its countdown is forgotten and you're safe again. That short grace period turns a frustrating death into a tense one: you get a beat to merge something and drop the stack back down — which is exactly the panicky scramble the game is supposed to be about.
Keeping it smooth
A few unglamorous guards keep the physics stable on a phone: a hard cap on how many fruits can exist at once, a short cooldown between drops (so you can't spawn a pile of overlapping fruit in one spot and blow up the solver), and quietly removing any body that somehow escapes the jar and falls off-screen. None of it is clever — it's just the difference between a toy that runs for thirty seconds and one that survives a real high-score run.
That's the whole engine, really: a lookup table, a one-frame memory, a single density rule, and a forgiving timer. Small numbers, big feel.
Started a weekly update log on this post. The reading layout was also tightened up for phones, so the article no longer feels squashed on a narrow screen. Any future tweaks to Match Mania's merge physics will be logged here, newest first.