Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Bevy Coven

Some parts of a game only grow as fast as people can author them: levels, set dressing, dialogue, the lives of villagers, interfaces, weather. Bevy Coven is a family of Bevy crates built so that those parts grow with agents too.

⚠️ 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.

The coven, the grimoire, the cauldron and the familiar

  • The coven is the family of crates. Each one is a Bevy plugin with its own settings, and works on its own: a sky, a forest, a crowd, a dialog box.
  • The grimoire is the book of spells, and it is what a game loads: its kits, the grammar learned from your example, and the maps built from that grammar (bevy_grimoire).
  • The Cauldron is the editor where the grimoire is written: you and the familiar place pieces, and the learned grammar brews more.
  • The familiar is the helper at your side: the agent that reads and writes the grimoire, through the Cauldron’s MCP server.

Two more helpers belong to the same story: bevy_fairy, which lets an agent read and drive a running game, and golem, which drives Blender to make meshes.

How the crates relate

  • Every crate stands alone: add it with cargo add and its plugin with app.add_plugins(...). See Using a crate on its own.
  • The Cauldron hosts each crate as an optional cargo feature and edits that crate’s own settings live, in your game’s project. See Using a crate in the Cauldron.
  • Some crates use each other. bevy_society speaks through bevy_speech_bubbles, steers its walkers with bevy_orca and leaves marks with bevy_stigmergy.

The map of the coven shows every crate, grouped by what it gives a game.

The map of the coven

Every crate in the family, grouped by what it gives a game. Each picture plays from the crate’s own repository.

The grimoire and the Cauldron

bevy_grimoire

The grimoire is what a game loads: a library of pieces and a map. The Cauldron is the editor that writes it: it learns a world’s grammar from your example and builds more, with the Familiar beside you.

World

bevy_elements

Sky, weather, wind, clouds, terrain and water, simulated in compute shaders.

bevy_flora

Procedural stylized trees and shrubs that grow over time.

isomesh

isomesh and bevy_isomesh: signed distance fields in, meshes out, with caves and carving.

bevy_lights

Flickering fires and a budgeted pool of real lights over thousands of virtual ones.

bevy_light_grid

An illuminance grid that creature AI can read and follow.

Life

bevy_society

Villagers with needs, moods, memories, relationships, rumours and factions.

bevy_stigmergy

Fading marks on a grid that crowds coordinate through, like ant trails.

bevy_orca

Walkers that steer around each other without colliding.

bevy_speech_bubbles

Speech balloons, barks, conversation graphs and a dialog box.

Interface

bevy_gooey

Menus, HUD meters, dialogs and tooltips with keyboard and gamepad navigation.

Spectacle

bevy_carnage

Wounds that bleed and bodies that break, deterministic to the bit.

Agents and tooling

bevy_fairy

Lets a coding agent read and drive a running Bevy game.

Using a crate on its own

Every crate in the coven is a Bevy plugin plus its settings, and none of them needs the Cauldron.

  1. Add it:

    cargo add bevy_elements
    

    A crate that is not on crates.io yet (bevy_light_grid) is a git dependency:

    bevy_light_grid = { git = "https://github.com/bevy-coven/bevy_light_grid" }
    
  2. Add its plugin to your app:

    use bevy::prelude::*;
    
    fn main() {
        App::new()
            .add_plugins((DefaultPlugins, bevy_elements::ElementsPlugins))
            .run();
    }

    Each crate’s page in this book, and its README, gives its own plugin name and a quick start that compiles.

  3. Tune it. A crate’s settings are plain Rust types that implement serde’s Serialize and Deserialize, so you can build them in code, or load them from a RON or JSON file in your project.

Each crate’s README has a cargo run --example line that opens the scene in its hero GIF; that is the fastest way to see what it does before you add it.

Using a crate in the Cauldron

The Cauldron is the coven’s editor, where the grimoire a game loads is written. It runs as its own program on your game’s project, so the editor never becomes part of your game’s build:

cargo run -p bevy_cauldron -- <game-root> <map> --kit <kit>

With --live, every change is written through to disk as you make it, for a game that watches its own project.

Each crate is an optional feature of the editor

The Cauldron takes each crate in the family as an optional cargo feature, pinned to a git revision on that crate’s main. Turning a feature on gives the editor that crate’s panels: a sky and weather panel for bevy_elements, a forest panel for bevy_flora, villagers for bevy_society, and so on.

Configure, don’t fork

The editor never defines a second schema for a crate’s settings. A crate’s own serde types are the data: the Cauldron saves them in your game’s project, and edits them live. So the file the editor writes is the same file your game loads with the crate on its own, and a game can drop the editor at any time.

The two-way street

When the editor needs something from a crate, the change is made in that crate’s repository, with its own tests, and requested there as an issue labelled consumer and consumer:bevy_grimoire. The editor takes it by moving its pin once the issue is closed as fixed. A game asks the editor for what it needs the same way, with consumer and consumer:<game> on the editor’s repository.

The Familiar

The Cauldron’s MCP server (bevy_cauldron_mcp) lets an agent, the Familiar, read the editor’s state with grimoire_state and change a map with grimoire_apply_operation. Each crate’s page says what its panel lets you, or the Familiar, change.

Grimoire and the Cauldron (bevy_grimoire)

