Solari and DLSS BSN Syntax Improvements Ready Event More Feathers Widgets Headless Tab Widgets WESL Shaders Mesh Shaders Sprite Materials 2D Extended Materials Sprite Render Backend Unification Pan Orbit Camera Weak System Ordering with chain_weak Contextual Theming Val::Em and Val::Rem Per-Column Change Ticks FixedNode Elliptical Border Radius Schedule Randomization Catching Panics Faster Bulk Despawning Better Texture Compression Bevy Error Context Messages InlineBox and InlineImage What's Next? Support Bevy Contributors Bevy 0.20 Posted on October 8, 2026 by Bevy Contributors Thanks to 227 contributors, 817 pull requests, community reviewers, and our generous donors, we're happy to announce the Bevy 0.20 release on crates.io! For those who don't know, Bevy is a refreshingly simple data-driven game engine built in Rust. You can check out our Quick Start Guide to try it today. It's free and open source forever! You can grab the full source code on GitHub. Check out Bevy Assets for a collection of community-developed plugins, games, and learning resources. To update an existing Bevy App or Plugin to Bevy 0.20, check out our 0.19 to 0.20 Migration Guide. Since our last release a few months ago we've added a ton of new features, bug fixes, and quality of life tweaks, but here are some of the highlights: Solari and DLSS: Solari, Bevy's realtime pathtraced renderer, is now faster, more accurate, supports more Bevy rendering features, and runs on macOS via Metal! BSN Syntax Improvements: BSN, Bevy's new scene system, had some syntax changes that made it much easier to read and compose Ready Event: BSN scene entities now trigger an observable Ready event when all of their children have been spawned. More UI Widgets: Bevy Feathers, Bevy's opinionated editor-centric UI toolkit, now has Color Input, Scrollable List View, Dropdown Selection, and Lazy Menu widgets. The Number Input widget is now scrubbable / draggable, and we've added Headless Tab Widgets. WESL Shaders: Bevy has officially adopted the WESL shader language (a standardized extension of WGSL). WESL is already an improvement over Bevy's old custom WGSL dialect, and we've been working with the WESL team to plan out the future of shader development in Bevy. Sprite Materials and Extended 2D Materials: It is now possible to create custom shader materials for Sprites, and 2D mesh materials can now be extended like they can in 3D. Pan Orbit Camera: Bevy now has a "pan orbit camera", making it possible to navigate scenes in a CAD-like way. Solari and DLSS # Solari, Bevy's realtime pathtraced renderer, has seen major improvements to pretty much every aspect of the plugin! Read JMS55's blog for the technical details, or continue reading below for the high level overview. Improved Image Quality # Thanks to improvements in our ReSTIR implementation, rendering is now mostly unbiased, leading to much more accurate lighting. Additionally, thanks to some other changes, moving objects no longer have shadows that lag behind, and reflections now look significantly less shimmery in motion, especially for non-metallic materials. Improved Performance # DLSS-RR has gotten very good in recent updates, and for many scenes, ReSTIR costs a decent chunk of performance, and does not significantly improve image quality. As a result, we've decided to make ReSTIR optional, and turn it off by default. If you were using Solari in Bevy 0.19, check if the loss of ReSTIR affects your scene, and if so re-enable SolariLighting::restir. With ReSTIR off, expect reduced shadow quality and missing shadows in motion in scenes with many lights. We are exploring cheaper ways of improving light sampling, without ReSTIR, to improve this in the future. In addition, Solari's scene management code is now retained (similar to retained render world optimizations in previous versions of Bevy), and overall much more optimized, leading to significantly reduced CPU costs. You may also want to take a look at the new fields in SolariLighting. While we aim to set reasonable defaults that will work well across a wide variety of games, there are now many knobs (world cache size, per-pixel light sample count, temporal accumulation, and path tracing bounce count) that can be tweaked to improve performance or quality for your specific scene, project and hardware. Improved Compatibility # Solari now supports lighting from Atmosphere and EnvironmentMapLights on cameras, in addition to the existing support for DirectionalLight and emissive meshes. We're hoping to add support for the remaining PointLight, SpotLight, and RectLight types in the near future. Solari now also runs on macOS, but note that there is currently no built-in denoiser included in bevy_solari for macOS. MetalFX Ray Reconstruction might be a possible solution in the future (contributions welcome!) DLSS Updates # Finally, our dlss_wgpu crate has been updated to support the latest version of DLSS, bringing support for DLSS-RR 4.5, which significantly improves denoising quality in Solari. If you were using DLSS in Bevy 0.19, make sure to download and setup the newest version of the DLSS SDK, else you will run into compiler errors. BSN Syntax Improvements # BSN landed with a few idiosyncrasies that caused friction in practice. We made some changes to BSN's syntax this cycle in the interest of improving its ergonomics and clarity. After this, the syntax should largely be nailed down. Explicit scene syntax # All scene references now require @ prefixes: // Before bsn! { scene_variable scene_function() @SceneComponent {scene_expression} } // After bsn! { @scene_variable @scene_function() @SceneComponent @{scene_expression} } In addition to making it easier to spot scene inclusions (and unifying the syntax across cases), this freed us up to make component values much easier to work with! No more template_value wrappers! # You can now remove all of those pesky template_value wrappers from your component values: // Before bsn! { template_value(component_variable) template_value(component_function()) } // After bsn! { component_variable component_function() } Enums "just work" # Enums no longer require VariantDefaults or FromTemplate, provided they implement Default and Clone: // Before #[derive(Component, Default, Clone, VariantDefaults)] enum Foo { A { x: u32, y: u32 }, #[default] B, } bsn! { Foo::B } // After #[derive(Component, Default, Clone)] enum Foo { A { x: u32, y: u32 }, #[default] B, } bsn! { Foo::B } If you were using an enum that didn't support VariantDefaults, you can now remove the template_value wrapper: // Before bsn! { template_value(Foo::A) } // After bsn! { Foo::A } The removal of VariantDefaults does mean that enums must now have every field specified: // Before (y field is initialized to its default value) bsn! { Foo::A { x: 1 } } // After (y field must be manually specified) bsn! { Foo::A { x: 1, y: 0 } } We believe this tradeoff is worth it, as it increases BSN's compatibility with arbitrary Rust enums. Rust doesn't support "enum variant defaults" anyway! Chained method support # The "builder pattern" (and chained methods generally) previously required a template_value wrapper. This can now be removed: // Before bsn! { template_value(Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y)) } // After bsn! { Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y) } Additionally, you can now remove the template_value wrapper in cases like this: // Before bsn! { template_value(node.clone()) } // After bsn! { node.clone() } In general, you should now be able to remove all template_value instances from your BSN declarations! Improved list syntax # BSN previously used commas to separate entities, with optional () around entities to make the boundaries clearer. This resulted in a lot of syntax noise, line noise, and over-indentation: bsn! { Node Children [ ( #OkButton @button("Ok") ), ( #CancelButton @button("Cancel") ), ] } To avoid this, many developers opted for this syntax instead, which made it very hard to visually distinguish entities: bsn! { Node Children [ #OkButton @button("Ok"), #CancelButton @button("Cancel"), ] } BSN now uses -- to separate entities in a list: bsn! { Node Children [ #OkButton @button("Ok") -- #CancelButton @button("Cancel") ] } This gives us the best of all worlds: entities are visually distinct, and there is no over-indentation, line noise, or syntax noise (the stats when compared to other competitors in the "markup format" space are very competitive!). Both () and , have been deprecated in this context. Using [] and () for bsn_list! (and bsn!) is now discouraged / warned against (ex: bsn_list! []), as it can result in poor rustfmt autoformatting. Instead, use bsn_list! {}, which is the only syntax that rustfmt won't touch. Don't worry, we plan to build a BSN auto-formatter! bsn_list! { #Ok @button("Ok") -- #Cancel @button("Cancel") } Ready Event # We landed BSN, Bevy's next generation scene system, in our last release. It was missing a key piece though: the ability to easily run logic when a scene is fully "ready" and spawned (ex: all dependencies have loaded, the full hierarchy is present, and all of the initial components are inserted in the scene). This is a critical piece for building cohesive, standalone, composable scenes. It is also necessary to properly layer Bevy logic on top of other scene representations (like glTF). The closest we had was the Add event for a given component, which runs "top down" (meaning children are not available). We needed a "bottom up" equivalent to enable building logic that relies on the complete loaded and spawned scene. The solution is pretty straightforward: trigger a new Ready event for each entity in a spawned scene after the full spawn logic has run for that entity (including its descendants). This enables the following: #[derive(SceneComponent, Default, Clone)] struct Widget; impl Widget { fn scene() -> impl Scene { bsn! { Node { width: px(100), height: px(100) } on(|ready: On
| { info!("The full scene, including 'widget.bsn' contents, is available here") }) Children [ Text("hello") -- :"widget.bsn" ] } } } world.spawn(bsn! { @Widget }) More Feathers Widgets # Feathers, Bevy's opinionated editor-centric UI toolkit, now has more widgets for you to play with: Color Input # Bevy now has a compact color input selector that displays a color picker widget popup when clicked. This includes a color wheel selector, RGB, and HSL selectors, and a recently used colors grid. List View / Scrollbar # A scrollable, selectable list view. Dropdown Selection # A selection field that when clicked, displays a dropdown containing a list of options to select. Lazy Menu # Spawns a menu popup when the menu is opened and despawns it when it is closed. This is in contrast to the normal Menu widget, which just hides the menu. Number Input Widget Scrubbing / Dragging # The FeathersNumberInput widget has been expanded to support both normal text input and scrubbing / dragging. There is a configurable "hard limit" (minimum and maximum value via any input method) and "soft limit" (minimum and maximum value via dragging), in addition to control over floating point precision and step sizes. Headless Tab Widgets # bevy_ui_widgets now has headless (bring-your-own-visuals) tab behavior: a TabList container and Tab headers. Selection is managed "externally". SelectedTab on the list holds the selected tab; interaction emits ValueChange