Expand description
§bevy_carnage
⚠️ Vibe Coded. Written by AI agents working from a human’s direction, covered by tests rather than a line-by-line human review. Read the code before you rely on it for something you care about.
Wounds that peel to the bone, bleed, pool and dry, and bodies that break into chunks, for Bevy characters, deterministic to the bit.
![]() | ![]() |
examples/preset.rs: a blow, a burn, a slash and shots to the bone | examples/skinned.rs: the same on a skinned, animated body, the wounds riding the limbs |
Status: usable · Bevy 0.20
Why: a fracture computed at the moment of impact costs a frame and a static pre-fracture cannot know where the shot landed, so this crate bakes each asset once into a hierarchy and a bond graph, and every blow is a cheap query against it, which is the shape PhysX Blast and Unreal’s Chaos arrived at too (why).
§Quick start
[dependencies]
bevy_carnage = "0.8"use bevy::prelude::*;
use bevy_carnage::preset::{Gore, GoreHit, GorePlugin, GoreScene};
fn wire(app: &mut App) {
app.add_plugins(GorePlugin);
}
// Dress a loaded character: every mesh under it is dressed by its node's name (`Head`, `Arm_L`).
fn dress(mut commands: Commands, character: Entity, floor: Entity) {
commands.entity(character).insert(GoreScene::humanoid());
commands.entity(floor).insert(Gore::floor());
}
// One message per injury, along your hit ray.
fn on_hit(mut hits: MessageWriter<GoreHit>, target: Entity, from: Vec3, dir: Vec3) {
hits.write(GoreHit::impact(target, from, dir, 0.03, 12.0)); // a crater 3 cm wide, 12 mm deep
hits.write(GoreHit::slash(target, from, dir, Vec3::X, 0.06, 8.0)); // a cut that gapes
}Putting it on a real glTF character has a checklist: docs/preset.md. The full quick start, and baking a fracture by hand, are in the guide.
§Try it
cargo run --release --example presetThe left GIF above, with keys to aim it yourself; T cycles the violence setting.

