How it fits together
Three layers, and one rule that keeps them apart.
your game Dart · Flutter widgets, game logic, interface │ ├── orblit_core C++ · archetype entity-component store, over a C ABI │ Dart sees component data as views, not copies │ └── orblit_filament C++ · Filament, into a texture Flutter composites. On Apple platforms, an IOSurface-backed pixel buffer adopted without a readbackThe rule: the core takes no Flutter dependency
Section titled “The rule: the core takes no Flutter dependency”orblit_core, and everything it exposes, knows nothing about Flutter.
That isn’t tidiness. It’s what keeps a second front end possible. Dart on Flutter is one way to reach the core, TypeScript on QuickJS is another, and a native front end on SDL3 would be a third. If the core depended on Flutter, each of those would have to be a second engine rather than a second front end, and the three would drift apart inside a year.
The practical version of the rule, if you’re writing engine code: a package
that might be used by a game which isn’t a Flutter application must not import
package:flutter. Simulation, geometry, rigging, noise, agents, collision,
sequencing and networking all stick to it, which is why they run on plain Dart
and can be tested on a Linux CI runner with no display attached.
Why component data is views and not copies
Section titled “Why component data is views and not copies”The core stores entities by archetype: everything with the same set of components sits together, so a system that wants every transform gets a contiguous run of them.
If Dart received copies, every frame would pay to marshal that run across the ABI and marshal the results back again. Instead, Dart receives a view: typed data backed by the store’s own memory. Reading a column is a read. Writing to one writes to the store.
There is a real cost to that bargain. A view is only valid while the store’s layout is unchanged, and adding or removing a component can move an archetype’s storage, at which point a view held across it is a view of the wrong thing. The engine’s rule is that views never outlive the system that asked for them.
Why the renderer is a texture and not a platform view
Section titled “Why the renderer is a texture and not a platform view”A platform view puts native content in a window of its own, positioned over the Flutter surface. It works, but it also means the 3D content isn’t really in your application’s layout. It can’t be clipped by a rounded rectangle, it can’t be overlapped by a panel, and it doesn’t animate with the rest of the frame.
On macOS and iOS, Orblit renders into a CVPixelBuffer backed by an IOSurface
and hands that to Flutter’s texture registry, which composites it like any
other texture. There is no readback and no copy through the CPU. The GPU wrote
it, and the GPU reads it. The other platforms reach the compositor in their own
ways, two of them with a copy per frame, and
platform support lists
them.
What that buys you, concretely: the viewport clips, scrolls, sits under other widgets, resizes with a slider, and takes part in a hero animation. In the editor, it’s why four viewports and a game view can share one window.
Where the repositories are
Section titled “Where the repositories are”Orblit is several repositories because the parts have genuinely different audiences, not because a monorepo was ruled out. Every game needs the engine, only a multiplayer game needs the networking, and only someone writing scripts needs QuickJS and a TypeScript toolchain.
| Repository | What it is |
|---|---|
orblit |
The engine. Most packages live here. |
orblit-editor |
The editor application |
orblit-examples |
The gallery, the viewport, worked examples |
orblit-net |
Multiplayer |
orblit-script |
TypeScript scripting on QuickJS |
Within orblit, granularity is per package rather than per repository.
pub can resolve a subdirectory of a git repository, so a package in a
monorepo is already consumable on its own. Splitting one repository per
package buys nothing, and costs you a co-ordinated release every time two of
them change together.
