flutter3d
Showcase Changelog 62 packages API reference

What core is

Core is everything all three games use and none of them owns. Six packages that never learn what a monster, a coin or a lap is, plus four backends that never learn what a scene is.

Nothing in this section is genre knowledge. That property is what made the second game possible to write, and every page here says which test enforces it.

The six packages #

Package What it owns Depends on
flutter3d The renderer, the scene graph, geometry, decoders, animation flutter3d_hardware and nothing below it
flutter3d_hardware The HAL: devices, encoders, buffers, textures, pipelines, passes. No implementation at all nothing
flutter3d_sim Fixed step, input, levels and holes in them, mechanisms, actors, navigation and the automap, ECS, snapshots, demos, rewind flutter3d_physics, vector_math. Plain Dart, no Flutter
flutter3d_app The backend choice, the surface a frame reaches Flutter through, a level loaded into a scene, storage flutter3d, flutter3d_sim and Flutter
flutter3d_game The devices, the run, the settings and save screens, actor and fixture visuals flutter3d_app, flutter3d_sim and Flutter
flutter3d_physics Shapes, broadphase, sweeps, rays, character controller, rigid bodies, liquids nothing. Plain Dart

Four backends implement the HAL, and an application names exactly one of them in its pubspec:

Backend Runs on
flutter3d_impeller flutter_gpu, over Metal and Vulkan. The production one
flutter3d_webgl WebGL2 in the browser. Runs all three games at a fixed resolution and a lower frame rate, and is what a web build opens where the browser hands out no WebGPU adapter
flutter3d_cpu Nothing: it rasterises in Dart, so 97 golden scenes stay checkable with no GPU in the room
flutter3d_webgpu WebGPU in a browser that has an adapter. A web build tries it first; --dart-define=FLUTTER3D_WEBGPU=false leaves it out of the bundle and keeps WebGL2 only

flutter3d_conformance is the suite each of them passed before it belonged in that table, and Writing a HAL backend is the guide for writing the fifth.

The two halves, and the line between them #

flowchart LR
  subgraph drawn["drawn: needs a device"]
    direction TB
    renderer["Renderer"]
    passes["passes: shadow, opaque,<br>transparent, bloom, composite"]
    device["GraphicsDevice"]
    renderer --> passes --> device
  end

  subgraph headless["headless: plain Dart"]
    direction TB
    step["EngineLoop · TimeSource"]
    input["InputState"]
    world["CollisionWorld · CharacterController"]
    level["Level · Mechanism · ActorSystem"]
    step --> world
    input --> world
    level --> world
  end

  subgraph shared["shared, device-free"]
    direction TB
    scene["Scene graph · bounds · culling"]
    geom["MeshData · Shape · MeshBuilder"]
    assets["glTF · OBJ · .f3d decoders"]
  end

  headless -. "loaded into a scene by" .-> app["flutter3d_app"]
  app -.-> drawn
  shared --> drawn

The left half runs under dart test on the VM. Its characteristic failures happen once in a thousand steps, so finding them means running thousands of steps in a loop, which is possible because none of it needs a device.

The middle band is the part people expect to need a GPU and does not. Bounds, culling, framing, picking, decoding and draw-call sorting are all arithmetic. The scene layer holds a MeshGeometry, not a GpuMesh, and requiring a device would have meant none of them could be unit tested.

What to read, in what order #

Each page below stands alone, but in this order each one builds on what the previous one explained.