General
Building BlockMyBlocks: A balance game for all ages
0 views

BlockMyBlocks is out now on Web, iOS, Android. This is how it got made: the decisions, the pipeline, and what I would do differently.
What it is
A balance game for all ages. Stack the pieces on a deck that tips, hold it steady, and keep going — the levels never run out.
Stack the pieces on a deck that tips, hold it steady, and keep going — the levels never run out.
Determinism and golden tests
The sim is pure and deterministic — time and seed are parameters. That buys the strongest cheap test there is:
The catch-up law: simulate(state, a+b) === simulate(simulate(state, a), b) for arbitrary
splits. Offline catch-up, tab-restore and server ticks all depend on it. Test it directly with
quantised splits (e.g. 120 = 60+60), plus: rewards are integers, no NaN anywhere after a very
long run.
Golden files: shared/test/golden/.json holds
{seed, state0, actions[], expected: {hash per interval, final snapshot}}. The test replays the
actions and compares a stable hash of the state at every interval. Any sim change fails golden;
--update regenerates only after a human confirms the change is intended and the
PR says why. Keep scenarios small but adversarial (empty world, dense world, each subsystem
stressed, one long-horizon run).
Cross-runtime determinism: the browser smoke computes the same hashes with the same shared
sim and must match the server — catches engine/float differences before they become a
Scene architecture
Rules that keep the frame loop cheap:
State reads in useFrame go through store.getState(), never per-instance subscriptions. Selectors are narrow (useWorld(s => s.entities)).
HUD is DOM, not canvas. Everything tappable is React DOM for accessibility and crisp text; in-world labels are drei with a hard cap (~40 visible, nearest first).
Zero per-frame allocation in steady state: pre-parsed THREE.Color constants (col.copy(c), never col.set('#hex')), module-level scratch vectors, geometry rebuilds gated on a content signature, not on "something changed".
Debug handles live on window.game: stats() (draw calls, tris, dpr, tier, fps), mem() (GPU bytes by owner), setQuality, setDpr, setTimeOfDay, governor flag. Test scripts read these; nothing in gameplay
Recommended platform collections
The generic meta-game set. Add game collections beside them; keep these shapes so payments, social and live-ops code stays portable between games. All timestamps epoch-ms.
Collection: Id Fields
accounts: account id (nanoid) displayName, isGuest, providers: [{provider: 'guest'\ 'google'\ 'apple'\ 'email', subject}], email?, passwordHash?, locale, country?, createdAt, lastSeenAt, bannedAt?, bannedReason?
sessions: opaque 32-byte token (never logged) accountId, deviceId?, createdAt, expiresAt, lastUsedAt
wallets: : balance (integer ≥ 0, a cache of the ledger sum), updatedAt
wallet_ledger: : accountId, currency, delta, balanceAfter, reason ('collect'\ 'purchase'\ 'gift'\ 'event'\ 'admin'\ 'refund'…), refType?, refId?, at — append-only; the ledger is truth
purchases: : accountId, productId, provider ('stripe'\ 'play'\ 'apple'), state ('pending'\ 'granted'\ 'refunded'\ 'failed'), amountCents?, currency?, createdAt, grantedAt?, refundedAt?, rawReceiptRef?
entitlements: : key ('season_pass:', 'no_ads', 'pack:'), sourcePurchaseId?, grantedAt, expiresAt? (null = permanent), revokedAt?
leaderboard_entries: :: score, meta? (display extras, id-only), updatedAt; periodKey = 'all', ISO week '2026-W35', or an event id
flags: flag key value, segments? ({country?, minLevel?, pct?}), version, updatedAt — live-ops overrides on top of content
events: event id runtime…
Validator-per-message
Every message type — WS and REST body alike — has exactly one validator function in shared/protocol.mjs, used by the client before sending and by the server before touching state. Validators are whitelists: the only fields that exist afterwards are the ones they return.
Unknown t values and oversized frames (> 16 KB) close the socket; unknown fields are dropped; null from a validator is BAD_PAYLOAD, before any service code runs. There is never a second, looser parser
What is next
It is live at https://ddkits.github.io/block-my-blocks. If you have shipped something similar, I would like to hear what you would have cut.
Read original: https://dev.to/reallexi_llc_384b040df139/building-blockmyblocks-a-balance-game-for-all-ages-4g84
PreviousBuilding Beyond CRUD with Claude and Codex: How we Created an Interactive Digital MuseumNextBuilding DevLog: What Happens When You Tell an AI to Think Like a Security Engineer First
Related posts
Hello Tech & Marketing Community, In today's digital landscape, combining effective marketing strategies
0dev.to Tutorial
Man completes 28-year, 31,000-mile walk around world, returns to mother's house
0Hacker News New
Johnstown, PA will be your best place to live if you make under 100K
0Hacker News New
Cryosphere faces permanent damage when world overshoots 1.5C, scientists warn
0Hacker News New
Nanoparticles for delivering personalized cancer vaccines
0Hacker News New
WTF Ubuntu?
0Hacker News New