A frame of preset after the last shot: the chunks, their cut faces, and the pool.
§What you can do
- Hurt a character with one message:
GoreHit::impact,blunt,burnandslashpeel skin, fat and muscle to bone, bruise and burn under the skin, and cut wounds that gape; a wound has a pale lip and, from a hand’s width, a ragged margin round a knobbly floor that glints (0.8.7 and now); a shot sentthrough()leaves a round entrance and a torn, star-shaped exit (the pair, 0.8.3 and now): quick start. - Make wounds bleed where they are: a pulsing bleed runs downhill on the pose on screen, pools on the floor and dries from its edge in where it fell (a dark crust round a red middle, crazed once dry: the front, 0.8.4 and now), a wall’s spatter keeping every drop it landed as, on skinned, animated bodies too:
docs/preset.md. - Let an artery spurt:
GoreHit::arterial()makes the wound throw an arc of large drops on every heartbeat, flying the parabola to the walls and floor, each stain on a wall with its run and a stipple of fine drops beside it, from where the wound is and the way it faces as the body turns, until it clots (the clip, 0.8.9 beside now, the stills):docs/preset.md. - Cut meat that reads as meat: the muscle in a cut face and in a laceration’s bed is a cherry red divided by pale sheets between its bellies and by wandering marbling, drawn at the sizes the anatomy has them and tuned against measured beef and photographs of a shank, not a flat burgundy with a hairline net (0.8.10 beside now, the clip):
docs/cross_section.md. - Cut faces that glisten: the cut faces of a body that has broken are no longer flat plates of one tone but wet, undulating surfaces whose clear coat catches glints along their folds, with the colour unchanged (0.8.12 beside now, the stills):
docs/preset.md. - Burst a body:
GoreBurstthrows a body’s blood in every direction at once, a spray of 960 droplets on the one size-and-speed law a wound’s cone obeys, each flying the parabola to the first wall, floor, ceiling, cloth or body it meets, with the body shadowing what is behind it and the stains arriving over three quarters of a second at a bounded cost a tick (0.8.11 beside now, the stills):docs/preset.md. - Throw a spray that reads as drops: each droplet a wound throws lands as its own round stain at the size it is, on a floor, a wall or a skin, and holds still while it is wet: the gallery.
- Drag a hand, a body or a sleeve through blood:
GoreSmearlifts the wet blood a contact crosses and lays what it carries along its path in parallel streaks, heavy where it touched down and broken into lines as it runs dry, on a floor or a wall, the blood moved and none made (the clip, the stills):docs/preset.md. - Break a body into its own pieces: bake once, then shoot a shoulder and the arm comes off, with watertight cut faces and convex colliders, and the guts it spills lie soaked, lumpy and flattened in its pool, two dark organs among the loops (0.8.6 and now): the API.
- Bore bullet channels that come out the far side: the hole and the gore it throws are the same subtraction: features.
- Honour the player’s violence setting:
GorePolicyswaps blood for sparks, or keeps blood and refuses the breaking, by substitution and never deletion:docs/preset.md. - Replay a fight exactly: every canvas is owned by the CPU and hashed, so two runs of a build print the same digest: determinism.
- Reach the seven kernels underneath: blood rheology, wetmaps, viscera, cross-sections, flaying, laceration and fracture modes, all modules of this one crate: the family.
§Part of the Coven
bevy_carnage is one of the Bevy Coven crates. The Cauldron, the Coven’s editor, uses it for destructible props and its damage dials: use it in the Cauldron.
§Bevy compatibility
bevy_carnage | Bevy |
|---|---|
| 0.6 – 0.8 | 0.20 |
| 0.5 | 0.19 |
§Learn more
- docs.rs/bevy_carnage: the API, with this page as its front page.
- The book page, and the gallery and
docs/DEMOS.mdfor every recorded example. docs/guide/: everything the old README carried, from the API to what each release changed.examples/andCHANGELOG.md.
§Who and licence
Maintained by @Ladvien. This repository is the source of truth: issues and pull requests belong at github.com/bevy-coven/bevy_carnage/issues, and how to contribute follows the org’s CONTRIBUTING.md.
MIT OR Apache-2.0, at your option.
Re-exports§
pub use crate::bloodstain::Appearance;pub use crate::bloodstain::BACK_SPATTER_SPEED;pub use crate::bloodstain::BLOOD_DENSITY;pub use crate::bloodstain::BLOOD_SURFACE_TENSION;pub use crate::bloodstain::Bleed;pub use crate::bloodstain::BloodSettings;pub use crate::bloodstain::Droplet;pub use crate::bloodstain::FORWARD_SPATTER_SPEED;pub use crate::bloodstain::Impact;pub use crate::bloodstain::PatternClass;pub use crate::bloodstain::Pool;pub use crate::bloodstain::Stain;pub use crate::bloodstain::StainShape;pub use crate::bloodstain::WELD;pub use crate::bloodstain::WoundKind;pub use crate::bloodstain::absorb;pub use crate::bloodstain::appearance;pub use crate::bloodstain::area_of_origin;pub use crate::bloodstain::droplet;pub use crate::bloodstain::droplet;pub use crate::bloodstain::droplet_count;pub use crate::bloodstain::droplets;pub use crate::bloodstain::flow;pub use crate::bloodstain::flows;pub use crate::bloodstain::hash_f32;pub use crate::bloodstain::landing;pub use crate::bloodstain::pick;pub use crate::bloodstain::pulse_period;pub use crate::bloodstain::pulse_wound;pub use crate::bloodstain::pulses_on;pub use crate::bloodstain::spread_pools;pub use crate::bloodstain::viscosity;pub use crate::bloodstain::wound_seed;pub use crate::bloodstain::yield_stress;pub use crate::bloodstain::stain::impact_at_plane;pub use crate::bloodstain::stain::rasterise;pub use crate::bloodstain::stain::stain_radius;pub use crate::bloodstain::stain::stain_shape;pub use crate::bloodstain::stain::stains;pub use bloodstain as blood;
Modules§
- bloodstain
- The blood model’s own modules, re-exported so a caller can reach the parts that have no
single-name entry point —
bevy_carnage::blood::patterns::cast_off,blood::rheo::flows,blood::dry::dry_ticks. One path to each function, spelled the way the leaf spells it. - cross_
section - The six kernels this crate composes, re-exported whole so a consumer needs one dependency
line and cannot end up with two versions of a leaf. Each is reached by its own name — a cap’s
bands from
bevy_carnage::cross_section, a body’s peel frombevy_carnage::flaymap, a cut that gapes frombevy_carnage::laceration, the modes a blow is projected onto frombevy_carnage::fracture_modes, the texture-space blood a hit leaves frombevy_carnage::wetmap, and the strands a torso spills frombevy_carnage::viscera— and this crate’s own bridges aremodal,FractureRegionandFragmentGeometry::annotate_cap.wetmapandviscerawere dependencies without a door until 0.3.1;examples/entrails.rsandcarnage_web.rshad to name them directly, which is the two-versions hazard this block exists to close. - flaymap
bevy_carnage::flaymap- flesh
- Flesh as a material — the pixel half. Behind
flesh(default on) because it embeds a shader and asksbevyforbevy_image; the deterministic half never needs either. Flesh, as a material. The pixel half of the stack: everything the CPU kernels compute is a byte in a canvas or a coordinate on a cap, and until 0.4.0 all of it was drawn by a plainStandardMaterial— flat red caps, matte stains, fat that reads as painted plastic. This module is oneExtendedMaterialoverStandardMaterialthat reads those same bytes and renders them the way tissue and blood actually scatter and reflect light, on the forward path, on WebGL2. - fracture_
modes bevy_carnage::fracture_modes- laceration
bevy_carnage::laceration- modal
- Fracture modes over a bond graph —
fracture_modescomposed onto this crate’s own decomposition. - preset
- The two-line inclusion:
GorePlugin,Gore,GoreHit. Behindflesh, because it dresses every surface it touches in the flesh material. The two-line inclusion.GorePlugin, aGorecomponent on the meshes that can be hurt, and aGoreHitmessage when one is — everything else in this family is wired behind it. - skin
- A skinned mesh, posed on the CPU — the shader’s blend of joint matrices, so a ray can be cast at the body as it is drawn rather than at its bind pose. The preset casts every hit through it. A skinned mesh, posed on the CPU.
- viscera
bevy_carnage::viscera- wetmap
bevy_carnage::wetmap
Structs§
- Bleeding
- A bleeding thing, as an entity. The ECS half of
Bleed. - Bond
- The shared face between two neighbouring fragments.
- Bond
Graph - Which fragments touch which, and over how much surface.
- BondId
- Index of one bond in a
BondGraph. - BondSet
- The caller’s accumulated damage state: which bonds have been severed so far.
- Bore
- A channel to subtract from the proxy — a bullet hole, a drill, a spear thrust.
- CapFace
- One cut face of a convex cell — the wound surface that travels with a fragment or a plug.
- Carnage
Plugin - Registers the fracture cache, its settings, the one-shot bake, and the
Woundedmessage. - Carnage
Settings - The carnage dials — how a wound bleeds, sprays, stains and hits, with nothing in it that decides when any of that happens.
- Carnage
Systems - The set
bake_fracturesruns in. Gate and order against this, not against the system. - CutSettings
- The geometry dials for one bake, without the ECS sizing policy that chooses
target. - Decal
Budget - The live-decal ceiling, with canonical eviction.
- Detached
Chunk - The pruned
DetachedPart, flung intact as a single tumbling chunk (it kept its own material). Baked in the same subject-local space as body fragments. - Detached
Part - Marks a subtree to be pruned out of the body and baked as one intact chunk, keeping its own material — a carried weapon, a hat, a backpack. The walk does not descend past it.
- Ejecta
- What a bore pushed out the far side — the channel’s own material, as a spawnable chunk.
- Ejecta
Chunk - A plug a bore pushed out, baked into mesh handles alongside the fragments.
- Flash
Gate - WCAG’s safe harbour, as a hard ceiling. Three flashes in any one-second period.
- Fracture
- One bake: every node the cut loop produced as a solid, plus the hierarchy that says how they nest — and the drawn meshes for the nodes somebody actually asked for.
- Fracture
Bores - Channels bored through this subject’s proxy before it is cut — bullet holes, baked in.
- Fracture
Cache - Baked fracture data, keyed by the subject’s source scene asset id so multiple distinct subjects each get their own bake and swapping the asset needs zero code change.
- Fracture
Proxy - The caller’s convex decomposition of the subject, in subject-local space.
- Fracture
Region - Which body region this subject’s cut faces belong to — a limb, a torso or a head.
- Fracture
Settings - How hard to break things. Twelve dials, all bake-time — nothing here decides how a chunk moves after it exists, because that is the caller’s physics, not this crate’s business.
- Fracture
Subject - Marks an entity whose descendants should be pre-fractured, and names the asset to key that bake
by. Put it on the root the scene hangs under; the bake walks
Childrenfrom there. - Fragment
- One baked body fragment, in subject-local units (render scale is applied at spawn). Both
meshes are recentered to
center_local(their shared bounding-box center), so a physics body placed atorigin + center_local*scalewith ahalf_extents*scalebox collider lines up exactly with the rendered chunk. Either mesh may beNone(a fragment with no cut faces has no cap; a pure-cap sliver has no outer skin). - Fragment
Geometry - One fractured piece: a convex proxy cell, and the render surface that belongs to it.
- Fragment
Id - Index of one fragment — a node in the
FragmentTreeand, equivalently, a position in the parallel fragment payload array (Vec<Fragment>orVec<FragmentGeometry>). - Fragment
Solid - Tier A for one node: the solid, with no drawn mesh. Cheap to produce for every node of the
tree, which is why it is separate from the geometry — a bake keeps one of these per node and
builds the
FragmentGeometryonly for the ids a caller actually asks to draw. - Fragment
Tree - The binary forest a bake produced: one tree per proxy cell, plus the frontier queries that read it at a chosen granularity.
- Gore
Policy - The framework’s one policy resource. What is drawn, how much of it, and what is refused.
- Joint
Group - The joints that make one piece, and the one that carries it.
- Joint
Link - Two pieces that meet at a joint: indices into the
groupsslice, and the joint whose bind position is where they are bonded. - Proxy
Cell - A convex cell of the caller-supplied proxy: the volume Tier A actually cuts.
- Reach
- How strongly a region reached each bond it touched.
- Skeletal
Fracture - What
fracture_skeletonmakes: the pieces, and the bonds between them as a graph overFragmentId(0)..FragmentId(pieces.len()). - Skeletal
Piece - One piece of the skeleton: group
iis piecei,idisFragmentId(i). - Solid
Audit - What one finished fragment turned out to be.
- Surface
Report - What a fragment’s drawn surface turned out to be. Every field is a count to be recorded.
- Tree
Node - One node of the hierarchy. Roots are the caller’s proxy cells, unsplit.
- Wound
- A wound surface in subject-local space. Deterministic: derived only from baked geometry.
- Wounded
- A wound, in world space, announced. The crate’s one message.
Enums§
- Fault
Policy - How the cut plane is chosen. One policy with a parameter, not a switch between two engines.
- Gore
Tier - How much gore is drawn. Four stops, and they map onto rating descriptors rather than taste.
- Loading
Mode - How a subject was loaded when it broke.
- Skeletal
Refusal - Why a body could not be cut along its skeleton. Nothing is repaired or guessed.
- Tissue
Class - What is breaking. Cortical bone, trabecular bone, or soft tissue.
Constants§
- MAX_SAG
- The flat-face sag a channel is allowed, subject-local, where a character is about 1.0 tall.
- MAX_
SIDES - Barrel faces beyond which the shard count is the cost and the roundness is not the benefit: 24 planes already read as smooth, and each one is a shard with its own two meshes.
- MIN_
ROUND_ SIDES - The fewest faces
sides_forwill derive, as opposed to the fewest [prism] will accept. - TRABECULAR_
MAX_ PIECES - Ceiling on the fragment count for trabecular bone.
- WCAG_
FLASHES_ PER_ SECOND - The absolute ceiling on flashes per one-second window, whatever a policy says.
Functions§
- audit_
cell - Audit one convex cell as the closed solid it is.
- audit_
proxies - Audit every fragment of a fracture. Fragments that cannot be measured are
warn!-skipped and omitted, so the returned length may be shorter thanfrags— deliberately, because padding the result with a fabricated clean audit is exactly the lie this module exists to stop telling. - audit_
proxy - Audit a fragment as the solid it is — its proxy cell, every face, closed.
- audit_
render - Measure a fragment’s drawn surface — the subject’s own skin plus the cut face, welded.
- bake_
fractures - Once a subject’s whole scene has streamed in, bake its fracture set (and its detached chunk)
exactly once per source. Walk the subject’s descendants, prune the
DetachedPartsubtree into a separate chunk, merge the rest into one soup in subject-local space, and fracture. Self-gates on all sub-meshes being present inAssets<Mesh>. - cap_
faces - Every raw-interior face of a convex cell: fracture cut faces and bore channel walls.
- capsule
- A swung edge. Falloff by distance from the segment
a → bto each shared face. - coalesce_
hitstop - One stop per frame, not a sum.
- directional
- A pull. Radial falloff, weighted by how squarely each shared face meets
direction. - fracture_
mesh - The whole pipeline, with no assets and no ECS. Cut the caller’s convex
proxyinto at mosttargetcells, carry thepartstriangles along as a payload, and return each piece. - fracture_
skeleton - Cut a skinned body along its skeleton, once.
- grady_
mott_ target - How many fragments an impact should produce, from the energy — not from an artist constant.
- hitstop_
ticks - How many fixed ticks of hit-stop this wound deserves. Zero for a graze.
- largest_
cap - The widest raw-interior face of a cell — “the wound on this chunk”.
- materialise_
fragments - Build the Tier B meshes somebody asked for. Chained straight after
bake_fractures, so a bake and a request that happen on the same frame are drawn on that frame — otherwise the first frame after a death would spawn nothing. - occludes_
aim - Would this land on the reticle?
truemeans refuse it. - radial
- A blast. Falloff by straight-line distance from
centerto each shared face. - shake_
offset - Camera offset for a given trauma at a given tick: trauma² magnitude, eased, along
dir. - sides_
for - The fewest barrel faces that keep a channel of this radius under
MAX_SAG, inMIN_ROUND_SIDES..=MAX_SIDES. - spread
- A projectile. Find the fragment nearest
point, then spread outward along the bonds, with falloff on distance travelled through the object rather than through space. - swept_
triangle - A swept blade. Every bond whose two fragments sit on opposite sides of the triangle
a, b, c, and whose joining segment crosses inside it, gives way completely. - trauma_
for - Trauma to add for a wound, in
[0, 1]— the caller adds it to its own accumulator. - wound_
from_ ejecta - The wound a baked plug leaves behind —
wound_of_channelfor anEjectaChunk. - wound_
of_ channel - The wound a channel is, from the plug’s own geometry.
- wounds_
from_ bonds - The wounds a set of already-broken bonds represents, at full severity.
- wounds_
from_ reach - The wounds a blow opened: every bond it reached at or above
threshold.

