A game framework written entirely in Swift, rendering with Metal — the MonoGame model of game development, designed the way Apple designs its modern Swift frameworks.
The goal: the complete 2D engine, in one box. Everything you need to
take a 2D game from empty folder to finished and shippable — sprites, text,
audio, input, effects, animation, particles, tilemaps, UI, saves — built
into the framework, with APIs you guess right on the first try. Where the
MonoGame world reaches for third-party libraries, SwiftGame just keeps
going. See Docs/ROADMAP.md for the scorecards and
phase plan.
import SwiftGame
import SwiftGameMetal
@main
final class MyGame: Game {
var position = Vector2(100, 100)
func update(_ time: GameTime, _ context: GameContext) {
position.x += 120 * time.deltaSeconds
}
func draw(_ time: GameTime, _ context: GameContext) {
// SpriteBatch arrives in workstream 1e.
}
}Phase 1 (Mac 2D core) is complete — everything needed to build and ship-quality-prototype a 2D game on macOS works today:
- ✅ SwiftPM package, Swift 6 strict concurrency
- ✅ Math: vectors (SIMD-backed),
Angle,Color,Rect, 2D matrices - ✅ Game loop: fixed timestep with MonoGame-exact catch-up semantics
(
isRunningSlowly, 500 ms stall clamp), or variable timestep - ✅ macOS window host (AppKit + CADisplayLink)
- ✅ Metal backend behind a backend-agnostic GPU seam
- ✅
Texture2D+Assetsloading (PNG/HEIC/JPEG from bundles, cached) - ✅
SpriteBatch: sort modes, blend/sampling enums, tinting, rotation, source rects, flips — 10,000 sprites at 120 fps inSpriteGallery - ✅ Input: keyboard, mouse, and gamepads as per-frame snapshots with
built-in edge detection (
input.wasJustPressed(.space)) — no previous-state bookkeeping in game code - ✅ Prototype shapes:
Rect,Circle(anti-aliased),Triangle,Line— onefill/line/strokecall to draw,contains/intersectsto collide, so a game is playable before any art exists - ✅ Text:
sprites.draw("Score: \(score)", font: .system(24, weight: .bold), at: p)— CoreText glyph atlas, Retina-sharp, kerning/emoji/fallbacks handled,font.measure(_:)for centering - ✅ Audio:
sound.play(volume:pitch:pan:)fire-and-forget on a pooled AVAudioEngine,makeInstance()for loops,context.audio.musicfor streaming — plus composed audio before any files exist:SoundEffect.tone(880, duration: 0.06)andSoundEffect.melody(.c(5), .e(5), .g(5))with per-note durations - ✅ iOS/iPadOS (Phase 2): UIKit host with lifecycle pause/resume, touch
input as snapshots (
input.primaryTouch,touches/previousTouches), andExamples/PongApp— an Xcode project that builds Pong as a real app (verified on the iPad Simulator) - ✅ Virtual resolution:
config.resolution = .fixed(width: 960, height: 540)declares the game's world size once — SwiftGame scales and centers it on any screen or window (letterboxed), text re-rasterizes to stay crisp, and touch/mouse input arrives already converted to world coordinates - ✅ Touch controls without art:
makeSymbolTexture("pause.circle.fill")rasterizes any SF Symbol into a tintable texture,sprites.draw(icon, in: rect)fits it to a layout rect, andinput.wasTapped(in: rect)is the whole button — tap or click, edge-detected - 🚧 Next — Phase 3, graphics completeness: render targets
(render-to-texture), built-in + custom shader effects, and scissor
clipping; then Phase 4's shipping essentials (text input, fullscreen,
rumble, save data). Full plan in
Docs/ROADMAP.md.
See Docs/ARCHITECTURE.md for how it fits together and
Docs/ROADMAP.md for where it's going.
- macOS 14+ or iOS/iPadOS 17+
- Xcode 16+ / Swift 6
Three ways to a running project:
- Tutorial —
Docs/TUTORIALS/01-your-first-game.mdstarts from an empty folder: create a SwiftPM package, add SwiftGame as a dependency, build a complete game learning each feature as you go. - Template — copy
Templates/StarterGame, point itsPackage.swiftat SwiftGame,swift run. - VS Code — install
Tools/vscode-swiftgameand run SwiftGame: New Game Project. (API autocomplete comes from the official Swift extension; ours adds scaffolding and game-idiom snippets.) - Xcode —
Docs/TUTORIALS/02-xcode-setup.md: open the package folder directly, or build an App project via File ▸ Add Package Dependencies../Tools/install-xcode-templates.shadds a SwiftGame section to the New File chooser.
swift run Pong # W/S, arrows, or gamepad; P pauses
swift run SpriteGallery # 10,000-sprite batching stress test
swift run CoinChase # the "your first game" tutorial result
swift run Pong --frames 300 # exits after 300 frames, prints avg fps (CI mode)New to SwiftGame? Start with
Docs/TUTORIALS/01-your-first-game.md —
a complete game in ~60 lines.
swift test # all suites; GPU tests skip without a Metal device
swift test --filter GameLoopClock # one suite- Depth before breadth. Mac-first, 2D-first: a complete 2D engine — MonoGame's capabilities plus what its ecosystem leaves to third-party libraries (animation, particles, tilemaps, UI) — before 3D or new platforms. The framework absorbs the boilerplate other engines make every game rewrite (edge detection, letterboxing, save paths).
- The first guess compiles. A Swift developer's naive guess at the call
site should be the actual API —
wasJustPressed,wasTapped(in:),SoundEffect.melodyare the bar. - Imperative core, declarative sugar. The
update/drawloop is the ground truth; a SwiftUI-flavored scene layer (SwiftGameScene) is optional and experimental until Phase 3. - Enums over configuration objects. Blend modes, sampling, and time steps are Swift enums with associated values.
- The GPU seam stays honest. The core framework depends on
SwiftGameGPUprotocols only. Metal lives inSwiftGameMetal; a future console backend is a new module, not a rewrite.
MIT — see LICENSE.
