The car
Twenty-one pieces and no model file
A crash sim lives or dies on how the car is split up, so this one is authored in code rather than imported: every piece that can dent, swing or fly off is its own mesh with its own pivot.
The tub and the cabin frame dent but never leave. Seven panels β hood, trunk, roof, two doors, two bumpers β can come off. Then four lights, two mirrors, four panes of glass, and four wheels that tear off whole. Each panel declares the things a crash needs to know about it: a bounding box for the collider it will get once it is loose, a hinge axis and anchor and swing limits, a looseAt damage figure, a breakAt figure, a maxCrush, and the direction it pops in when the bolts finally shear.
Vertex density is chosen for how well a dent will read rather than for silhouette detail β the tub is subdivided 6 Γ 3 Γ 12 β and every panel is de-indexed so its triangles do not share vertices. Flat shading then does the rest: move one vertex and the facets around it break into creases with no extra work at all.
Low-poly is not a style decision here. Few vertices is what makes crumpling cost almost nothing, and de-indexed flat facets are what make it look like metal.
Driving
One box and four rays
The wheels are not physics objects. The chassis is a single rigid body with two box colliders β the tub and the cabin β and each wheel is a ray cast at the road through Rapier's raycast vehicle controller.
The ray finds the ground, a virtual spring pushes back at 34 stiffness over 0.18m of travel with a 900N ceiling, and a tire model turns engine force into grip. Rear-wheel drive at 26 units per driven wheel, and the steering lock shrinks with speed β 0.56 / (1 + speed Γ 0.055) β so the car does not knife-edge itself off the road at 90 km/h.
One filter callback on that raycast is doing more work than it looks. A suspension ray pressing 900 newtons into a 0.4kg crate is a catapult, so the ray refuses to stand on any dynamic body under 2kg. Without it, driving over the crate pyramid launches the car into the sky, which is funny exactly once.
Reverse while rolling forward is not reverse β it is the brakes. Two lines in applyDriving, and the car stops feeling like a spreadsheet.
Damage
A dent is vertices, moved
Each dentable mesh remembers where its vertices were born. A hit pushes the ones near the contact point along the blow, and that is the whole deformation model.
The falloff is a smoothstep on distance from the crater's centre, so the dent has a soft lip rather than a punched edge. Two tangents perpendicular to the blow add a per-vertex wrinkle at 0.6 of the displacement, from a deterministic hash β crushed metal shears sideways a little instead of denting like clay, and it does so identically on every run.
Then the clamp that keeps it looking like sheet steel: every vertex's distance from its home position is capped at the panel's maxCrush β 0.38m for a door, 0.30m for the roof, 0.08m for a headlight. Metal bottoms out. Without that limit a hard enough hit folds a panel through itself and the car turns inside out.
Click the panel beside this to hit it. The default depth is a fairly ordinary blow; the slider runs up to 0.26m, which is what a 3200-newton impact lands. Keep hitting the same spot at that end and the panel comes apart into loose facets β see the last section for why, because that is a real edge of the technique rather than a rendering glitch.
Normals are recomputed after each dent. The flat-shaded creases would appear without it; the Lambert shading shifting as the damage accumulates would not.
The engine's own Dentable over a real PartDef geometry.
Hands on
Try it
This is the whole simulation β Rapier world, raycast vehicle, dentable panels, hinges, glass. Nothing below is staged: βfloor itβ holds the throttle down and lets the crates decide what happens.
The actual Sim and ChaseCamera, imported by this page. The demo itself is drivable.
Detection
Listening for the crunch
The physics engine already knows when something hit hard. Both chassis colliders are flagged for contact-force events with a threshold of 180, so scrapes never reach the damage model at all.
What arrives is a force magnitude and two collider handles; the contact point is fetched separately from the manifold, because that is what a dent needs and the event does not carry it. Below 350 the hit is a tap and nothing happens. At 3200 it lands the full 0.26m of dent. In between, the depth curve is deliberately sub-linear β raised to the power 0.75 β so glancing blows still leave a mark instead of nothing.
One correction is pure fantasy, and it is stated as such in the source. Contact force undersells hitting light things: a crate cannot push back harder than its own inertia however fast you plough into it. So the reading is multiplied by 1 + speed Γ 0.1 before the damage model sees it. Closing speed is what the fantasy runs on.
The bolts feel a different number from the metal. Visible dent depth is capped per hit, but the damage a panel accumulates is scaled by 0.5 + (force / 3200)^1.35 β superlinear, so one monster impact can walk a panel from bolted, past loose, to gone inside a single frame, while ten small ones leave it dented and still attached.
Every panel near the blow dents toward its own centre, not just the closest one. That is what keeps a crater coherent across a panel seam instead of stopping dead at the shut line.
Coming apart
Bolted β loose β detached
A panel walks one road, and each step is a different kind of object.
Bolted is a plain child mesh of the chassis. It has no body and no collider of its own; it dents, and its damage number climbs.
Loose means the latch gave. The part gets its own rigid body at exactly its current pose, inheriting the chassis's linear velocity plus the tangential velocity at its own offset β Ο Γ r β so a door on a spinning car leaves with the speed that point actually had. Then a revolute joint with limits: the hood swings up through 1.15 radians and no further, the doors open outboard to 1.35, the bumpers droop either way through 1.1. A velocity motor at zero with a factor of 0.15 gives just enough hinge friction that doors flop instead of buzzing.
Detached removes the joint. The part pops along its own escape direction, with a shove added from the blow, scaled by mass so a headlight and a door leave at comparable speeds rather than the light being fired into orbit.
Breaking a latch fires an impulse at the panel's free edge, computed from the hinge anchor. Without it, a door that has just come unlatched simply sits there and the moment reads as nothing at all.
Two collision problems come with all this, and both are solved with filter groups rather than with more physics.
A loose panel overlaps the chassis colliders it was bolted to, so chassis and parts are put in groups that cannot see each other. And a freed part is kept chassis-transparent for a 0.6-second grace period β which does not expire on time. It expires when the part is genuinely more than 3.4m clear of the car; until then the timer keeps resetting itself. Restore collision while a shed roof is still inside the cabin and the depenetration solver launches it into orbit.
The chassis has continuous collision detection enabled, which is the other half of not tunnelling: a car at speed covers more than the thickness of a wall in one 60Hz step.
Glass and wheels
Two things that keep their own ledgers
Panes and wheels do not dent, so they get their own damage accounting β and a couple of rules that fire on somebody else's damage rather than their own.
Each pane is a child of the panel that carries it, so a door keeps its window while it swings and loses it when it is written off. A direct hit within a metre crazes it β a deterministic per-vertex jitter of 0.05 plus a whiter, more opaque material β and a harder one makes it vanish. The interesting part is the rules that do not involve the glass at all: roof damage past 0.5 blows out the windshield and the backlight, and door damage past 0.5 blows that door's window. A caved roof with intact glass is one of those details that looks wrong before anyone can say why.
Wheels tear off when hits within 0.8m accumulate past their own threshold, and the consequence is the good part. The corner does not get a stump: its suspension stiffness, maximum force and both friction figures are all set to zero, so that corner of the car now rides on the chassis collider. The dragging, the sparks and the pull to one side are not simulated β they are what happens when you stop holding a corner up.
The torn wheel becomes a cylinder collider rotated inside its own body rather than a rotated body, because the tire spins on local X and Rapier's cylinders are Y-axis. Rotating the body would have made the spin wrong.
Feel
What the chrome adds
The simulation is a headless object β no renderer, no DOM, driven one fixed 60Hz tick at a time. Everything that makes a crash feel like a crash is layered on outside it, from the same impact list.
Each impact carries a point, a direction, a force and the dent depth it caused. The renderer turns that into a spark burst, a camera shake kick scaled by force / 5200, and β above 3800, at most once every five seconds β a drop into 0.22Γ time that eases back to normal on its own. The chase camera also widens its field of view with speed and eases its follow direction, so a spin does not whip it around.
Keeping all of that outside Sim is what lets the same object run in plain Node for the sanity tests, staging precise crashes through one public method: applyImpact(point, force).
The bill
What this approach costs
Faking deformation with vertex displacement buys a crash model that runs on a phone and gives up any claim to being physical. Some of that is a fair trade and some of it is just the cost.
The collider never changes shape. A panel can be folded into a crater and the box the physics engine sees is exactly the box it started with. You can crumple the nose flat and still bounce off a wall at the old silhouette. Nothing about the damage feeds back into the simulation.
Metal has no memory of being metal. There is no stress, no plasticity, no propagation along a beam. A dent is local to the vertices inside one radius, so a hit on the corner of a panel cannot buckle its far end, and a crumple zone does not actually absorb energy on the way to the cabin.
Force is not force. The reading that drives everything is a contact-force magnitude multiplied by a speed term invented to make light obstacles satisfying to hit. The thresholds β 350, 3200, 5200 β were calibrated by watching, not derived.
Panels can only ever swing one way. A hinge is an axis and two limits, fixed at build time. A door bent backwards by a hit from behind still opens forwards, because there is only one joint and it was authored before anything happened.
A badly beaten panel comes apart into facets. The geometry is de-indexed so that dents crease, which means every triangle owns its own copy of each corner β and the wrinkle term jitters each copy independently, by up to 0.6 of the displacement. At an ordinary hit that is a texture. Land several near-maximum blows on the same spot and the copies separate far enough to see daylight between the triangles. On the car it is mostly hidden, because a panel taking that much damage has usually sheared off its bolts first; in the figure above, at full depth, it is plain.
The grace period is a heuristic. Freed parts stay chassis-transparent until they are 3.4m clear, which means a panel resting against the car is a ghost until it slides off. That is a lie the eye rarely catches and the alternative β restoring collision on a part still overlapping the chassis β fires it across the arena.
Nothing here simulates a material. It moves vertices, tracks one number per panel, and hands the physics engine a new rigid body when that number gets big enough β and at low-poly resolution, in the dusk, that is enough.