/ home  ›  apps  ›  lyran defense
Lyran Defense app icon

Lyran Defense

A painterly tower-defense campaign of the Lyran Wars — twenty-one stands along the road to the evacuation fields, a procedural orchestral score, and one deterministic Go simulation that plays the same battle, bit for bit, on iPhone, iPad, Mac and Apple TV.

App Store · iPhone · iPad · Mac · Apple TV · one free download

A war you cannot win, painted one board at a time.

The Lyran homeworld is falling. The evacuation ships are loading. Between the rift and the beacon stands one thing: you. Lyran Defense is not a war you win — it is a year of buying hours. Twenty-one stands along the road to the evacuation fields, each held long enough for another ark to clear the ground, and then The Long Watch: an endless mode where the only score that matters is how many more ships got away because you were still standing.

Every board is a hand-illustrated painting with its own biome and its own way of turning against you — mires that slow the column, rifts that tear open mid-wave, roads that fork and cross. Eight tower families upgrade into an elite form, and three of them fork at the top into one of two shapes, permanently. A fully procedural orchestral score is composed on the device as you play: it rises with the wave, thins when the line thins, and mourns the levels you lose.

It is a personal project shipped with the same discipline as the client work: free and complete, fully offline, no ads, no account, nothing collected — and every one of those sentences is checked at deploy, not merely written down.

“I am no soldier. None of us are.” Warden Sael · the first watch
21
story levels, plus The Long Watch endless mode
22
hand-illustrated boards, gold against violet
8
tower families; three fork at the top, permanently
19
painted creeps across four sub-races that never mix
4
Apple platforms from one download and one codebase
26
Game Center achievements, none for opening the app

The campaign, the line, the song.

The campaign

Twenty-one stands and an endless watch

A story campaign across the falling homeworld with hand-written briefings before every stand, four difficulties from a calm first watch to an edge that will take your soul, and per-level records: fastest clear, least gold, fewest towers, most lives held.

The line

Towers that fork, and never un-fork

Arrow, frost, ember, mortar, venom, lance, banner and mender. A Longbow becomes a Starshot Bastion or a Skypiercer — never both. Target priorities per tower, special attacks aimed by hand, and ground that refuses some towers outright and tells you why.

The board

Every level is a painting you are allowed to lose

Twenty-two illustrated boards, each its own biome and landmark, painted taller than wide so a phone held upright gives the battlefield the whole screen. Weather fights you: rain and snow slow the field and hide the road.

The song

A score composed as you play

No looped tracks. The orchestral score is generated on the device from the state of the battle — rising with the wave, thinning with the line — and The Score keeps a chronicle of the war's music, earned by holding it.

The record

A war codex that fills as you meet it

The Journal writes down every tower, creep and doctrine you encounter, in the voice of a warden who never expected to need any of it. The Gallery keeps the paintings you have earned. Twenty-six Game Center achievements, optional and signed-in-or-not.

Made to be fair

Free, complete, offline, quiet

No ads, no account, no analytics, no third-party SDKs. The whole game is on your device; the only network it ever touches is Apple's own, and every bit of that is optional. “Light the Beacon” is a one-time supporter purchase that unlocks nothing you need to win.

One simulation, four machines.

The game is a pure-Go simulation with no renderer, no I/O and no floating point: integer math in milli-cell fixed point, a seeded PCG random stream, and no map iteration anywhere in game logic. That is what makes it deterministic — the same seed and the same commands produce the same state, byte for byte, on every CPU it has ever run on — and determinism is what makes everything else cheap: a battle is exactly replayable, a victory is exactly earned, and a bug report is a seed and a command log.

The sim's own rule checks are the integrity layer: every command goes through sim.Apply, which refuses anything the rules refuse, so there is no second copy of the rules in any client to drift. Above it sits one Swift codebase building four Apple targets — the phone, the iPad, the Mac and the television are cuts of the same game, not ports of a phone game — and the marketing site's claim that it "runs the same battle on each" is a golden-hash test, not a sentence.

Tech.

Simulation

  • Go, pure — no renderer, no I/O, no floats
  • Milli-cell fixed-point math, seeded PCG RNG
  • StateHash() + committed golden hashes in CI
  • Content as JSON via go:embed, validated at load; bad data fails CI
  • Terminal playtest client drives the sim exactly as a renderer would

Apple Clients

  • SwiftUI + SpriteKit, one codebase, four targets
  • iOS · iPadOS · macOS · tvOS, Swift 6 strict concurrency
  • gomobile XCFramework binding, JSON over the FFI
  • Game controllers (PlayStation, Xbox, Switch) · Siri Remote focus cursor
  • StoreKit, Game Center, iCloud key-value store — all optional

Sound & Art

  • Procedural orchestral score, composed on device from battle state
  • 22 hand-illustrated boards, painted towers and creeps — no sprite grid
  • One icon master → every platform cut, enforced by a byte-compare
  • Story-art ledger: every named plate must ship on every client

Shipping Discipline

  • Release gates read the signed archive — entitlements asked of codesign, not of the source
  • Marketing claims checked against the store both ways: a badge for a platform that does not serve fails, and so does a live platform the page hides
  • Every rune in the shared string tables must have a glyph in every font that draws it
  • Screenshot walks across all four targets; a caption that covers the HUD refuses to upload

The parts worth writing down.

Determinism is the test oracle

Because the sim has no floats, no I/O and no map iteration in game logic, a level's final state hashes to the same value on every machine. Those hashes are committed as golden files and checked on every build, and a cross-platform job proves the sim is bit-identical on a second host. A balance change that alters a hash is a decision; one that alters it by accident is a bug, and the diff says which.

Golden state hashes Fixed-point math Seeded PCG

The board is taller than it is wide

Most tower defense is designed landscape and cropped for phones. These boards were painted portrait from the first sketch, so a phone held upright and an iPad in portrait both give the battlefield the whole screen, and the television letterboxes a painting instead of stretching one. The camera, the HUD and the touch targets all follow from that one decision.

A score composed, never replayed

The orchestral score is generated on the device from what the battle is doing — the wave building, the line thinning, a tower lost — rather than crossfading between loops. Two identical sources are kept in lockstep by a byte-compare in CI, so the music one client hears is the music every client hears.

Procedural music Orchestra parity check

Records merge; they never overwrite

Progress rides the player's own iCloud between devices, and the merge is per record: a fastest clear set on the Mac and a no-loss run set on the phone both survive, and a device that has been in a drawer for a month contributes what it earned instead of being flattened by one that has not. It is the player's iCloud, not a server of mine — and the whole game plays signed out of it.

iCloud key-value store Per-record merge No first-party server

Ask the font, the only party that knows

A shared string drawn by two clients with different font stacks can come out as a tofu box on one of them — string correct, JSON valid, layout unchanged, and a text-level check reports it as fine. So a test sweeps every rune in the shared tables against every font that draws them and asks the font itself whether the glyph exists. Exemptions are by file path, never by rune, and an exemption that matches nothing fails.

Ship gates read the artifact, not the intent

The entitlements that make iCloud and Game Center work live in source files that were correct all along; what matters is whether they reached the signed archive. So the release lane asks codesign what the archive actually carries before anything is submitted, and the marketing site's platform badges are reconciled against the live store in both directions at deploy — a claim about what ships is checked against the thing that ships.

Artifact-level gates Store-reconciled claims Fairness contract, checked