Skip to main content
SwiftUI lays out a tree of views. A game screen is a tree of sprites: artwork placed at coordinates, animated per frame, hit-tested where it is drawn, and ordered by explicit z-position. SpriteKit — Apple’s 2D scene-graph framework — models that tree directly. The 2D game / Interactive app template starts you on SpriteKit, wired into the same RIBs structure the Production app template uses.
A 2D game running on iPhone: a games grid, a countdown, then an arithmetic mini-game with sprite characters, hearts, a score and a timer

A drawn game surface: sprite artwork, SKAction motion, and a HUD living in the same scene as the playfield.

What you get on the first build

Pick the card, press Create, and the app runs: splash → menu → a tap-the-target sample game with a score and a sound effect, plus a settings screen that persists sound volume. Five pieces you would otherwise write yourself are already wired behind that sample:
  • A resource gate. ResourcesLoadingWorker registers the fonts, makes the sprite atlas resident and warms the sound pool; the splash waits on it. If a resource fails to load, the worker records it to Diagnostics and publishes it on loadingIssues for the splash to display, then finishes loading and opens the gate anyway.
  • An artwork pipeline. Source PNGs live in Art/<Atlas>/, and Scripts/pack-atlases.sh packs them into the committed atlas page the app reads. Run it when the artwork changes; Scripts/pack-atlases.sh --verify checks the committed page against the sources without re-packing.
  • Pooled audio. A fixed-size pool of preloaded AVAudioPlayers, rotated least-recently-used. Overlapping plays each get their own player up to the pool size; past that the new sound is dropped rather than cutting off a playing one.
  • Idempotent layout. layoutScene() runs on presentation, on rotation and on every safe-area change. Chrome is positioned against safeAreaFrame and the playfield against the full frame, so the notch and home indicator are accounted for.
  • Tests that pass from the first build, registered in the same all-modules workspace as the other templates. The sample game’s tests run its rules without instantiating SpriteKit, because the score and sound logic is Interactor state.
The SpriteKit machinery — the scene base class, the scene navigator, the routing protocols, the sound pool, the atlas loader, the button control — is the open-source modaal-foundation-spritekit package, pinned by URL in your Package.swift. It is a versioned dependency: you import its products, and it is upgraded by moving the pin rather than by editing files in your project.

What changes from the Production app template

A RIB’s presenter is an SKScene or an SKNode subclass instead of a SwiftUI view. The rest is the same: Builder, Interactor, Router and Component, the same protocol boundaries, the same generated mocks. One rule follows from that: build screens as node RIBs, not scene RIBs. Presenting a scene runs a transition and discards the outgoing scene’s node tree. Attaching a node RIB is an addChild into the scene already on screen, and the node remains attachable elsewhere afterwards — it reads no ambient state (self.scene, UIScreen, the view) and lays out against the metrics its host hands it, so the same node can be attached into a different scene, or into a host app’s scene, without edits. The scaffolded app is built that way: two scenes, Splash and Menu, with the game and settings screens as nodes attached into the Menu scene.
Ask the agent for a new screen and it scaffolds the RIB for you — the right variant, plus the artwork wiring when the screen draws sprites.

Starting one

Describe the game in the composer, pick 2D game / Interactive app, and press Create. The first turn writes a PRD.md and a first feature spec, as it does for every template. The architecture is already recorded in .modaal/project.json, so that turn goes straight to product scope. Two prompts with enough detail for the agent to plan from:
The sample game and the four placeholder sprites in Art/Game/ are there to be replaced. Only the sample game in RIBs/Game uses them, and deleting them and re-running Scripts/pack-atlases.sh also drops the ~1 MiB atlas page they occupy.
The card carries a Beta chip. It marks the newest entry in the template catalog. The template scaffolds, builds and tests like the others, and the chip places no restriction on what you can build with it.

When not to use it

Stay on Production app for a conventional app, including an animation-heavy one — rich SwiftUI animation is not a reason to move to SpriteKit. Building a single playful screen on a scene tree means that screen takes on the scene lifecycle, the layout pass and the atlas pipeline, and its UI is assembled from SKNode subclasses rather than from your existing SwiftUI views. If the graphics requirement is uncertain, start on Production app. RIBs → RIBs + SpriteKit is additive, not a rewrite: the RIB tree, the modules and the tests keep their shape, and what gets added is the SpriteKit packages and a scene host. Ask the agent to add the game surface at the point you need it.
More on picking a template: Starting a new project. For a second opinion on your idea, ask in the community.