The grimoire is the book of spells a game loads (bevy_grimoire and bevy_grimoire_core). The Cauldron is the coven’s editor, where you write it: a world editor for Bevy games where you build levels with an AI agent, the Familiar, working beside you. The Familiar changes a map only through the same named operations you use, its changes join your one undo history, and you show it what you mean by snipping a rectangle of the editor with a note.

The demo

64 seconds on a walled map: the agent frames two wall pieces, turns wall_02_2 to 90 degrees and removes wall_02_3; the person’s Cmd+Z reports put back wall_02_3. It ends with a snip: the person drags a box over the walled cluster, writes a note, and the receipt names the PNG it saved.

Add it

The editor is a program you run from a clone, on your game’s project:

git clone https://github.com/bevy-coven/bevy_grimoire
cd bevy_grimoire
cargo run -p bevy_cauldron -- /path/to/your/game

Your game links only the libraries: cargo add bevy_grimoire_core bevy_grimoire. bevy_grimoire_core is engine-free and holds the grammar and the solvers; bevy_grimoire puts a library and a map into a Bevy world (GrimoirePlugin), and GrimoireLivePlugin reloads them while the game runs, so an edit shows up about a second later. A project from before the rename keeps its data in assets/emerge; the editor moves it to assets/grimoire the first time it opens the project.

What it gives you

  • Maps built from kits: place, fill, turn, remove and undo (building maps).
  • Layouts learned from a corner you laid out and continued with wave function collapse, drawn before you keep them.
  • Kits and labels, with a vision model’s proposals marked unchecked until you confirm them (kits and labels).
  • The MCP server, bevy_cauldron_mcp, whose tools (grimoire_state, grimoire_apply_operation, captures, snips) let the Familiar read and drive the open editor (MCP tools).
  • Panels for the rest of the coven, each editing that crate’s own settings (using a crate in the Cauldron).

Use it in the Cauldron

This is the Cauldron. Every other crate’s page says what the Cauldron does with it.

bevy_elements

Living environments for Bevy 0.20: a sun and moon on real ephemerides, the bright-star sky, a seeded climate that schedules hourly weather, a GPU wind field, volumetric clouds, rain and snow, lightning and thunder, fog, heightfield terrain with erosion and flooding, ground cover, rocks, dust, paths and trails — all in compute shaders. Everything runs behind one Environment system parameter, so gameplay asks the simulation what the ground, the water, the light and the weather are doing instead of guessing at it.

Add it

cargo add bevy_elements
use bevy::prelude::*;
use bevy_elements::*;

fn main() {
    App::new()
        .add_plugins((DefaultPlugins, ElementsPlugins))
        .insert_resource(WorldClock::new(CivilDate::new(2026, 6, 21)).at_hour(7.0))
        .insert_resource(Climate::temperate())
        .add_systems(Startup, setup)
        .run();
}

fn setup(mut commands: Commands) {
    // The environment follows and renders for the `ElementsCamera`.
    commands.spawn((
        Camera3d::default(),
        ElementsCamera::default(),
        Transform::from_xyz(0.0, 160.0, 0.0).looking_at(Vec3::new(100.0, 100.0, 0.0), Vec3::Y),
    ));
    // 2 km of rolling hills, centred on the origin.
    commands.spawn(Terrain::procedural(ProceduralTerrain::hills(42)));
}

Or run it first: cargo run --release --example quickstart.

What it gives you

  • A living sky over any scene — sun, moon and 8 404 stars from real ephemerides, with exposure that keeps night dark: sky, weather and light.
  • A seeded climate that schedules the weather — Clear through Blizzard, hourly, with seasons and a snow season a game sets by date: climate, seasons and weather.
  • Rain, snow, fog and lightning that read from every camera — including orthographic and isometric views, through PrecipitationLook::readable(): precipitation.
  • A terrain you generate, sculpt and save — procedural presets, a height map or flat ground, brushes on the live heightfield, snapshots to save and restore: terrain.
  • Ground that wears — drifting snow and melt, wetness and puddles, blown sand, trodden paths, pressed footprints: ground cover.
  • Water that answers — shallow-water hydrology with erosion, lakes and rivers, wakes from anything moving through: rivers, lakes and waves.
  • A simulation gameplay can query — ground height and normal, water depth and velocity, snow depth, cloud shadow, the lumens a night lamp needs: gameplay queries.

Use it in the Cauldron

The Cauldron takes bevy_elements as the optional cargo feature bevy_elements (the volume snow feature builds on it as volume_snow), and its map environment panel edits this crate’s own serde types, written to <project>/family/bevy_elements.ron so the editor’s preview and the game read the same values:

  • WorldClock, GeoLocation and Climate — the date, the clock, where the observer stands and the climate the scheduler draws from.
  • WeatherPreset as forced_weather — Some(Rain) forces one preset and None releases the weather back to the climate’s own schedule, so a map can be authored under a storm and played under whatever the season brings.
  • SnowSettings, WetnessSettings, RockSettings and DustSettings — how the snow settles, drifts and melts; how the ground wets and puddles; how many rocks lie on the ground; how the sand moves.
  • SurfaceCover as volume_cover — a fixed snow and wetness a carved bevy_isomesh volume wears whatever the weather is, which is what a preview of a snow-capped cliff needs.

