← Back to Blog 🇯🇵 日本語

Dev Log: Soul Shop, Stamina System
& Bulk Swipe Sync Architecture

Published: July 23, 2026 | Category: Development Log

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:

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:

  1. Every swipe is immediately saved to browser localStorage as a pending action.
  2. The UI updates instantly (optimistic update).
  3. A background sync process batches all pending swipes and sends them to the server in a single POST /api/swipe/bulk request.
  4. On server acknowledgment, the pending actions are cleared from localStorage.
  5. 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:

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

Related Articles