Fire the same cannonball at five walls in a browser and you get five completely different failures. Concrete shears into 154 tumbling slabs. Stone gives you about five boulders that thud once and stop dead. Glass does not crack at all — it holds as a single pane until one qualifying hit releases the whole thing at once, 114 shards in the air and nothing left standing. Same projectile, same engine, same solver. The entire difference lives in how each wall was cut up before anything hit it.
That was the surprise. We expected material feel to come out of contact parameters — restitution, friction, thresholds, the usual dials. It mostly does not. It comes out of the fracture layout, and the dials only decide how the debris behaves once it already has the right shape.
The wall is cut before it breaks
Every wall is partitioned into irregular solid pieces at build time, using a Voronoi partition computed in-engine rather than anything the physics engine provides. Uneven sizes, no grid: the cells look like the pieces you actually get out of a real impact rather than a chocolate bar. Each piece also records which other pieces it genuinely shares a face with.
That neighbour map is the part that matters. It is what lets a wall behave like a structure instead of a pile of props. When a hit removes pieces, the wall re-derives what is still holding itself up: start from whatever is resting on the ground, walk outward, and keep anything with an unbroken path back down to something solid.
The rule that makes collapses read correctly is that support travels up only, never sideways. It sounds like an optimisation and it is not — it is the whole illusion. Allow lateral support and a wall with its middle blown out will hang there, cheerfully propped up by its own edges, because each remaining column can point at its neighbour and claim to be held. Anything left unsupported drops, lowest pieces released first, so a collapse ripples upward the way real structures give way.

*Concrete after a centre impact. Those uneven cell faces are decided before the cannonball exists — and they are most of what separates concrete from stone.*
Material personality then comes almost entirely from how the seeds are laid out, not from the numbers attached to them:
- Brick does not get organic jitter at all. It gets real masonry courses with running-bond stagger, so it fails along its mortar lines.
- Wood is stretched roughly ten times along the grain, so it splinters into planks instead of crumbling.
- Glass is laid out radially, and only at the moment of impact — a pane that is pre-partitioned reads as already-cracked glass, which is a completely different object.
- Concrete and stone share a strategy and differ mainly in coarseness: fine mixed cells versus a handful of big ones.
The dials still matter, second
Contact parameters do real work once the shape is right. The spread is wider than you might guess:
| Material | Restitution | Friction |
|---|---|---|
| Concrete | 0.05 | 0.85 |
| Stone | 0.01 | 0.98 |
| Glass | 0.18 | 0.35 |
Glass is roughly 3.6× bouncier and 2.4× more slippery than concrete; against stone the bounce gap widens to about 18×. That is what makes shards skitter across a floor while boulders arrive once and commit. Impact thresholds separate them further — glass gives way at 5 m/s, concrete at 8, stone holds until 12. But none of it rescues a bad partition. Tuning friction on badly-shaped debris just gives you badly-shaped debris with different sliding behaviour.

*Switching material swaps the partition strategy first and the contact numbers second — in that order of consequence.*
The 147-metre chunk
The clearest evidence for "shape first, dials second" arrived as a bug, and it is worth the detour because the failure mode generalises.
During bomb-tool testing, one fragment reached a logged peak height of 147.73 m off a 4.2 metre wall — an implied launch speed somewhere around 51–55 m/s. Not a tuning problem. Blast impulse was being computed as impulse scale × speed × average mass, which makes the velocity delta proportional to mass. A full cell absorbed that fine. A light sub-fracture chunk at roughly one-thirtieth the mass took the same impulse and left the scene at something on the order of 600 m/s.
A second bug was quietly compounding it: pre-crack refinement was permanently rewriting the base partition, so repeated hits grew one wall from 140 cells to 7,403, and Reset did not restore the original. Every test after the first was running against a progressively different wall.
Three changes fixed it. Blast impulses became mass-independent with a hard 18 m/s launch clamp applied after every blast — scaled down, never zeroed, so debris still moves. The pristine partition became immutable per material profile, so Reset genuinely resets. And a development-only invariant monitor now checks nine failure classes on every physics step, including a volume ledger that flags any step where total fragment volume *grows* outside a known carve event.
Re-running the identical scenario afterwards: peak height 3.55 m. A three-charge volley now tops out at 240 dynamic chunks and 425 live cells while holding a 53.3 fps floor, with nothing in orbit.
Notice what kind of bug that was. Nothing was mistuned — no restitution was too high, no threshold too low. The *definition* of the impulse was wrong: it multiplied by mass, so the formula meant something different for a light chunk than for a heavy one. That is the same lesson the five materials teach from the other direction. The behaviour you get is set by how the thing is defined, and the parameters only decide how that definition plays out. Tuning cannot rescue a wrong definition, and it could not have found this.

*A bomb carve mid-detonation with the live chunk and frame-rate counters visible — roughly 156 dynamic pieces, frame budget intact.*
There is no screenshot of the original 147-metre launch. We went back to capture one and it would not reproduce, so the record says so instead of showing a reconstruction.
What transfers
If you are building runtime destruction, four things here are worth stealing:
- Treat fracture as authored data, not simulation. Partition once, deterministically, and treat the resulting neighbour map as the source of truth. Deterministic seeding also means a pattern you liked is a pattern you can get back.
- Support is a graph problem, not a physics problem. Walking outward from grounded pieces with upward-only propagation is cheap, predictable, and fails in ways that look structural rather than arbitrary.
- Material identity is layout first, parameters second. Masonry courses and grain direction did more for material feel than any friction pass.
- Clamp at the impulse site, and assert where the risk actually is. Every chunk-in-orbit bug is a missing clamp plus a missing assertion. The airborne-sleeper check earns its keep by running at the exact point a body is about to be frozen, not in a sweep afterwards.
What this is not, yet
There is no dust or smoke — a first attempt looked worse than nothing and was removed. No sound. No contact shadows. Materials are flat colours; the texture pass has not happened.
Every frame-rate number above comes from headed Chromium on a single development machine, not a hardware matrix, so treat them as a ceiling rather than a promise. And this is one wall per material — six metres by 4.2 for the masonry set, a thinner five by 3.2 pane for glass — not a city block. The acceptance gate is a 50 fps floor during a three-charge volley; the worst measured dip was 53.3.
The next phase is presentation rather than physics: procedural dust done properly, crack decals to telegraph damage before it lands, and a sag-and-groan beat before a collapse commits. Then the interesting scaling question — one wall is not a tower, and a tower is where support graphs start to earn real money.
*Try it: cars.swapp1990.org/demolition. Desktop gets drag-orbit and right-click to fire; mobile is tap to fire.*