The editor also builds a map’s heightfield from ProceduralTerrain‘s presets (hills, mountains, dunes, plains) and puts bevy_elements’ SnowyField over a carved volume. Nothing outside those types is reachable from the editor yet: EnvironmentSetup and SurfaceSetup — the whole sky-and-ground setup as one saved, validated value that applies live — are the natural next panel, and the terrain’s TerrainSetup, SculptTerrain and RestoreTerrain would give it sculpting and undo.

bevy_flora

bevy_flora is the coven’s trees: parametric, stylized procedural trees for Bevy 0.20 that grow over time. A tree is a TreeParams value with a seed in it — one of 20 presets or every field tuned by hand — and the same value makes the same tree on every run, at every age. Twenty thousand of them draw from a pool of a few seed variants in under 4 ms a frame, and they can be notched, felled, bucked, burnt, shaken and picked like game objects rather than looked at like scenery.

Add it

cargo add bevy_flora

Add the plugin and spawn a tree. Growing advances its age every frame until it is mature; leave it off and set TreeAge yourself.

use bevy::prelude::*;
use bevy_flora::{FloraPlugin, Growing, ProceduralTree, TreeAge, TreePreset};

fn main() {
    App::new()
        .add_plugins((DefaultPlugins, FloraPlugin))
        .add_systems(Startup, setup)
        .run();
}

fn setup(mut commands: Commands) {
    let mut params = TreePreset::Maple.params();
    params.seed = 7;
    commands.spawn((ProceduralTree(params), TreeAge(0.0), Growing::default()));
    commands.spawn((DirectionalLight::default(), Transform::from_xyz(10.0, 20.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y)));
    commands.spawn((Camera3d::default(), Transform::from_xyz(0.0, 5.0, 20.0).looking_at(Vec3::new(0.0, 4.0, 0.0), Vec3::Y)));
}

cargo run --release --example showcase is the same thing with the controls wired up; the README’s quick start and its doctests are the version CI compiles.

What it gives you

  • A tree over any age. TreeAge and Growing on the CPU, or GpuGrowth for two hundred oaks at 0.81 ms a frame — growing.
  • A forest. TreePool and PooledTree with three levels of detail and baked impostors, BackgroundPools to build them off the frame, PoolColors for more than one look, Scatter for where the trees stand, FloraGround for the terrain, FloraUnits for a world that is not in metres — forests.
  • A look. Bundled CC0 bark scans and real leaf outlines, veins grown by space colonisation, Natural, Painterly and Toon styles, and low-poly CrownStyle::Hull conifers — bark, leaves and looks.
  • A year. FloraSeason for autumn colour, leaf fall, snow and blossom, fruit through summer, and FloraWetness for rain — seasons.
  • Things that happen to trees. TreeShakes, DropLeaves, TreeFruit, TreeNotch and FellTree, BuckTree, FloraFire and FoliagePusher — interaction.
  • Answers for the game. TreeShape for colliders, FloraIndex (nearest tree, trees within a radius, the trunk a ray meets), TreeCondition for sick and dying trees, and what a save holds — gameplay.
  • Cameras. SeeThroughTrees to keep the player visible, and orthographic cameras whose levels of detail follow the zoom — cameras.

Use it in the Cauldron

The Cauldron takes this crate as the optional cargo feature bevy_flora (pinned to a git revision of main, with the crate’s ron feature on). With the feature off, the two operations are still registered and refuse, naming the feature, so the editor’s surface does not change with the build.

Two operations, both writing files the game loads with the crate on its own:

  • tree_edit writes <grimoire_dir>/trees/<name>.tree.ron, which is TreeParams in the format TreeParamsLoader reads. preset starts the file from one of the 20 presets (by name, through TreePreset::from_name); set changes fields by dotted path into TreeParams — trunk.radius, branches.density, foliage.scan, crown, trunk_style, style, wind, bark — and a path the crate does not know is refused with the crate’s own message, so a typo is never written. The settings are the crate’s serde types; the editor defines no second schema for them. A tree that shares the file regrows after an edit with nothing to recompile, which is ProceduralTreeAsset and the file_watcher.
  • forest_scatter writes <grimoire_dir>/forest/<map>.ron: one Tree per trunk — an id, a species (a preset name or a .tree.ron path), its (x, z), its ground height, its scale, its seed and its pool variant — masked to the stroke the author drew. The editor plants it with the crate’s own Scatter (its Poisson-disc spacing, groves and species weights, its seed) and stands each trunk on the map’s terrain with FloraGround.

In the viewport the trees are always the crate’s: a scattered trunk is a PooledTree of its species’ TreePool, built with BackgroundPools; a tree placed by hand is a ProceduralTree at its own TreeAge and seed, so the same seed grows the same tree in the editor and in the game. The preview’s FloraSeason follows bevy_elements’ WorldClock, so the month set in the environment panel turns the forest from blossom to leaf to autumn to snow.

Not yet a panel: FloraFire, TreeCondition and SeeThroughTrees have no editor surface — a fire’s grid (FloraFire::cells) and a tree’s condition would each be a file beside the forest’s, restored with FloraFire::with_cells and restore and with insert(TreeCondition::Sick).

isomesh

Signed distance field in, triangles out. An SDF (signed distance field) is a function that returns how far a point is from a surface, negative inside; isomesh turns one into a mesh with any of seven extractors, so a voxel world can have caves, arches and overhangs that a heightfield cannot store, and a CAD tool can have f64 precision from the same crate. Three crates live in the repository: isomesh (the engine-free core, no_std, one dependency), bevy_isomesh (the Bevy plugin and every example) and isomesh-gpu.

