Today's update ships the Soul Shop — the core monetization and progression feature for gene46 — along with significant infrastructure work on the swipe synchronization architecture. This log explains the decisions behind each change.
1. NEW Soul Shop
Souls are the in-game currency earned by swiping. The Soul Shop provides a marketplace where players spend accumulated Souls on upgrades that enhance their gene46 experience:
- Stamina capacity expansion: Increase the maximum Stamina limit, enabling longer uninterrupted play sessions.
- Stamina recovery items: Instantly restore Stamina without waiting for auto-recovery.
- Thread slots: Unlock additional evolution thread slots (parallel lineages).
Design Philosophy: Earn, Don't Buy
gene46's economy is designed so that Souls can only be earned through play — never purchased directly. This keeps the core experience fair and ensures that progression is always a reflection of engagement, not payment.
The Stamina system creates a natural play rhythm: intensive evolution sessions consume Stamina, which auto-recovers over time. This mirrors the "energy system" common in mobile games, but with a key difference: the cost is time, not money.
2. ARCHITECTURE Bulk Swipe Sync
One of gene46's core UX requirements is offline-first responsiveness: every swipe must feel instantaneous, even on poor mobile connections. This creates a tension with data consistency — if swipes are sent to the server one-by-one, connection latency makes the experience feel sluggish.
The solution is a bulk sync architecture:
- Every swipe is immediately saved to browser localStorage as a pending action.
- The UI updates instantly (optimistic update).
- A background sync process batches all pending swipes and sends them to the server in a single
POST /api/swipe/bulkrequest. - On server acknowledgment, the pending actions are cleared from localStorage.
- If the request fails, the pending actions remain in localStorage and retry on the next sync cycle.
Why Cloudflare D1 for Persistence
gene46's backend runs entirely on Cloudflare Workers, with Cloudflare D1 (a serverless SQLite database) for persistence. D1 was chosen because:
- Zero cold-start latency from Workers (same runtime environment).
- SQLite's ACID guarantees prevent partial swipe batch writes.
- Global distribution via Cloudflare's edge network minimizes read latency for any geographic user base.
- Cost-effective at scale — D1 pricing is consumption-based with a generous free tier.
3. FIX Local Storage Persistence on Reload
A reported bug: players who reloaded mid-session occasionally lost their most recent swipe decisions. The root cause was a race condition: the reload triggered before the background sync completed, and localStorage wasn't written fast enough.
The fix: all swipe decisions are now written synchronously to localStorage before any async sync begins. This guarantees that a reload or connection loss never loses swipe history, as the pending sync queue is always persisted.
4. Stamina System Architecture
Stamina tracking has two sources of truth:
- Client-side (localStorage): Stamina is deducted locally on each swipe for instant UI feedback.
- Server-side (D1): The authoritative Stamina value is maintained on the server, synced on session start and periodically during play.
If there's a discrepancy between client and server Stamina values on sync, the server value takes precedence. This prevents Stamina manipulation by modifying localStorage values.
Auto-Recovery Calculation
Stamina auto-recovers at a fixed rate per minute. The recovery is calculated server-side as:
recovery = min(maxStamina, lastStamina + floor((now - lastSyncTime) / recoveryInterval) * recoveryAmount)
This means recovery is always correctly calculated based on elapsed time — even if the user hasn't opened the app for hours.
What's Next
- Thread Fork UX refinement — clearer visual distinction between original and forked threads.
- Generation history viewer — ability to browse and compare past generations.
- PWA installation guide (shipped August 1 — see next dev log).