You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Should Bevy's editor be a thin host over a WIT extension boundary?
This post is trying to do one specific thing: argue that the direction in the Vision and Architecture docs has a more thorough version worth someone qualified investigating. I'm not proposing a design. I'm raising the question of whether someone should design this. Governance, reflection internals, UI contribution, and performance characteristics are real questions, but I'm not the person to answer them. That's the work I'm hoping to make visible, not work I'm claiming to have done.
With that scoping clear, the claim:
Bevy's editor should be a thin host over a WIT extension boundary, so the existing crate ecosystem can project itself into editor form rather than the editor becoming a parallel ecosystem of its own.
Cargo came first
This is the part I think matters most.
Other game engines built their editor and their ecosystem together. The editor came first or alongside the engine, and the plugin ecosystem grew out of it, with extensions filling gaps in what the editor offered. The editor is the foundation; the ecosystem is downstream.
Bevy is in the opposite situation. Bevy already has a deep, mature ecosystem with hundreds of crates, expressed as pure code, distributed through cargo. That ecosystem grew without an editor and doesn't need one to function. Any editor arriving now is arriving into something that already exists.
This inverts the usual relationship. The architecture doc partially recognizes it: "self-contained Plugins reusable without the editor binary" gestures at the inversion. But there's a more thorough version. The editor isn't just a thing that uses plugins; the editor is downstream of the ecosystem. It's a window into cargo and crates.io, not a parallel source of tools.
A thin host with a WIT extension boundary is what makes that directionality real. Crates implement against the host's WIT contract, the host stays small, and the editor remains derivative of the ecosystem rather than competing with it. Component-defining crates ship companion extensions providing editor UI for their own types: Rapier knows about RigidBody, the host doesn't need to.
Three open questions, one mechanism
The architecture doc names three open questions:
how do we allow users to add and remove non-trivial functionality to the editor?
do users need to have a local working copy of the Rust compiler to use the editor effectively?
how does the Bevy editor communicate with the Bevy game to enable effective scene editing with user-defined types?
A WIT boundary answers these together. Adding or removing functionality becomes loading or unloading a component, no fork required. The Rust toolchain becomes optional, since extensions can be authored in any language that targets WIT. User-defined types become a question of what's in the reflection interface, not whether the host has heard of them.
I think this is the strongest single argument for the approach: not that any one of these problems demands WIT, but that one mechanism dissolves three of them at once.
The parallel-editors problem is a symptom, not a coincidence
Jackdaw and bevy_editor_prototypes (now archived but it'll be my example) both exist, both are structured as crates with extension APIs, and neither is monolithic. What they can't currently do is share extension infrastructure without coupling to each other's Rust types. If you write an inspector panel for Jackdaw, you can't drop it into bevy_editor_prototypes; the trait it implements lives in Jackdaw's crate.
This isn't anyone's wrong decision. It's a consequence of native-trait extension, which is the only path currently available.
I think the bigger point is that the current state of editor work in Bevy, multiple parallel efforts rather than shared infrastructure, isn't a coincidence. It's what happens when the smallest viable unit of contribution is a whole editor. Everywhere else in the Bevy ecosystem, "going ahead with what interests you and what your workflow requires" means writing the crate you need and publishing it. People scratch their itches at the granularity of crates, and the ecosystem composes. Editor work doesn't follow that pattern because there's no extension surface to publish into. The unit of useful contribution is much larger than it should be, so contributors who want to ship something end up shipping editors.
A WIT boundary would make editor contributions look like the rest of the ecosystem: scoped, composable, distributable as units smaller than a whole application. The proposal isn't really "Bevy should have one editor instead of many." It's "the unit of editor contribution should be small enough that the question of one-versus-many stops being interesting." If Jackdaw and bevy_editor_prototypes implemented the same WIT, the question of which one wins matters less, because the contributions stack up against the same target either way.
Why not native dynamic plugins?
Bevy already tried that path. bevy_dynamic_plugin was deprecated in 0.14 and removed in 0.15. The reasons were concrete:
TypeId isn't stable across compilation units
Rust has no stable ABI
VTable layouts aren't guaranteed
Loading foreign code runs ctor unconditionally
The reasons it was removed are roughly the reasons WIT works. WIT provides a stable, versioned, language-agnostic ABI by construction, and that's also what enables the toolchain-independence property.
The hard part: reflection across the boundary
An editor's whole job is reflecting and manipulating ECS state. If the reflection interface across the boundary is awkward, chatty, or forces a round-trip per field, everything else collapses.
This is the question I most want someone qualified to look at. The encouraging thing is that Bevy already has bevy_reflect. A WIT reflection interface wouldn't have to invent reflection from scratch; it could mirror a model that's already battle-tested inside the engine. But how it should mirror it (what gets pushed across the boundary eagerly versus lazily, how change detection works, how mutations batch, what queries look like) is real design work, and it's the part I can't do.
If someone with deep bevy_reflect context sees a fundamental obstacle, the rest of the proposal doesn't survive it.
The other hard part: how extensions contribute UI
A gap I want to flag explicitly, because I didn't address it earlier and should have: editor extensions are mostly UI. An inspector panel, a gizmo, an asset browser tab, a command palette entry: these are all widget trees, and the host needs some way to know what an extension wants to draw. If extensions can't contribute UI cleanly, the host has to ship every widget itself, which collapses back into the editor-as-monolith model the proposal is trying to avoid.
I don't know the right answer here, but the question has a few plausible shapes worth investigating:
Schema in component metadata. WASM components can carry arbitrary custom sections, so an extension could declare its widgets statically: "I provide an inspector for type path X, with this layout." The host reads metadata without instantiating the component, registers widgets up front, and only calls into the component for behavior. Cheapest option, maps well to extensions that mostly project component fields onto standard widgets, but rigid.
Widget tree returned across WIT. The component exposes a function returning a tree of widget descriptions, which the host renders. More flexible than static schemas because the tree can depend on runtime state, but every redraw potentially crosses the boundary.
Immediate-mode drawing via host primitives. The host exposes drawing primitives over WIT, the component calls them. Maximum flexibility, maximum chattiness, probably wrong for the same reasons chatty reflection would be wrong.
Hybrid. Declarative schemas for registration and structure, function calls only for behavior and dynamic content. Roughly what Zed and Lapce do, and probably the direction worth starting from.
I'd put this as the second most load-bearing technical question after reflection. The two interact: a declarative widget schema that references reflected component fields needs both interfaces to agree on how types are addressed. They probably want to be designed together.
Performance
A WASM boundary adds overhead. Hot paths stay native: engine, renderer, ECS itself never cross. What crosses are editor operations bounded by interaction rather than frame budget. "Interaction-bounded" isn't a free pass, though: selecting thousands of entities and dragging them is a real workload, and chatty reflection or chatty redraws would hurt there. This is downstream of the two questions above.
Prior art
[Wasvy](https://github.com/wasvy-org/wasvy) bridges Bevy ECS and the Component Model with working WIT for register-component, query-result, and guest systems. Runtime ECS-as-components is a different problem from editor reflection, but it's encouraging that the basic plumbing works.
Zed and Lapce both ship WASM-component extension systems in production, suggesting developer ergonomics are at least livable.
The WASM component ecosystem has standardized on OCI registries for distribution, so a plugin marketplace wouldn't need designing from scratch.
This isn't a new conversation in Bevy either. Multi-editor questions came up in [#7100](#7100) and [#5043](#5043), and Cart suggested WASM plugin support in [#1103](#1103) before the Component Model existed. I'm trying to connect those older threads to current tooling.
Constraint check
The architecture doc forbids editor dependencies on crates that themselves depend on Bevy. wasmtime and wit-bindgen don't, so this passes. This is also orthogonal to "the editor doesn't inspect the running game": WIT here is for host/extension communication, not editor/game communication.
What I'm not the person to figure out
In rough order of how much they'd determine whether this works:
Reflection design across the boundary. The whole proposal stands or falls here.
UI contribution. How extensions describe and draw widgets, and how that interacts with reflection.
Governance. Who owns the WIT, how it versions, what the change process is, how deprecation works, who keeps the host's interface from sprawling under feature pressure. This is a commitment to running a small standards process indefinitely, not a one-time technical decision.
Whether the cargo-came-first framing is actually right. Maybe Unity and Unreal users would say their ecosystems are more independent of the editor than I'm crediting. I don't have deep history in either; this is the claim I'd most like someone with that background to push on.
Whether native traits with careful versioning could get most of the way there without WIT's costs. I lean WIT because of the bevy_dynamic_plugin lessons and toolchain-independence, but reasonable people could disagree.
Tooling ergonomics for WIT. Lagging native Rust today; trajectory matters.
If the cargo-came-first framing is right, reflection is tractable, and UI contribution has a clean answer, I think the rest is engineering. If any of those three fails, none of this works. I'd rather put the question up for argument than sit on it.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
-
Should Bevy's editor be a thin host over a WIT extension boundary?
This post is trying to do one specific thing: argue that the direction in the Vision and Architecture docs has a more thorough version worth someone qualified investigating. I'm not proposing a design. I'm raising the question of whether someone should design this. Governance, reflection internals, UI contribution, and performance characteristics are real questions, but I'm not the person to answer them. That's the work I'm hoping to make visible, not work I'm claiming to have done.
With that scoping clear, the claim:
Bevy's editor should be a thin host over a WIT extension boundary, so the existing crate ecosystem can project itself into editor form rather than the editor becoming a parallel ecosystem of its own.
Cargo came first
This is the part I think matters most.
Other game engines built their editor and their ecosystem together. The editor came first or alongside the engine, and the plugin ecosystem grew out of it, with extensions filling gaps in what the editor offered. The editor is the foundation; the ecosystem is downstream.
Bevy is in the opposite situation. Bevy already has a deep, mature ecosystem with hundreds of crates, expressed as pure code, distributed through cargo. That ecosystem grew without an editor and doesn't need one to function. Any editor arriving now is arriving into something that already exists.
This inverts the usual relationship. The architecture doc partially recognizes it: "self-contained
Plugins reusable without the editor binary" gestures at the inversion. But there's a more thorough version. The editor isn't just a thing that uses plugins; the editor is downstream of the ecosystem. It's a window into cargo and crates.io, not a parallel source of tools.A thin host with a WIT extension boundary is what makes that directionality real. Crates implement against the host's WIT contract, the host stays small, and the editor remains derivative of the ecosystem rather than competing with it. Component-defining crates ship companion extensions providing editor UI for their own types: Rapier knows about
RigidBody, the host doesn't need to.Three open questions, one mechanism
The architecture doc names three open questions:
A WIT boundary answers these together. Adding or removing functionality becomes loading or unloading a component, no fork required. The Rust toolchain becomes optional, since extensions can be authored in any language that targets WIT. User-defined types become a question of what's in the reflection interface, not whether the host has heard of them.
I think this is the strongest single argument for the approach: not that any one of these problems demands WIT, but that one mechanism dissolves three of them at once.
The parallel-editors problem is a symptom, not a coincidence
Jackdaw and bevy_editor_prototypes (now archived but it'll be my example) both exist, both are structured as crates with extension APIs, and neither is monolithic. What they can't currently do is share extension infrastructure without coupling to each other's Rust types. If you write an inspector panel for Jackdaw, you can't drop it into bevy_editor_prototypes; the trait it implements lives in Jackdaw's crate.
This isn't anyone's wrong decision. It's a consequence of native-trait extension, which is the only path currently available.
I think the bigger point is that the current state of editor work in Bevy, multiple parallel efforts rather than shared infrastructure, isn't a coincidence. It's what happens when the smallest viable unit of contribution is a whole editor. Everywhere else in the Bevy ecosystem, "going ahead with what interests you and what your workflow requires" means writing the crate you need and publishing it. People scratch their itches at the granularity of crates, and the ecosystem composes. Editor work doesn't follow that pattern because there's no extension surface to publish into. The unit of useful contribution is much larger than it should be, so contributors who want to ship something end up shipping editors.
A WIT boundary would make editor contributions look like the rest of the ecosystem: scoped, composable, distributable as units smaller than a whole application. The proposal isn't really "Bevy should have one editor instead of many." It's "the unit of editor contribution should be small enough that the question of one-versus-many stops being interesting." If Jackdaw and bevy_editor_prototypes implemented the same WIT, the question of which one wins matters less, because the contributions stack up against the same target either way.
Why not native dynamic plugins?
Bevy already tried that path.
bevy_dynamic_pluginwas deprecated in 0.14 and removed in 0.15. The reasons were concrete:TypeIdisn't stable across compilation unitsctorunconditionallyThe reasons it was removed are roughly the reasons WIT works. WIT provides a stable, versioned, language-agnostic ABI by construction, and that's also what enables the toolchain-independence property.
The hard part: reflection across the boundary
An editor's whole job is reflecting and manipulating ECS state. If the reflection interface across the boundary is awkward, chatty, or forces a round-trip per field, everything else collapses.
This is the question I most want someone qualified to look at. The encouraging thing is that Bevy already has
bevy_reflect. A WIT reflection interface wouldn't have to invent reflection from scratch; it could mirror a model that's already battle-tested inside the engine. But how it should mirror it (what gets pushed across the boundary eagerly versus lazily, how change detection works, how mutations batch, what queries look like) is real design work, and it's the part I can't do.If someone with deep
bevy_reflectcontext sees a fundamental obstacle, the rest of the proposal doesn't survive it.The other hard part: how extensions contribute UI
A gap I want to flag explicitly, because I didn't address it earlier and should have: editor extensions are mostly UI. An inspector panel, a gizmo, an asset browser tab, a command palette entry: these are all widget trees, and the host needs some way to know what an extension wants to draw. If extensions can't contribute UI cleanly, the host has to ship every widget itself, which collapses back into the editor-as-monolith model the proposal is trying to avoid.
I don't know the right answer here, but the question has a few plausible shapes worth investigating:
Schema in component metadata. WASM components can carry arbitrary custom sections, so an extension could declare its widgets statically: "I provide an inspector for type path X, with this layout." The host reads metadata without instantiating the component, registers widgets up front, and only calls into the component for behavior. Cheapest option, maps well to extensions that mostly project component fields onto standard widgets, but rigid.
Widget tree returned across WIT. The component exposes a function returning a tree of widget descriptions, which the host renders. More flexible than static schemas because the tree can depend on runtime state, but every redraw potentially crosses the boundary.
Immediate-mode drawing via host primitives. The host exposes drawing primitives over WIT, the component calls them. Maximum flexibility, maximum chattiness, probably wrong for the same reasons chatty reflection would be wrong.
Hybrid. Declarative schemas for registration and structure, function calls only for behavior and dynamic content. Roughly what Zed and Lapce do, and probably the direction worth starting from.
I'd put this as the second most load-bearing technical question after reflection. The two interact: a declarative widget schema that references reflected component fields needs both interfaces to agree on how types are addressed. They probably want to be designed together.
Performance
A WASM boundary adds overhead. Hot paths stay native: engine, renderer, ECS itself never cross. What crosses are editor operations bounded by interaction rather than frame budget. "Interaction-bounded" isn't a free pass, though: selecting thousands of entities and dragging them is a real workload, and chatty reflection or chatty redraws would hurt there. This is downstream of the two questions above.
Prior art
register-component,query-result, and guest systems. Runtime ECS-as-components is a different problem from editor reflection, but it's encouraging that the basic plumbing works.This isn't a new conversation in Bevy either. Multi-editor questions came up in [#7100](#7100) and [#5043](#5043), and Cart suggested WASM plugin support in [#1103](#1103) before the Component Model existed. I'm trying to connect those older threads to current tooling.
Constraint check
The architecture doc forbids editor dependencies on crates that themselves depend on Bevy.
wasmtimeandwit-bindgendon't, so this passes. This is also orthogonal to "the editor doesn't inspect the running game": WIT here is for host/extension communication, not editor/game communication.What I'm not the person to figure out
In rough order of how much they'd determine whether this works:
bevy_dynamic_pluginlessons and toolchain-independence, but reasonable people could disagree.If the cargo-came-first framing is right, reflection is tractable, and UI contribution has a clean answer, I think the rest is engineering. If any of those three fails, none of this works. I'd rather put the question up for argument than sit on it.
Beta Was this translation helpful? Give feedback.
All reactions