🧬 SteemBiota — A No-Backend Life Simulation Built Entirely on Steem

in Steem Dev4 days ago

1000091223.jpg

Hey Steem devs! 👋

I've just released SteemBiota: Immutable Evolution — a fully on-chain life simulation game — and I wanted to share it here with a proper technical write-up, because a lot of the architectural decisions I made along the way might be interesting (or at least relatable) to anyone building on Steem.

🌐 Live app: https://puncakbukit.github.io/steembiota
📄 White paper: (attached / linked below)

The Core Premise

The question I started with: can a non-trivial game live entirely on a public blockchain with zero backend?

The answer turned out to be yes — with some interesting trade-offs.

Every creature is a Steem post. Every interaction (feed, play, walk, breed, transfer) is a Steem reply. Every rule (anti-spam, kinship checks, ownership resolution) is enforced client-side by reading the same chain data. No smart contracts. No database. No server. Just getContent, getContentReplies, and Keychain.

Stack

LayerChoiceReason
BlockchainSteemSocial post/reply model maps naturally to creature/event model
SigningSteem KeychainNo private key exposure to the app
UIVue 3 + Vue Router 4 (CDN)Zero build toolchain; ships as four plain files
HostingGitHub PagesStatic only — consistent with the no-backend constraint

No npm, no webpack, no CI pipeline. The entire app is index.html, blockchain.js, components.js, and app.js. Anyone can read the source directly in the browser.

On-Chain Data Model

All game state is stored in json_metadata.steembiota on posts and replies. A few examples:

Founder creature (top-level post):

{
  "version": "1.0",
  "type": "founder",
  "genome": { "GEN": 42, "SX": 0, "MOR": 1234, "APP": 5678,
              "ORN": 9012, "CLR": 180, "LIF": 100,
              "FRT_START": 30, "FRT_END": 70, "MUT": 1 },
  "name": "Vyrex Nymwhisper"
}

Feed reply (reply to creature post):

{
  "version": "1.0",
  "type": "feed",
  "creature": { "author": "alice", "permlink": "vyrex-nymwhisper-..." },
  "feeder": "carol",
  "food": "nectar",
  "ts": "2026-01-03T07:00:00Z"
}

Ownership transfer — a two-step handshake via transfer_offertransfer_accept replies. The effective owner is derived at read time by walking the reply tree with parseOwnershipChain(). The original post.author never changes.

Post tags are also generated dynamically from the genome — each creature post is tagged with steembiota, gaming, evolution, the genus name, the creature's first and last name, and its sex, making every creature discoverable through Steem's existing tag infrastructure.

The Interesting Engineering Problems

1. Client-side rule enforcement without smart contracts

Anti-spam (one feed/play/walk per feeder per UTC day), kinship checks (BFS ancestry walk up to 12 generations), and breed permit validation all run in the browser. A determined actor could bypass them by posting directly to the API — but any non-compliant post gets a provenance badge (⚠ Duplicate, ⚠ No Parents, etc.) that's visible to all clients reading the same data. Transparency enforces honesty rather than restriction.

2. Deterministic breeding

breedGenomes(a, b) uses a seeded PRNG (mulberry32) keyed on a hash of both parent genomes. This means the same two parents always produce the same child — verifiable by any client independently. No server needs to be trusted for the outcome.

1000091224.jpg

3. Ownership without on-chain state mutation

Since you can't mutate Steem posts, ownership is a derived concept. parseOwnershipChain() walks all replies on a creature post, finds the latest valid transfer_offer / transfer_accept handshake, and returns the effective owner. Permits issued before a completed transfer are automatically voided by comparing their timestamps against the transfer's permitsValidFrom value.

4. XP computation across six sources

XP covers founders (100), offspring (500), feeds given (10), upvotes cast on creature posts (5), unique genera contributed (25), and speciation events (75). For the leaderboard, this requires parallel per-user fetches of comment history (getDiscussionsByComments) and account vote history (getAccountVotes), cross-referenced against the corpus of known creature posts. All via Promise.allSettled to keep one slow node from blocking the whole render.

5. RPC node resilience

All API calls use a fallback chain across four public nodes (api.steemit.com, api.justyy.com, steemd.steemworld.org, api.steem.fans). A single callWithFallbackAsync wrapper handles retry logic transparently.

What I'd Do Differently

A few honest lessons from building this:

  • getDiscussionsByTag caps at 100 per call. Cursor-based pagination with start_author/start_permlink works, but it's verbose and requires careful off-by-one handling (the first result of page 2 overlaps with the last of page 1).
  • getRebloggedBy is not available on all nodes. I handle this with a .catch(() => []) but it means resteem counts silently fail on some nodes.
  • Client-side XP is eventually consistent. If a user has more than 100 recent comments or 1000 recent votes, their XP is slightly under-counted. Good enough for a game; not suitable for anything financial.

1000091225.jpg

White Paper

I've written a full white paper covering the genome system, lifecycle model, breeding mechanics, kinship rules, provenance system, ownership transfer protocol, and the full XP/rank structure. Happy to share it — drop a comment if you'd like a copy, or find it linked from the app's About page.

https://github.com/puncakbukit/steembiota/blob/main/SteemBiota_WhitePaper.pdf

Code

The full source is on GitHub: https://github.com/puncakbukit/steembiota

Happy to answer questions about any of the implementation details — the on-chain data model, the deterministic rendering pipeline, the client-side rule enforcement approach, or anything else. Always good to talk to other devs building on Steem! 🙌

@puncakbukit

Assisted by:

See also:

Sort:  

Nice idea, I like it. Besides, I never thought I’d have another child. But the urge was too strong :-)

Thanks.. :-)

Upvoted! Thank you for supporting witness @jswit.

Coin Marketplace

STEEM 0.07
TRX 0.30
JST 0.058
BTC 71454.44
ETH 2186.49
USDT 1.00
SBD 0.53