A devlog by Neeraj Kotwani, who builds every game on Elipar.
Bubble Burst is a hex-grid bubble shooter. The hardest part of building it was not the aiming, the physics or the match detection — it was pushing a new row onto the top of the board without quietly corrupting everything already on it.
Why a bubble shooter is not a square grid
The genre's whole identity is the offset packing you see in a brick wall. (If you are building one yourself, Red Blob Games' hexagonal grids reference is the canonical write-up of the coordinate systems involved, and worth reading before you pick one.) Rows alternate: even rows hold 12 bubbles starting flush with the left edge, odd rows hold 11 and sit shifted right by half a bubble. That is what lets each bubble touch six neighbours instead of four, and it is why the board reads as a honeycomb rather than a spreadsheet.
So a bubble's screen position depends on its column and on whether its row index is odd or even. That second dependency is the trap.
The obvious implementation is wrong
Every few shots the ceiling drops and a fresh row appears at the top. The natural way to write that is one line:
grid.unshift(newRow)
Every existing row's index increments by one, which is exactly what you want conceptually. It is also a serious bug.
An old row keeps its column values, but at its new index its parity has flipped. A row that was even is now odd. The rendering code asks "what x-position does column 5 of an odd row have?" and gets a different answer than it did a frame ago. Every bubble already on the board jumps sideways by half a bubble width, all at once.
Worse than cosmetic: a row that held 12 columns is now being treated as an 11-column row, so the twelfth bubble is at an index that is out of bounds for its own row.
The fix is one global toggle
Instead of asking a row for r % 2, ask for (r + parityFlip) % 2, where parityFlip is a single boolean for the whole board. Pushing a row flips it in the same step as the unshift.
The two changes cancel. Each existing row's index goes up by one and the global offset goes up by one, so its rendered parity — and therefore the exact pixel position of every bubble already placed — is provably unchanged. Only the brand-new row 0 lands on the opposite parity, which is precisely the one you need to continue the zigzag.
Why this needed a test and not a screenshot
This is the part worth taking away. A screenshot of one push looks completely fine either way. Half a bubble width is small, and if you are looking at a board of coloured circles you will not spot that they all moved together. The bug only becomes visible as an accumulating diagonal drift after several pushes, by which point you are debugging a symptom several steps from its cause.
So the check is arithmetic, not visual. Seed a known three-row board, record the real pixel x of every bubble, call the push, then assert that every bubble's x at its new (r+1, c) index is bit-for-bit identical to what it was before. Across 35 seeded cells: zero mismatches.
That test would have failed instantly on the naive version and passes on the parity-flip one. It is four lines of assertion protecting a property no amount of playtesting reliably surfaces.
Two more things the hex grid forced
Snapping is neighbour-based, not nearest-empty. When a fired bubble hits something, it searches the hit bubble's real hex neighbours — computed through the same parity-aware function match detection uses — for the closest empty one. Sharing that function is the point: if snapping and matching disagreed about what "adjacent" means, bubbles would land in cells that then refuse to match. Verified that a landed bubble sits within 0.01px of a perfect hex-packed distance from its nearest occupied neighbour, which means no overlap and no unintended gap.
Floating clusters need a second, different flood fill. Match detection walks same-colour neighbours. Detachment detection walks any occupied neighbour, seeded from row 0, and anything it never reaches has lost its last connection to the ceiling and falls. Dropping a big cluster pays more than popping a small match, which is what makes aiming at a support bubble the interesting shot rather than aiming at the biggest colour blob.
The game is Bubble Burst, free and instant in a browser tab. If you want the same genre's logic without the reflexes, Sweet Swap is the match-3 next door.