Appearance
Offline progress
Credits the time the player was away — after a reload (loaded event) and after returning from a hidden tab (resume event) — by advancing the simulation, capped by your policy.
ts
import { offlineProgress } from "@idlekitjs/plugins/offline-progress";Usage
ts
engine.use(offlineProgress({ maxMs: 8 * 60 * 60 * 1000 })); // cap: 8 h
// Later:
await engine.load(save); // emits "loaded" -> catch-up runs automatically
engine.start();That's the whole integration. On loaded, the plugin computes Date.now() - savedAt from the save's timestamp; on resume (emitted by the page-lifecycle bridge) it receives the elapsed background time. Either way it clamps to [0, maxMs] and calls engine.advance(elapsed / 1000).
Why advance, not a lump sum
engine.advance replays the away time in the same fixed steps as real time (see Simulation loop). Idle mechanics are non-linear — caps, thresholds, produce→spend loops — and a single giant dt would distort them. With fixed-step catch-up, offline production is exactly what attended production would have been, and every mechanic (producers' cycles, crafting jobs, boost expiry) handles it correctly with no special code.
Options
| Option | Default | Role |
|---|---|---|
maxMs | 24 h | Cap on credited time, in milliseconds |
Guards: a missing/invalid maxMs falls back to the default; negative elapsed times (clock skew, corrupted savedAt) are ignored entirely.
Choosing the cap
The cap is game design, not engineering: it decides whether sleeping is a strategy. Common choices are 2–24 h. Communicate it in-game ("You were away 3 h — production credited") so the cap feels like a feature, not a theft.
Interaction with page-lifecycle
The plugin and the page-lifecycle bridge are the two halves of away-time handling:
- Reload / cold start → the save's
savedAtpowers the catch-up (loaded). - Same-session background → page-lifecycle pauses the loop, then emits
resumewith the hidden duration.
Install both, or reloads work but backgrounding is lost (on desktop, a hidden tab's rAF is throttled, not stopped — without page-lifecycle you'd get slow, inconsistent background simulation instead of a clean pause + credit).
Limits
- No offline summary data — it advances the state but doesn't report what happened. Snapshot before/after around
engine.loadif you want a "while you were away" dialog:
ts
const before = engine.state.coins;
await engine.load(save);
const gained = engine.state.coins - before;- The cap is global — per-mechanic offline policies (e.g. "crafting continues but boosts freeze") are a game-side composition.
Related
- Offline progress recipe — the full stack.
- Page lifecycle bridge.