Case study · 2026

Day to Day Match 3

A match-3 game that generates its own music in real time, in C, on the audio thread, and ships as four visually and sonically distinct products from a single binary and a single set of JSON files.

The game is the surface. What is underneath is a configurable app framework with a real-time synthesis engine in it.

Objective-C SpriteKit AVAudioEngine Core Haptics Core Graphics iOS + macOS
Sole engineer and designer · Finished and in testing, App Store submission pending · The game

Why it is built this way

The game was built for people living with dementia, and their caregivers. My wife had Alzheimer's, and a match-three game was one of the last things she could still do on her own, until it started running out of lives and showing her a countdown.

That brief is why the architecture looks the way it does. No lives, no timers, no fail states, no ads, no purchases, no accounts. Removing failure removes the need for most of the systems a casual game usually ships, and it forces the interest to come from somewhere else.

Difficulty steps down. A single comfort control bundles board size, color count, the specials roster, tempo ceiling, hint timing and language tier. The design rule for every feature was that it still gives something at the lowest setting, or it turns off.

The full story is on Day to Day Dementia →

By the numbers

354lines of C in the render callback
0allocations on the audio thread
36concurrent synthesis voices
4products from one binary
~15,300lines of shared source
0music files — the score is synthesized

The audio engine is a synthesizer, not a sound player

Sound has three states, chosen by the player: off, conventional game sounds, or music. The first two are what a casual game normally ships, a set of recorded effect samples triggered on events. The third replaces them entirely.

In music mode there are no music files. Every note, drum hit and echo is generated sample by sample while you play, and the effect samples step aside so the score is what you hear.

Two synthesis models, chosen per product

  • Karplus–Strong plucked string. A physical-modeling algorithm that excites a delay line with noise and filters it as it recirculates, producing the warm decaying pluck of the primary identity.
  • PolyBLEP sawtooth through a state-variable filter. A classic subtractive voice, band-limited to avoid aliasing, with drive, detune and a resonant filter for the harder second identity.

Both drive the same voice architecture: 24 melody voices, 4 harmony and pad voices, 4 chime voices, 4 drum voices, each with its own envelope, pan and filter state.

Drums are synthesized from primitives too, a pitch sweep and a noise burst and an envelope per voice, with three distinct kits defined as data rather than code.

A tempo-synced stereo delay

Cross-feedback with per-repeat damping. The delay time is derived from the current musical tempo and glides toward it, so as the game's pace drifts the repeats bend in pitch like tape catching up.

The music is played by the board, not triggered by it

The engine has a music-theory layer: a scale ladder, chord tones, phrase grids, quantization, and a real V–I cadence that resolves at the end of each cycle. Gameplay events are mapped onto it musically rather than as sound effects.

  • The board is a control surface. Where you match determines what you hear. Horizontal position sets register and stereo placement; vertical position opens or closes the filter. The idea is borrowed from grid-based performance instruments.
  • Special-piece detonations become musical figures whose shape matches their geometry. A radial blast plays expanding intervals outward in both directions at once. A cross plays two runs departing from a struck center. A diagonal moves two chord tones per step because it crosses two cells per step. A color-clear sweeps the whole register because it gathers from everywhere.
  • An arrangement system brings layers in and out on bar lines as the session progresses, so parts are earned rather than switched on.
  • Adaptive tempo with a deliberate lag. The music's pulse follows the player's pace but is curved to stay slightly calmer than the play, with an asymmetric glide that is quick to rise and slow to fall. Borrowed from the iso-principle in music therapy: meet where someone is, then lead.

One binary, four products

Every visual, verbal, musical and pacing decision is resolved through a single lookup point that reads JSON. There is no compiled-in brand.

A skin file defines up to 16 sections: typography, palette, board geometry, sky gradients, world treatment, art sets, HUD, objectives, music, pacing, and language.

  • Language as data. Each skin carries 44 wording overrides across three tiers, full, simplified and wordless. A product can restate the entire game's vocabulary without touching code.
  • Movements, a second-order config layer. A single skin can carry an ordered set of movements, partial overrides of its own music, sky and world, selected by progress. The hardest-edged product moves through six of them, so it gets sonically and visually darker as you go deeper, then loops. Each movement is a few lines of JSON on top of the parent skin.
  • Per-product user defaults. Each identity gets its own settings suite, so tuning one product's mixer cannot leak into another.
  • A live mixer built on trims, not absolutes. The in-app sound controls store offsets from the skin's authored value rather than replacing it, so a player's adjustment survives every movement change instead of flattening the authored variation.