Add it

cargo add isomesh        # the core, no engine
cargo add bevy_isomesh   # the Bevy plugin

The README has a quick start compiled on every cargo test. Eleven of the demos run in a browser at bevy-coven.github.io/isomesh.

What it gives you

  • Seven extractors: Marching Cubes (with MC33’s tunnels), Surface Nets, Dual Contouring, Manifold Dual Contouring, Marching Tetrahedra, greedy quads, and subgrid Marching Tetrahedra, which sees features thinner than a voxel (algorithms).
  • Chunks that tile with no cracks at their seams, streaming, LOD with Transvoxel seams, and meshing off the main thread under a frame budget (what it can do).
  • Validity and accuracy checks as public API: Euler characteristic, manifoldness, orientation, self-intersections, determinism, Hausdorff distance (what gets checked).
  • GPU extraction in isomesh-gpu, 37 times faster than the CPU at 129³ when the field is evaluated in the shader.

Use it in the Cauldron

The Cauldron takes isomesh for carved volumes (issue #101): a volume’s edit list is the data, and the editor samples it over the ground one chunk at a time, each chunk sharing its boundary plane with its neighbour so the mesh has no cracks, and meshes it with isomesh. That is how caves, overhangs and tunnels a heightfield cannot hold get onto a map, and how bevy_elements’ terrain gets them too.

bevy_lights

Practical lights for Bevy 0.20: the fires a scene’s props actually carry. A world can hold thousands of torches and afford a dozen real lights, so this crate ranks the candidates by the light they put on you, hands the budget out without a pop, modulates each light the way a fire’s light moves, and draws the flame that motivates it — with sparks, smoke, tongues for a wide fire and a bed of coals that goes on glowing after the fire is put out.

Add it

cargo add bevy_lights
use bevy::prelude::*;
use bevy_lights::*;

#[derive(Component)]
struct MyFocus;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins((FlickerPlugin, LightPoolPlugin::<MyFocus>::default(), FlamePlugin, FirePlugin))
        .add_systems(Startup, |mut commands: Commands| {
            let fire = Fire::lit(FireKind::Campfire, 250.0, 0.35);
            commands.spawn((fire, Transform::from_xyz(4.0, 0.0, 2.0)));
            commands.spawn((MyFocus, Transform::default()));
        })
        .run();
}

Fire::lit(kind, lux, over) is a whole fire in one component: the kind’s flame, the light the pool hands it, its sparks, smoke, tongues and bed of coals. Or run it first: cargo run --release --example courtyard.

What it gives you

  • Fires that flicker, flare and go out (fires): Flicker dips a light below its base at the rate its fuel bed sets, Kindle flares it up over 0.6 s, Snuff puts it out over 1.2 s with the linked flame.
  • A budget of real lights for a town of thousands (the light pool): ranked by the light each candidate puts on you, held with hysteresis, cross-faded on a hand-off, with a shadow budget of its own and one template that reaches every light live.
  • The flame drawn where the fire is (shader flames): one additive billboard per fire, breathing on its light’s clock, in a hue of its own, shedding sparks, smoking, leaning in a wind and streaming behind a torch you carry — and going out when a wall hides its source.
  • Coals that outlive the flame (the fuel bed): a temperature field that breathes with the flicker, warms as the fire catches and cools over a minute once it is put out.
  • A cinematographer’s numbers (the lighting look): fire_lumens, fill_for, blackbody_colour and white_balanced turn “1000 lx at 4 m” and “1900 K” into a sized, coloured light.
  • Every curve outside the engine (engine-free maths): the same maths as plain f32 functions with no bevy in them, for a host that drives its own lights.
  • What a host touches, and what it saves (host API): the components and resources a game spawns, marks and reads, and the serde feature that makes a placed fire one line of a scene file.

Use it in the Cauldron

The Cauldron, the Coven’s level editor, takes bevy_lights as an optional cargo feature (bevy_lights) and a project lists it in its family.ron. Two things it does with the crate today:

  • A placement’s fire is the crate’s own settings type. family_set writes a field of the fire’s Flicker — base, phase, depth, diameter — in <project>/family/bevy_lights.ron, and the placed prop reads back as the crate’s Flicker, so the editor has no schema of its own for it. placement_set_family puts that value on one placement, or takes it off.
  • Two read-only operations answer “which of my torches are real, and how lit is this tile?” — light_pool runs the crate’s own LightPoolPlugin over the map’s fixtures and reads its PoolBudget for where the viewer stands, and light_map bakes a bevy_light_grid illuminance field from the same fixtures.

There is no panel for the fire kinds yet. One would edit Fire { kind, lumens, phase, color, embers, smoke, tongues, bed } — the whole fire as one serde value — and read Fire::problems() beside the fields, which is what every settings type in the crate returns: a list of Problem { field, kind } naming what cannot work, before the value reaches a light.

bevy_light_grid

An illuminance grid that creature AI can read. It answers “how bright is it here, and which way is brighter” per cell, on the CPU, so a creature can flee into shadow or climb toward a lamp. Your GPU lighting already knows how bright each pixel is, but that answer lives in a framebuffer gameplay cannot read; this crate computes it in a form gameplay can sample and differentiate.

Add it

Not on crates.io yet:

