bevy-vfx
Skill29 starsChanged 11 days agoMITView on GitHub
Use when adding GPU particles via `bevy_hanabi 0.20.0` (`HanabiPlugin`, `EffectAsset`, `ParticleEffect`, `ExprWriter`, `SpawnerSettings`, modifiers like `SetPositionSphereModifier` / `ColorOverLifetimeModifier`), rendering Gaussian splats via `bevy_spark`, picking between hanabi and `bevy_spritesheet_animation` flipbooks or `bevy_vector_shapes`, writing custom `Material` + `.wesl` shaders for fire/water/distortion, or budgeting GPU costs and WebGPU compatibility in Bevy 0.20.
In this repository34 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- .github/copilot-instructions.mdCopilot instructions
- bevy-a11ySkill
- bevy-animationSkill
- bevy-assetsSkill
- bevy-audioSkill
- bevy-camerasSkill
- bevy-captureSkill
- bevy-cargo-featuresSkill
- bevy-core-conceptsSkill
- bevy-custom-assetsSkill
- bevy-diagnostics-profilingSkill
- bevy-ecs-componentsSkill
- bevy-ecs-queriesSkill
- bevy-ecs-systemsSkill
- bevy-fluentSkill
- bevy-input-actionsSkill
- bevy-migration-0-17-to-0-18Skill
- bevy-migration-0-18-to-0-19Skill
- bevy-migration-0-19-to-0-20Skill
- bevy-pbr-materialsSkill
- bevy-physicsSkill
- bevy-portingSkill
- bevy-renderingSkill
- bevy-save-loadSkill
- bevySkill
- bevy-testingSkill
- bevy-uiSkill
- bevy-voxel-dataSkill
- bevy-voxel-pipelineSkill
- bevy-voxel-runtimeSkill
- bevy-wasm-webgpuSkill
- similarity-rsSkill
The file
We split this skill into the rules it gives an agent
1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 166
ExprWriter::lit(v)returnsWriterExpr: Call .expr() for the ExprHandle modifiers take.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Bevy 0.20 — VFX (particles, shaders, Gaussian splats)
Compatibility status (checked on crates.io, 2026-11-03)
| Crate | Version | Pins Bevy | Status |
bevy_hanabi | 0.20.0 (2026-11-01) | ^0.20 | Use it. WESL shaders, batched instances. |
bevy_spritesheet_animation | 8.0.1 | ^0.20 | Use it. |
bevy_vector_shapes | 0.14.0 | ^0.20 | Use it. |
bevy_spark | 0.2.0 (2026-05-04) | ^0.18 | No 0.19/0.20 release. Snippets stay pinned to 0.18. |
bevy_prototype_lyon | 0.17.0 | ^0.19 | Lags one release. Prefer bevy_vector_shapes. |
bevy_hanabi needs compute shaders: WebGPU only on WASM, no WebGL2. bevy_spark has a
webgl2 feature. Custom Material shaders and flipbooks work on both targets.
When to use this skill
- Spawning a GPU particle effect via
bevy_hanabi— emitters, modifiers, gradients. - Picking a modifier for a behaviour (radial burst, colour-over-lifetime, drag).
- Choosing between hanabi,
bevy_spritesheet_animationflipbooks, or custom-material shaders. - Loading and rendering a
.spzGaussian-splat scene viabevy_spark(0.18 only for now). - Debugging "my effect doesn't show in WASM" — usually the WebGPU vs WebGL2 gotcha.
- Hanabi custom shader fails to load after 0.20 — naga_oil
#importmust become WESL.
Canonical end-to-end pattern
API verified against the bevy_hanabi 0.20.0 source. Not compiled in the tester crate
(third-party dependency); every symbol below exists in that release.
use bevy::prelude::*;
// Do NOT use `bevy_hanabi::prelude::*` — `Gradient` name-collides with
// `bevy::prelude::Gradient` (bevy_ui's CSS gradient). Import explicitly:
use bevy_hanabi::prelude::{
AccelModifier, Attribute, ColorOverLifetimeModifier, EffectAsset, ExprWriter, HanabiPlugin,
ParticleEffect, SetAttributeModifier, SetPositionSphereModifier, SetVelocitySphereModifier,
ShapeDimension, SpawnerSettings,
};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(HanabiPlugin)
.add_systems(Startup, setup)
.run();
}
fn setup(mut commands: Commands, mut effects: ResMut<Assets<EffectAsset>>) {
commands.spawn((
Camera3d::default(),
Transform::from_xyz(0.0, 3.0, 20.0).looking_at(Vec3::ZERO, Vec3::Y),
));
// ExprWriter accumulates shader expression nodes into a Module.
// lit() returns WriterExpr — call .expr() to get the ExprHandle modifiers need.
let writer = ExprWriter::new();
// Init: scatter particles across a sphere's volume + push outward.
let init_pos = SetPositionSphereModifier {
center: writer.lit(Vec3::ZERO).expr(),
radius: writer.lit(1.0_f32).expr(),
dimension: ShapeDimension::Volume,
};
let init_vel = SetVelocitySphereModifier {
center: writer.lit(Vec3::ZERO).expr(),
speed: writer.lit(4.0_f32).expr(),
};
// Init: LIFETIME recycles particles; AGE is read by ColorOverLifetimeModifier.
let init_lifetime = SetAttributeModifier::new(
Attribute::LIFETIME,
writer.lit(0.5_f32).uniform(writer.lit(1.5_f32)).expr(),
);
let init_age = SetAttributeModifier::new(Attribute::AGE, writer.lit(0.0_f32).expr());
// Update: constant downward acceleration.
let update_gravity = AccelModifier::new(writer.lit(Vec3::new(0.0, -6.0, 0.0)).expr());
// Render: red → orange → transparent over lifetime.
let mut color: bevy_hanabi::Gradient<Vec4> = bevy_hanabi::Gradient::new();
color.add_key(0.0, Vec4::new(4.0, 0.5, 0.0, 1.0));
color.add_key(0.5, Vec4::new(2.0, 1.0, 0.0, 0.8));
color.add_key(1.0, Vec4::new(0.5, 0.5, 0.5, 0.0));
let render_color = ColorOverLifetimeModifier::new(color);
// Spawner: 200 particles/sec. There is no `Spawner` struct; use SpawnerSettings.
let spawner = SpawnerSettings::rate(200.0_f32.into());
// finish() consumes the writer, producing the Module passed to EffectAsset::new.
let module = writer.finish();
let effect = EffectAsset::new(16384, spawner, module)
.with_name("ember_burst")
.init(init_pos)
.init(init_vel)
.init(init_lifetime)
.init(init_age)
.update(update_gravity)
.render(render_color);
let handle = effects.add(effect);
// ParticleEffect is a bare component; #[require(...)] adds CompiledParticleEffect etc.
commands.spawn((
Name::new("ember_burst"),
ParticleEffect::new(handle),
Transform::default(),
Visibility::default(),
));
}Topics
| Topic | Reference |
EffectAsset, Module / ExprWriter, SpawnerSettings, the bare-component spawn pattern | references/hanabi-anatomy.md |
| Modifier catalog grouped by context (Init / Update / Render), 0.20 additions | references/hanabi-modifiers.md |
2D sprite particles vs 3D billboards, OrientModifier, FlipbookModifier, texture slots | references/hanabi-2d-vs-3d.md |
| When NOT to use hanabi — CPU particles, flipbooks, vector shapes, trails, decals | references/non-hanabi-vfx.md |
Custom Material + WESL effects (fire / water / distortion) | references/shader-effects.md |
bevy_spark Gaussian-splat rendering (Bevy 0.18 pin) | references/gaussian-splats.md |
| GPU budgets, WASM/WebGPU caveats, profiling, mixing techniques | references/performance.md |
Picking the right VFX technique
| Need | Reach for |
| Thousands of small moving particles | bevy_hanabi → hanabi-anatomy.md |
| One localised animated effect (single fire, water surface) | Custom Material + WESL → shader-effects.md |
| Canned explosion / hit-spark, identical every time | Flipbook via bevy_spritesheet_animation → non-hanabi-vfx.md |
| Tiny counts (<100) or WebGL2 | CPU particles → non-hanabi-vfx.md |
| Photoreal scene capture | bevy_spark (0.18 only) → gaussian-splats.md |
| Stylised geometric SFX (rings, lightning) | bevy_vector_shapes → non-hanabi-vfx.md |
Gotchas
0.19 → 0.20
- Hanabi shaders are WESL. Custom shader code using naga_oil
#import/#ifdefmust becomeimport ...;/@if(...). The common module is nowbevy_hanabi::render::vfx_common(wasbevy_hanabi::vfx_common). Same rule for your ownMaterialshaders — seebevy-pbr-materials. ParticleTextureModifier::new(slot: u32)takes a slot index fixed at module build time. Passing an expression (writer.lit(...)) is the stale 0.18 shape.TextureSampleExpr::imageis nowslot_index.- Same-effect instances batch into one compute dispatch. Reuse one
EffectAssetfor many emitters. - New in 0.20:
HanabiMainCamera(tag exactly one camera),KillFrustumModifier,SetPositionBoxModifier,TextureLoadExpr, 1D/3D/array/cube texture slots. Spritenow renders throughMesh2d+SpriteMaterialand has analpha_modefield. CPU sprite particles can pickOpaque/Maskto skip the transparent pass.- Bevy's
RenderGraphnodes are gone (0.19). Custom passes are systems inCore2d/Core3d.
0.18 → 0.19 (hanabi 0.19)
EffectAsset::meshisOption<AssetPath>, not a handle. Per-instance mesh override is theEffectMeshcomponent.serde/typetagfeatures removed; serialization is always on viaEffectAsset::serialize.
Still true
SpawnerSettings::rate(...), notSpawner::rate.Spawnerdoes not exist.ParticleEffectis a bare component;ParticleEffectBundleis gone.ExprWriter::lit(v)returnsWriterExpr. Call.expr()for theExprHandlemodifiers take.ColorOverLifetimeModifierneedsAttribute::AGEinitialised alongsideLIFETIME.bevy_hanabi::Gradientcollides withbevy::prelude::Gradient. Import hanabi items explicitly.bevy_sparkloads.spzonly, colour is baked (no Bevy lighting), and it has not moved past Bevy 0.18.- No 0.20-ready trail or decal crate. Hand-roll or use
bevy_vector_shapes.
See also
bevy-pbr-materials— customMaterial+ WESL shader path and the naga_oil → WESL table.bevy-cameras— camera framing;HanabiMainCameratags the camera hanabi culls against.bevy-wasm-webgpu— hanabi needs WebGPU;bevy_sparkhas awebgl2feature.bevy-animation—EasingCurve/EaseFunctionfor procedural FX without particles.bevy-migration-0-19-to-0-20— WESL shader translation.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