On the reference material

The second identity's palette was built by studying a specific catalog of industrial and electronic records, analyzing the drum programming, detuning, delay behavior and arrangement dynamics that make those records work, then rebuilding them from synthesis primitives. Nothing is sampled. It is reference, not reproduction.

Wordless mode, larger pieces, fewer colors and calmer pacing are not a settings screen bolted on. They are the same skin and preset machinery the four products are built from.

Haptics as a parallel channel, not decoration

Core Haptics patterns share the same geometric vocabulary as the audio. A blast's shape becomes a haptic envelope the same way it becomes a musical figure. A radial blast is a crack over a short rumble, because a single transient can never read as an explosion. A cross is three transients departing, each sharper than the last. A color-clear is a rising rumble driven by a parameter curve, ending in an arrival.

Two decisions worth noting because they are decisions not to build:

  • No continuous or physics-driven haptics. No pulse, no per-cascade feedback. The battery cost is not worth it, and for the intended audience constant actuator activity is overstimulating. Haptics fire on the player's action only.
  • Full graceful degradation. Devices without Core Haptics fall back to standard impact generators. Devices without a Taptic Engine get nothing rather than a broken switch, and the settings row hides itself where it cannot do anything.

Haptics also fire before the audio branch, so they work with sound off.

Game engine and lifecycle discipline

  • Non-blocking play. Cascades never lock input. Only settled pieces are selectable, and a match made during a cascade inherits the cascade's multiplier.
  • Dynamic board geometry. Tile size drives the grid rather than the reverse, so the board fills any screen at any size, respecting safe-area insets on all four edges.
  • Rotation by transposition. On an aspect flip the grid transposes, so pieces keep their neighborhoods rather than being reshuffled, and the change is deferred until the board settles.
  • Single-owner animation queue. Pieces carry a dying flag, and there is exactly one entry point into the animation queue, which dedupes and refuses to override a committed removal. This closed a class of bug where overlapping blasts claimed the same piece twice and stranded an invisible, unmatchable sprite on the board.
  • A self-audit. A board-integrity checker detects count drift, sprites in the scene but not in the grid, and displaced pieces, feeding a diagnostics log that deduplicates repeated findings so real faults are not buried under repeats.

The world layer is a cumulative day cycle with CG-rendered sky gradients, a sun that tracks progress across the header, a moon rendered per day with a real terminator (a lit-limb arc and a swept half-ellipse, mirrored for waning phases), and seasonal tinting. All of it derives from constants, so no calendar literal is hard-coded anywhere.

How it is verified

There is no test framework. A real-time audio engine and a physics-adjacent board do not submit to unit tests easily. Instead:

  • Numerical claims are replayed independently. Gain stacks, envelope timings, wave scheduling and layout arithmetic are re-derived in a separate script from values read out of the source, so a change is checked against what the code says rather than what it was meant to say.
  • Measure, then draw. UI panels compute their own height from their content instead of carrying a guessed constant.
  • Root causes over symptoms. Persistent audio harshness turned out to be big-endian PCM files playing as static, not a mixing problem. A frozen progress indicator turned out to be an animation being re-targeted faster than it could complete, not a math problem.

Facts at a glance

Language and frameworksObjective-C, SpriteKit, AVAudioEngine, Core Haptics, Core Graphics
PlatformsiOS and macOS from one project, one shared source set
Shared source~15,300 lines. Audio engine 4,727, scene 5,547, settings 2,070, skin layer 893, haptics 464
AudioAVAudioSourceNode render callback, C11 atomics, lock-free note queue, 36 concurrent voices
SynthesisKarplus–Strong physical model, PolyBLEP saw through a state-variable filter, synthesized drum kits, tempo-synced stereo delay
Products4 skins from one binary, up to 16 config sections each, 44 language overrides per skin, nested movement overrides
DistinctiveGenerative score driven by board position, blast geometry shared between audio and haptics, accessibility implemented as the configuration system