[dependencies]
bevy_light_grid = { git = "https://github.com/bevy-coven/bevy_light_grid" }

The README has a quick start that is compiled on every cargo test. The crate takes bevy_math and bevy_ecs and nothing that draws.

What it gives you

  • LightGrid with bake (static lamps, rerun when one changes) and compose (moving FlashlightCones on the cached bake, every tick), both shadowed by your own line-of-sight closure (the field).
  • sample_cell and gradient_cell, and light_push_at for a steering push up or down the gradient.
  • Photophobic, Photophilic and Phototropic markers, and phototropic_scale for growth toward light.

Use it in the Cauldron

The Cauldron takes this crate behind its bevy_light_grid feature for the light map (issue #103), a read-only operation and overlay. Every placement that carries a bevy_lights Flicker is a fixture, a placement whose descriptor is a wall blocks line of sight, and the values drawn over the map are the crate’s own LightGrid::sample_cell, so what the author reads is what a light-shy creature in the game reads.

bevy_society

Living societies for Bevy: villagers who remember, gossip, take sides, fall out and make up. Big Five personalities, moods on a pleasure/arousal/dominance axis, an OCC emotion set, needs that decay and memories that fade; utility-AI daily lives over tagged places and other people; directed ties with a Dunbar-style cap; opinions, rumors, reputation and factions; Rhai scripts, dialogue through bevy_speech_bubbles; and a save that restores the society the reload would have produced.

cargo run --release --example capture -- --scene village: one afternoon in a village of twenty-four people. The plate reads Day 4 · 14:01 Friendships 30 · Rivalries 1 on the first frame and Day 4 · 20:01 Friendships 27 · Rivalries 1 with “Bram stole bread” — known by 2/24 on the last.

Add it

cargo add bevy_society
#![allow(unused)]
fn main() {
use bevy::prelude::*;
use bevy_society::{
    Autonomous, Home, Person, Place, PlaceTags, SocietyClock, SocietyDefs, SocietyPlugins,
    SocietySettings,
};

let defs = SocietyDefs::starter().expect("the starter pack is valid");
// A headless app has no frame clock, so `Time<Fixed>` never advances on its own. Without this the
// society takes zero ticks however many updates you run.
let step = std::time::Duration::from_secs_f64(1.0 / 64.0);

let mut app = App::new();
app.add_plugins((MinimalPlugins, TransformPlugin))
    .insert_resource(bevy::time::TimeUpdateStrategy::ManualDuration(step))
    .insert_resource(SocietySettings::default())
    .insert_resource(defs);
// `DialoguePlugin` needs a speech-bubbles plugin over a camera, which this snippet has not.
let plugins = SocietyPlugins.build();
#[cfg(feature = "dialogue")]
let plugins = plugins.disable::<bevy_society::DialoguePlugin>();
app.add_plugins(plugins);

let well = app
    .world_mut()
    .spawn((Name::new("well"), Place { capacity: 6 }, PlaceTags(vec!["gathering".into()]), Transform::default()))
    .id();
for name in ["Bram", "Alys", "Corin"] {
    app.world_mut()
        .spawn((Person, Autonomous, Name::new(name), Home(well), Transform::default()));
}

for _ in 0..600 {
    app.update();
}
// The society advanced: this line is what proves the fixed step was pinned.
assert!(app.world().resource::<SocietyClock>().tick > 0);
}

With the default dialogue feature, DialoguePlugin panics in finish without bevy_speech_bubbles::SpeechBubblesPlugin::<YourCamera> over a camera; add that first, or .disable::<DialoguePlugin>() as above.

What it gives you

  • People with a mind. Big Five personalities, PAD mood, OCC emotions, decaying needs, fading memories, and a Chronotype so a lark and an owl keep their own hours in one village. features
  • A day they choose. Utility-AI scoring over tagged places and other people, with travel, arrival, durations, per-tier ranks and an urgency that wins the tier while a need is low. features
  • Relationships. Directed ties with affinity, trust, respect and familiarity, a Dunbar-style cap, friend and rival labels with hysteresis, and rebuff memory so someone turned down stops asking. features
  • Opinions, rumors, reputation and factions. Bounded-confidence influence, Daley–Kendall gossip, witnesses who judge deeds and hold grudges, in-group affinity and conferred standing. features
  • Scripts. Rhai triggers over a per-tick snapshot, bound to an area, a faction or named people, with a ctx reader and an fx writer that cannot see its own writes. scripting
  • An economy and a save file. Goods as quantities with a per-head price, sharing and swapping between people, and capture/restore of the whole society. features · persistence
  • An editor’s handles. fast_forward, preview_forward (run a day, report what moved, put it back), test_trigger, check_script, place_dependents and persons_in_rect. editor

Use it in the Cauldron

The Cauldron takes bevy_society as an optional cargo feature, named bevy_society in Cargo.toml and in a project’s family.ron. When the project lists it, the editor:

  • writes the crate’s own settings through family_set, which rewrites one dot-pathed field of the RON file the crate’s serde types parse — the editor holds no schema for them, so it keeps the rest of the file as the author left it;
  • refuses a definitions file the crate would reject: a saved society definition goes through bevy_society::SocietyDefs::from_ron first, so a file the loader would not compile is never written;
  • authors people on a map through person_set (<emerge_dir>/people/<map>.ron), with a name, a position, a faction the project’s definitions declare, a home, a workplace and starting ties;
  • runs a map’s people forward through people_run, which spawns them in bevy_society’s own headless app and reports where each went and which ties moved — the crate’s systems decide, the editor adds no behaviour.

A settings panel for this crate would edit SocietySettings (seed, tick_secs, seconds_per_day, start_hour, start_weekday, the clock source, locomotion, the society plane and the perception, contact and interaction radii), the tie capacities (max_ties, close_ties), the memory and knowledge capacities, and the rates on Tuning — every one of which is Serialize through the crate’s settings types. SocietyDefs itself is authored as a DefsFile in RON, and DefsFile::to_ron exists so an editor can save what it edited.

bevy_stigmergy

Fading marks on a grid that a crowd coordinates through, like ant trails. Agents deposit into shared channels that evaporate and diffuse between floor cells, never through a wall, and read them back by value or by gradient, so a crowd converges on a trail, scatters from a threat or rallies toward a moving target without any agent planning a route. The field is computed once per tick and shared by every agent.

Add it

cargo add bevy_stigmergy

The README has a quick start that is compiled on every cargo test. The crate depends only on bevy_math; it owns no resources, registers no systems and has no schedule, so it ticks wherever your game’s loop says.

What it gives you

  • StigGrid<N>: N scalar channels with deposit, evaporate_diffuse, sample_cell, gradient_cell and hotspot_cell (the field).
  • RallyGrid: a vector per cell, for recruiting toward a target that moves.
  • CellFrame: an optional world-to-cell mapping for an axis-aligned grid.
  • evaporate_diffuse_with: the same tick split into chunks for your own thread pool, bit-identical to the inline one (ticking).
  • serde and reflect features on the settings types.

Use it in the Cauldron

The Cauldron takes this crate behind its bevy_stigmergy feature, for the crowd preview (issue #107): it walks N people from one area of a map to another with bevy_orca’s avoidance, and each lays a trail and a congestion mark into a StigGrid laid over the map with a CellFrame. The result is a measurement, not an edit: the two fields drawn as an overlay, how many walkers arrived, and where congestion peaks, so you can see where a gap between buildings jams before a game ships it. The settings it uses are the crate’s own ChannelDefs, which are Serialize/Deserialize behind the serde feature.

bevy_orca

Walkers that steer around each other without colliding. ORCA (optimal reciprocal collision avoidance) gives each agent the velocity closest to the one it wanted that no neighbour can object to, assuming every neighbour reasons the same way and each takes half the dodge. It is a plain library on bevy_math’s Vec2, with no ECS, plugin or schedule: you call a function and get a velocity back.

Add it

cargo add bevy_orca

The README has a single-agent quick start that is compiled on every cargo test.

What it gives you

  • new_velocity for one agent, with a documented, finite answer for every unusable input and never a velocity over max_speed (one agent).
  • Crowd for many: a neighbour grid, a deterministic tie-break, a sideways nudge, and no allocation once warm (a whole crowd).
  • Right of way through priority, and Crowd::begin / CrowdStep::solve_range to split a step over your own thread pool with bit-identical results.
  • ObstaclesBuilder for buildings, walls, polygons and tile maps, as RVO2’s obstacle lines (static obstacles).
  • serde and reflect features on Agent, CrowdMember and CrowdParams.

Use it in the Cauldron

The Cauldron takes this crate behind its bevy_orca feature for the crowd preview (issue #107): it walks N people from one area of a map to another, each stepping with bevy_orca’s Crowd against its neighbours and against the cells the map’s pieces make solid, and lays their trails and congestion into a bevy_stigmergy field. The editor draws the result over the map with how many arrived and where congestion peaks, and saves nothing to the map. The settings it passes are the crate’s own CrowdParams, which are Serialize/Deserialize behind the serde feature.

bevy_speech_bubbles

A dialog kit for Bevy: world-space speech and thought balloons that float over their speakers, a conversation graph with a runtime, paced barks, and a skinnable screen dialog box. Balloons are quads in the scene rather than a UI layer, so they hold a world position, can be hidden behind a wall, and turn to face the camera.

Add it

cargo add bevy_speech_bubbles

The README has a quick start that is compiled on every cargo test. Your game names the camera balloons face with a marker component, and loads its own font.

What it gives you

  • Balloons (build_bubble, track_bubbles, BalloonSkin, BalloonScale), including pixel-perfect ones (balloons).
  • A DialogueScript of Line and Choice nodes, loaded from RON or any serde format, strict about unknown keys, and checked by validate_script (conversations).
  • A runtime driven by StartConversation, ChoicePicked and ConversationEnded, with DialogueNodeEntered for effects, and two presenters: balloons or the dialog box.
  • Barks with BarkSettings pacing, and a 9-sliced DialogSkin with portrait, name tag and typewriter.

Use it in the Cauldron

The Cauldron takes this crate behind its bevy_speech_bubbles feature and keeps a project’s dialogue as the crate’s own DialogueScript in family/bevy_speech_bubbles.ron, with barks as Vec<Bark>. family_set writes them and runs validate_script before anything is saved, so a dangling next or a missing start node is refused with nothing written. dialogue_preview walks one path through a conversation, taking the options you name at each choice, and answers with the lines said, where the walk stopped and which nodes no path reaches, without changing a file. People placed with bevy_society speak these lines.

bevy_gooey

Game interfaces made easy for Bevy 0.20: a skin, ready-made game widgets and keyboard and gamepad navigation, built on Bevy’s own headless widgets (bevy_ui_widgets) and input focus (bevy_input_focus). A menu is a few nested calls, every widget looks like one family, and a pad or a keyboard reaches everything a mouse does — with the WCAG 2.2 contrast and target-size checks the crate runs on your own skin.

Add it

cargo add bevy_gooey
use bevy::prelude::*;
use bevy_gooey::*;

fn main() {
    App::new()
        .add_plugins((DefaultPlugins, GooeyPlugins))
        .add_systems(Startup, setup)
        .run();
}

fn setup(mut commands: Commands) {
    commands.spawn(Camera2d);
    commands.spawn((
        screen(),
        children![(
            panel(),
            children![
                heading("Paused"),
                (
                    menu(),
                    children![
                        (button("Resume"), observe(|_: On<Activate>| info!("resume"))),
                        button("Quit"),
                    ]
                ),
            ]
        )],
    ));
}

Or run the gallery first: cargo run --example gallery.

What it gives you

  • A skin as data (skins): GooeySkin holds every colour and size, with the dark and parchment presets and problems(), which checks WCAG 2.2 contrast and target size. Change it and everything restyles that frame. Fonts live in GooeyFonts, never in the skin.
  • Every widget a game screen needs (the widget set): screen, panel, menu, heading, label, button, meter (a HUD bar), toggle, choice, slider, list, toast, toast_alert, Tooltip and the modal dialog — each a free function returning a bundle, so they compose in children![] and with observe.
  • Settings controls that are the widget (controls): a toggle, choice and slider each root on Bevy’s own Checkbox, Button and Slider, so the entity you spawn carries the value, takes observe and says each change once in ValueChange.
  • A list that scrolls and follows focus (lists): list(4.5, children![..]) shows four rows and a half, cuts the fifth so the player sees there is more, and keeps the next row in sight as the arrows walk down it.
  • Navigation with no mode to enter (focus): arrows and the D-pad move focus to the nearest widget, Enter, Space and South activate, Tab cycles, an open dialog holds focus, and the focus ring appears the moment a key is pressed.
  • Toasts that read at the player’s pace (toasts): commands.spawn(toast("Game saved")) and it stacks, times itself to its own length and ages on real time, so a paused game’s menu still lets it go.
  • One interface at every resolution (scale): GooeyScalePlugin keeps the skin’s sizes the same share of the screen, and every border is a whole number of device pixels at any scale.
  • A pointer that passes through a HUD (over a game world): the empty part of a screen is Pickable::IGNORE, so a click on the world under a HUD still reaches the world.

Use it in the Cauldron

The Cauldron, the Coven’s level editor, takes bevy_gooey as an optional cargo feature (bevy_gooey) and a project lists it in its family.ron. It adds the crate’s GooeyStylePlugin (not GooeyNavPlugin: the editor owns the arrow keys) and reads a project’s skin from <project>/family/bevy_gooey.ron, which is the crate’s own GooeySkin type written as RON — the same file a game ships:

(accent: Srgba((red: 0.2, green: 0.6, blue: 0.9, alpha: 1.0)), text_scale: 1.25)

GooeySkin is #[serde(default)], so a partial file is the default skin with only that field changed. Editing the file live swaps the running GooeySkin resource and the crate’s own apply_skin restyles every widget that frame; a file that is not a skin is a problem on the status line and the last good skin stays; a skin that breaks the crate’s own rules is applied and each broken rule (GooeySkin::problems()) is a problem. Removing the file puts the default skin back.

There is no panel for the skin yet. One would edit GooeySkin’s fields — the colours (background, surface, surface_raised, text, text_muted, accent, accent_text, danger, focus_ring, outline, scrim), the sizes (text_size, heading_size, gap, padding, radius, border, meter_height, min_target) and the player’s text_scale — with problems() listed beside them, and it would read UiScaleSettings { reference_short_side, scale } and ToastSettings { anchor, max_visible, base_seconds, chars_per_second, duration_scale, fade_seconds } the same way: each is serde data, so each is one line of the project’s family/ folder.

bevy_carnage

Runtime gore for Bevy characters, deterministic to the bit. Add a plugin, dress a loaded character with one component, and send a GoreHit per injury: a wound peels through skin, fat and muscle to bone, bleeds along the pose of an animated body, pools on the floor and dries, and a body that takes enough comes apart into watertight-capped chunks. A violence setting the player controls is honoured, and every canvas is owned by the CPU, so a whole fight can be hashed and replayed.

Add it

cargo add bevy_carnage

The README has an 18-line quick start that is compiled on every cargo test; the full quick start covers the flesh material, skinned characters, cost, the violence tiers and baking a fracture by hand. Putting it on a real glTF character has a checklist.

What it gives you

  • The preset: GorePlugin, Gore and GoreScene to dress surfaces, GoreHit for impacts, blows, burns and slashes, and GorePolicy for the player’s violence setting.
  • The fracture: bake once with FractureSubject, then query a hierarchy and a bond graph per blow, with two meshes per fragment and convex colliders (the API).
  • Seven kernels as modules: bloodstain, wetmap, viscera, cross_section, flaymap, laceration and fracture_modes (the family).
  • A recorded gallery of every demo.

Use it in the Cauldron

The Cauldron takes this crate behind its bevy_carnage feature for destructible props (issue #108). destructible_set marks a piece destructible and fractures it once with the crate’s own fracture_mesh over its mesh and a box proxy of its bounds, caching the chunks in the project (destructible/<piece>.bake.ron, keyed by the mesh’s bytes and the cut settings, so it never bakes twice). destructible_hit previews a hit on the cached chunks and reports which break loose and whether each is a closed solid, by the crate’s own audit. The violence dials are the crate’s own GorePolicy, saved as family/bevy_carnage.ron through family_set; the editor itself ships no gore.

bevy_fairy

bevy_fairy lets a coding agent look inside a running Bevy 0.20 game, drive it, and ask the person playing it for help — without touching the desktop. It is one crate with two halves: a Bevy plugin the game links, which registers bevy_fairy/* methods on the Bevy Remote Protocol (BRP, Bevy’s own JSON-RPC interface on port 15702), and an MCP server, bevy-fairy, that gives an agent tools to read the game’s state, run the game’s own commands, capture a PNG from the game’s own pixels and inject input into Bevy’s own messages. Nothing here captures the screen or synthesises an OS event: a capture from the window focused had 7,188 distinct colours on the game this was built for, and one flat colour with another app in front.

Add it

cargo add bevy_fairy --no-default-features --features plugin
use bevy::prelude::*;
use bevy::remote::http::RemoteHttpPlugin;
use bevy_fairy::DebuggerPlugin;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        // Owns the BRP RemotePlugin. Never add a second one next to it.
        .add_plugins(DebuggerPlugin::default())
        .add_plugins(RemoteHttpPlugin::default())
        .run();
}

Then cargo install bevy_fairy --no-default-features --features server and register bevy-fairy --stdio with the agent. The README’s Quick Start is the same two steps with the checks between them.

What it gives you

  • Read state, change it, undo it. brp calls any BRP method the game serves; observe answers a phrase query with component values and diffs it against the last read; experiment spawns, modifies and deletes entities and steps back through its own history — the tools, changing the game.
  • Watch one value over time. watch reports which fields moved and which stopped, each sample stamped with the game frame; burst returns a run of frames as one labelled contact sheet; bevy_fairy/ring records frames inside the game and freezes on a trigger — the tools.
  • Run the game’s own actions, and replay them. commands lists what the game registered with typed params and a worked example; run calls one; scene save writes the run to a file BEVY_FAIRY_SCENE replays at startup — commands, scenes.
  • See the screen. screenshot captures from an offscreen render target or from the window, with a crop and a zoom, and says which frame it was asked on and which it was delivered on — capture.
  • Press keys, type, click and drag. bevy_fairy/input writes into Bevy’s own input messages, so ButtonInput, text fields and bevy_ui picking all see it; a press, a cursor move and a release elsewhere is a drag — input.
  • Ask a person. guide posts a script the game draws one step at a time, and each step moves on when a checkpoint the game registered passes — the guide overlay.
  • Six worked sessions. A value that stops changing, an entity in the wrong place, a capture with state beside it, a player’s keystroke, a regression test that is a command, a reproduction walked through a person — workflows.

Use it in the Cauldron

The Cauldron takes this crate as an optional dependency, bevy_debugger_bevy (the package is bevy_fairy), pinned to a git revision of main with default-features = false, features = ["plugin", "plugin-guide", "plugin-screenshot"]: the guide overlay, capture, and the injected cursor, and none of the server. With the feature off, cargo tree -i bevy_debugger_bevy finds no package and neither does bevy_remote.

There is no settings panel for it, because there is nothing to author: the plugin has no file format of its own and no serde configuration. What it does in the editor is drive the editor.

  • DebuggerPlugin::default() is added in harness.rs, so the editor’s own window answers bevy_fairy/status, commands, input, screenshot, capture, guide and quit at 127.0.0.1:15702. That is how an agent looks at the Cauldron the way it looks at a game; it is on by default because the editor is not a build that ships to a player.
  • DebugCaptureTarget is inserted in surface.rs with the image the editor’s camera renders to, so a screenshot of the Cauldron is the editor’s own interface — panes, tabs and all — captured offscreen, with the window behind another app or on another Space.
  • DebugCommandsAppExt::add_debug_command in ops_brp.rs registers the editor’s own verbs, so an agent finds them with commands and runs them with run with typed params and an example, rather than driving the UI blind.
  • Checkpoints and Guide in guided.rs and chooser.rs are what a guided run uses: each step names a condition the editor itself evaluates, so a script the agent posts advances when the author actually got there. GuidePlacement is insert_resourced at top: 58.0 so the card sits below the title line rather than over the tab bar it describes.
  • cursor_position(&window, &DebugCursor) in view.rs is the one line an editor that reads the pointer itself has to write: the plugin injects a cursor instead of moving the real mouse, so a host reads through the plugin’s function rather than Window::cursor_position.

A setting that would fit a panel, and does not exist yet: a GuidePlacement and a “debugger on/off” toggle in the editor’s own settings file, so the port and the card’s position survive a restart.

Contributing

How issues, pull requests and docs work across the coven is in the org’s CONTRIBUTING.md, and every README and docs page follows the one documentation guide, DOCUMENTATION.md. A change that a user can see updates its crate’s README, its docs/ folder and its page in this book in the same pull request.