TechIDaily Journal
Six Games, One Model: DailyIQAI's On-Device Cognitive Training
DailyIQAI runs six cognitive training games powered by a single CoreML model that adapts to each player on-device. We walk through the model, the adaptive difficulty engine, and why we refused to put a server in the middle.
Six Games, One Model: DailyIQAI's On-Device Cognitive Training
*How a single CoreML embedding powers six daily cognitive games, adapts difficulty in real time, and never sends a training sample to a server.*
DailyIQAI is a daily cognitive training app for iPhone and iPad. It ships six games that exercise memory, attention, processing speed, cognitive flexibility, reasoning, and spatial working memory. Every game runs on a single shared CoreML model, every difficulty adjustment happens on-device, and every training sample stays in the user's sandbox. This post is the architecture behind that promise, and the five mistakes that taught us how to honor it.
1. Why On-Device
The cognitive training market is full of apps that send every tap to a server, run a model there, and ship an updated difficulty curve back to the phone. That model is wrong for two reasons. The first is latency. A difficulty adjustment that arrives 300 ms after the user finishes a round feels like a phone notification, not a coach. The second is privacy. Cognitive training data is behavioral. It is a fingerprint of how a person thinks, and it should not leave the device.
CoreML on a modern iPhone or iPad runs the model in under 30 ms. That is fast enough to adjust difficulty before the next round starts, and it leaves the training data in the app's encrypted sandbox. We do not need a server, and we do not want one.
2. The Six Training Domains
The six games are not six separate models. They are six presentations of a single shared embedding. The embedding is a 128-dimensional vector that captures the player's recent performance pattern, and each game projects that vector into a difficulty curve specific to its own domain.
- Memory: a paired-card recall game where the deck size adapts.
- Attention: a target-detection game where distractor density adapts.
- Processing: a symbol-matching game where the symbol set size adapts.
- Flexibility: a rule-switching game where the switch frequency adapts.
- Reasoning: a pattern-completion game where the pattern depth adapts.
- Spatial: a rotation game where the rotation magnitude adapts.
Every game reads from the same embedding and writes back into the same embedding. A player who trains memory in the morning and reasoning in the evening is building a single integrated model of their cognitive state, not six isolated ones.
3. Architecture: CoreML + GameplayKit + SwiftUI
The app is structured in three layers. The SwiftUI layer renders the UI and captures the tap events. The GameplayKit layer manages the game state and the round-by-round scoring. The CoreML layer ingests the score history and produces the next difficulty curve.
SwiftUI (Views, Bindings)
↓ tap events
GameplayKit (Game state, Scoring)
↓ score history
CoreML (Embedding, DifficultyAdjuster)
↓ next difficulty
GameplayKit (Round setup)The CoreML layer is a single Swift package that ships with the app. It contains the embedding model, the difficulty adjuster, and the on-device trainer that updates the embedding after each round. The package has no network code and no I/O outside the app sandbox.
4. The Shared Embedding Model
The embedding model is a small neural network trained on anonymized aggregate data from the beta fleet. The input is a 64-element vector of recent performance: accuracy, response time, error pattern, and a few derived features. The output is a 128-dimensional embedding that captures the player's current cognitive state.
The model is intentionally small. It is 1.2 MB on disk and runs in 8 ms on an A15. A larger model would be marginally more accurate, but the latency cost would push us past the 30 ms budget and the disk cost would push the app over the 50 MB download threshold we set for ourselves.
import CoreML
final class EmbeddingModel {
private let model: embedding_v3
func infer(history: [PerformanceSample]) -> [Float] {
let input = MLMultiArray(shape: [1, 64], dataType: .float32)
for (i, sample) in history.enumerated() {
input[i] = sample.featureVector[i]
}
let output = try! model.prediction(input: input)
return output.embedding
}
}The MLMultiArray is allocated per inference. We tried a pooled allocator to reduce GC pressure, but the savings were marginal and the code complexity was not worth it.
5. Bayesian Knowledge Tracing for Adaptive Difficulty
The difficulty adjuster is a Bayesian Knowledge Tracing (BKT) model. BKT is a four-parameter model that estimates the probability that a player has mastered a skill based on their observed performance. The four parameters are prior knowledge, learning rate, slip rate, and guess rate. We tune them per game on the beta fleet data.
The BKT update runs after each round. It takes the player's accuracy on the round and the time taken, and it produces an updated mastery probability. The next round's difficulty is set by mapping the mastery probability to a difficulty curve.
struct BKTState {
var pMastery: Double
let pLearn: Double
let pSlip: Double
let pGuess: Double
}
func update(state: BKTState, correct: Bool, timeMs: Int) -> BKTState {
let pCorrectGivenMastery = correct ? 1.0 - state.pSlip : state.pGuess
let pCorrectGivenNotMastery = correct ? state.pGuess : 1.0 - state.pGuess
let posterior = (state.pMastery * pCorrectGivenMastery) /
(state.pMastery * pCorrectGivenMastery +
(1 - state.pMastery) * pCorrectGivenNotMastery)
let newMastery = posterior + (1 - posterior) * state.pLearn
return BKTState(pMastery: newMastery, ...)
}The BKT update is the only place where we touch the player's history. The history itself is a sliding window of the last 200 rounds, and it is stored in an encrypted SQLite table inside the app sandbox.
6. On-Device Training: Why and How
The embedding model is pre-trained on aggregate beta data, but the per-player embedding updates after each round. We use a tiny on-device trainer that runs a single gradient step on the player's embedding vector after every round.
The trainer is not a full backpropagation. It is a single-vector update that nudges the embedding toward a target vector derived from the round's performance. The update takes 2 ms and the new vector is written back to the sandbox database.
We made the on-device training decision for two reasons. The first is privacy: a model that updates on-device never needs to ship training data to a server. The second is responsiveness: the embedding reflects the player's last round, so the next difficulty adjustment is informed by their most recent performance.
7. Privacy: Zero Server, Zero Analytics
DailyIQAI has no analytics SDK, no crash reporter that ships user data, and no server endpoint that accepts training samples. The app does not know the player's identifier beyond the iCloud account that owns the sandbox, and the sandbox is encrypted at rest by the operating system.
We have turned down three acquisition offers that came with strings attached: the buyer wanted to add a "personalization server" that would have shipped training samples to a backend. The on-device architecture was the reason the founders said no, and it is the reason the app stays independent.
8. Performance: 30 ms Inference Budget
The full pipeline from tap to next difficulty is 30 ms on an A15. That is the budget we set for ourselves, and it is the budget we hit. The breakdown is 8 ms for the embedding inference, 2 ms for the on-device training update, 4 ms for the BKT update, and the rest for the GameplayKit state machine and the SwiftUI render.
On an A12 the pipeline runs in 45 ms, which is over budget but still feels responsive. We considered dropping A12 support, but the beta fleet had 8% A12 users and the cost of supporting them was a single optimization pass on the embedding model.
9. Five Things We Got Wrong
Pre-training on the full beta fleet. Our first embedding model was trained on every beta player's data, including outliers who played for ten hours a day. The model learned the outlier pattern and the average player found the game too easy. We retrained on the median player and the difficulty curve matched the target population.
BKT priors from the literature. The BKT literature has canonical priors for educational settings, but they were wrong for our app. Our rounds are shorter and our players are older, and the canonical priors produced a difficulty curve that was too aggressive. We tuned the priors on the beta fleet and the curve settled.
MLMultiArray pooling. We pooled MLMultiArray allocations to reduce GC pressure. The savings were 1 ms per inference and the code complexity was significant. We removed the pool.
iCloud sync of the embedding. Our first version synced the embedding across the user's devices via iCloud. The sync added 200 ms of latency and the device-specific embedding drifted when the user switched devices. We disabled sync and the embedding stays on the device where it was trained.
Game Center leaderboards. The leaderboards showed the player's global rank, which produced an optimization mindset that conflicted with the on-device training philosophy. We removed the leaderboards and the retention curve actually improved.
10. What's Next
Three things are in active development. First, an iPad split-screen mode that lets two players train on the same device. Second, a visionOS variant that uses the spatial sensors for the spatial working memory game. Third, an Apple Watch complication that surfaces the daily mastery score for the player's favorite domain.
If you want to see the architecture in action, DailyIQAI has a seven-day free trial with no commitment. The free trial ships with all six games and the full on-device training pipeline, so you can see the model adapt without spending a cent.
*DailyIQAI is designed for general cognitive exercise, not treatment of any cognitive condition. If you have a cognitive condition or symptoms that worry you, please consult a qualified clinician.*