An arcade game that literally runs across the Hive blockchain. You play a Hive courier delivering blocks while real on-chain activity streams past you — live blocks, real witnesses, freshly-published posts, and the accounts you follow racing alongside you. Your real Hive activity powers your run, and you compete on an on-chain weekly leaderboard.
Story — Keepers of the Chain: every ~3 seconds the Hive seals a block, and only Couriers carry it to the next Witness before it fades. You're one of them; your standing in the Hive is your strength. A skippable intro sets it up (replay via 📖 Story), and it reframes every mechanic — the full bible is in docs/story.md.
▶ Play the live demo: https://hdev-core.github.io/hive-runner/
Built with Vite + TypeScript + PixiJS v8. 100% client-side (static site); the only "backend" is a scheduled GitHub Action that indexes scores. No gambling — every reward is skill/effort/content based.
- Features
- How it works
- The weekly contest & indexer
- Project structure
- Running locally
- Deployment
- URL parameters
- Configuration
- Tech stack
- Roadmap
- Endless runner — tap / Space / ↑ to jump; clear "blockchain block" hazards and grab coins.
- Hearts — an occasional floating heart (every ~22–38s) restores a lost life, up to the run's starting count (or a few points if you're already full).
- Levels — survive N seconds to advance; each level ramps scroll speed and spawn density, with a graceful drain → "Level complete" → next-level flow (no items cut off mid-air).
- Start / Pause / Restart / Play again — the game loads in a ready state; a contextual overlay button and a control row handle Start, Pause/Resume, and Restart (rebuilds the scene so you can preview freshly-equipped cosmetics). Tapping the canvas also works (mobile-friendly).
- Ranked ⇄ Free-play — Ranked (default) is a fair run: trail perks off, score postable to the weekly contest. Free-play turns on the equipped trail's perk (+score%, coin magnet, hit-shield, more hearts…) for a casual run that is not contest-eligible — so the leaderboard stays pure skill.
- Jump-clearable spawns — hazard spacing has a computed minimum gap so every block is always clearable, with randomized jitter so it never feels metronomic.
- Three games ship behind the same engines: Runner Dash (flagship), Fruit Rush (catcher), Meteor Dodge (dodger).
- Live block feed — polls the Hive head block every 3s, parses its real operations, and turns each new block into an in-game event: a screen pulse + a block coin whose size scales with the block's op count.
- Witness avatars — each block coin shows the block producer's profile picture (routed through a CORS image proxy for WebGL) and handle.
- Whale coins — a real large transfer (≥100 HIVE/HBD) spawns a golden coin labeled with the amount
(points awarded are shown as a green
+Nso it's never confused with "earning" that transfer).
- Billboards — real freshly-published Hive posts drift by as background signposts (author avatar + title). Once you log in, the source switches to posts from accounts you follow.
- Post-coins — collectible coins carrying a real post; grabbing one pops a clickable discovery toast that opens the post on peakd (the game auto-pauses so you don't die while reading).
- In-world ghost racers — translucent rival runners race on the track beside you, driven by real scores: your personal best (green), the leaderboard rival one rank above you (blue), and the top scorer of your selected Team pool (gold ⭐). Each ghost sits ahead by its score and drifts back as you climb; pass it and an overtake toast fires. No real score → no ghost (a neutral "Goal" line stands in only for brand-new players). Design + phases: docs/shadow-racing-spec.md.
- Community teams — the Team dropdown picks the rival pool (your follows, or a community's active members) and the community you post your score to.
- Posting a score writes a signed
custom_jsonto Hive; a scheduled indexer aggregates all scores into a weekly leaderboard (see below). - A collapsible Weekly Contest card shows the current week, a live UTC countdown, prize terms, medal-ranked standings with avatars, and your own rank.
- Keychain login — proves account ownership by signing a challenge, then persists your session (auto-restores on return). A read-only "Load" fallback works without Keychain.
- Live energy — your RC/mana %, on-chain ops in the last 24h, and today's Actifit steps are read from chain and combined into an energy score (⚡ N/15). Energy grants bonus lives, a higher jump, and a score multiplier — so being active on Hive genuinely powers your run.
- Dusk on-chain city background: Hive-red horizon glow, parallax windowed block-towers, and drifting official Hive logos.
- A dark "ledger" ground with a Hive-red rim, honeycomb ticks, and scrolling motion dashes.
- The Hive courier character: outlined runner carrying a Hive-branded parcel (a block being delivered), with a shoulder strap and running animation.
- The official Hive logo (rendered from its brand SVG, tinted/scaled) appears on the parcel, the pickup coins, drifting in the background, and as a "HIVE" watermark.
The architecture is a clean split: Hive is the live data source, a deterministic Pixi engine is the game (ghost racers included), and the DOM is just the shell (HUD, buttons, cards, toasts).
read-only public Hive nodes (JSON-RPC)
rc_api · condenser_api · block_api · bridge
│
┌──────────────────────┼───────────────────────────────┐
│ │ │ │ │
HiveFeed PostFeed HiveSocial HiveEnergy HiveAuth
(blocks) (posts) (follows/ (RC/ops/ (Keychain
│ │ communities) steps) login+post)
│ │ │ │ │
└─────► main.ts (orchestrator) ◄─────────┴─────────────┘
│
makeActivity(energy) → applyHooks(spec) → createEngine(spec)
│
┌─────────────┴─────────────┐
RunnerEngine FallingEngine
(runner) (catcher / dodger)
│
PixiJS canvas
- Spec-driven engines. Each game is a declarative
GameSpec(src/types/spec.ts) interpreted by a fixed engine — no code generation, noeval.createEngine()maps an archetype to its engine. - Activity → gameplay.
makeActivity()turns energy inputs into an energy/vitality snapshot;applyHooks()maps that to concrete modifiers (lives, jump strength, score multiplier) before a run. - Energy source. Logged in → real reads via
HiveEnergy.ts. Guest /?dev=1→ mock sliders. - Signing. Only score posts and login use Hive Keychain (active/posting stays in the extension — no keys touch this code). Everything else is read-only.
The leaderboard is on-chain and aggregated with zero dedicated server — the "indexer" is a scheduled GitHub Action that commits a JSON file the client reads.
- Submit — "Post score" broadcasts a
custom_json(idhive-runner, v0.2):{ "app": "hive-runner/0.2", "action": "score", "game": "Runner Dash", "community": "hive-1XXXXX", "score": 312, "contest": "2026-W27", "ts": 1782900000 } - Index —
.github/workflows/indexer.ymlrunsindexer/index.mjsevery ~15 min: it streams new blocks (block_api.get_block_range), extractshive-runnerscore ops, buckets each into the contest week of its block timestamp (tamper-resistant — a spoofedcontestfield can't move a score), keeps each account's best per week, and commitsdata/leaderboard.json+indexer/state.json. - Read — the client fetches
data/leaderboard.jsonvia the raw GitHub URL (CORS-enabled, ~5-min cache) and renders the standings. Indexer commits use[skip ci]anddeploy.ymlignoresdata/**, so leaderboard updates never trigger a site redeploy. - Payout — prizes are paid manually to the top scorers each week (no smart contract).
Because scores are Keychain-signed
custom_json, the leaderboard is tamper-proof by construction.
src/
main.ts orchestrator: DOM shell, login/session, energy, contest card, lifecycle
types/spec.ts GameSpec types (the declarative game format)
specs/ game specs: runnerDash, fruitRush, meteorDodge
runtime/
createEngine.ts archetype → engine factory
RunnerEngine.ts the `runner` archetype (jump physics, parcel courier, block hazards)
FallingEngine.ts the `catcher` + `dodger` archetypes (CatcherEngine.ts re-exports it)
engineState.ts shared EngineState
Background.ts themed parallax backdrop (block-city + Hive hexes/logos)
hiveLogo.ts official Hive logo, rendered from brand SVG (tintable/scalable)
avatar.ts witness/author avatars via CORS image proxy
hooks.ts maps activity → gameplay modifiers
activity/mockActivity.ts hybrid energy formula (+ guest/dev fallback inputs)
hive/
HiveFeed.ts live head-block poller + op parser
PostFeed.ts real-post stream (firehose / your feed) for billboards + post-coins
HiveSocial.ts follows / community members → rival pool; communities → teams
HiveEnergy.ts real energy reads (RC/mana, 24h ops, Actifit steps)
HiveAuth.ts Keychain login + score custom_json
contest.ts contest config + ISO-week/countdown helpers
indexer/
index.mjs block-streaming score indexer (runs in CI)
state.json checkpoint + best-score-per-week map (committed by CI)
data/leaderboard.json public standings (committed by CI, fetched by the client)
.github/workflows/
deploy.yml build + deploy to GitHub Pages on push to main
indexer.yml scheduled contest indexer (cron */15m)
Requires Node 20+.
npm install
npm run dev # Vite dev server (hot reload)
# or
npm run build # type-check + production bundle → dist/
npm run preview # serve the production buildRun the indexer once locally (streams the live chain, writes data/leaderboard.json):
node indexer/index.mjs
⚠️ Verify production behavior in a real/headless browser, not justcurl— a top-levelawaiton Pixi init works in dev but hangs in the prod bundle, andcurlreturns 200 even when the canvas is blank. Boot is a fire-and-forget async function for exactly this reason.
Pushing to main triggers deploy.yml → builds and publishes to GitHub Pages. vite.config.ts
sets base: "./" so it works from a subpath. The contest indexer runs on its own schedule and commits
data without redeploying the site.
| Param | Effect |
|---|---|
?hive=<user> |
Auto-load a Hive account (overrides the saved session) — handy for sharing/verification |
?dev=1 |
Show the mock-energy sliders panel and expand the contest card |
?play=1 |
Skip the Start overlay and auto-start a run |
?pt=1 |
Preview the post-discovery toast |
?lb=<url> |
Override the leaderboard data source (point at a staging/test file) |
- Contest — prize text, top-N, and the leaderboard data URL live in
src/contest.ts(CONTEST). - Hive nodes — each
hive/*.tsmodule lists public API nodes and rotates on failure. - Energy formula — caps and ratios (RC baseline, ops-per-energy, steps-per-energy) live in
src/activity/mockActivity.ts. - Images — avatars/logos are proxied through
wsrv.nlfor WebGL CORS. For production you should self-host a CORS image proxy rather than depend on a third party.
- PixiJS v8 — WebGL rendering (async
Application.init,Graphics, SVG,GraphicsContextsharing, ticker loop). - Vite 6 + TypeScript 5 (strict,
esnexttarget for top-level await support). - Hive — public JSON-RPC (
rc_api,condenser_api,block_api,bridge), Hive Keychain for signing,custom_jsonas a free on-chain event log. - GitHub Actions + Pages — CI build/deploy and the scheduled score indexer.
Making it genuinely lucrative to Hive users requires a real value inflow (reward pool, sponsors, or a token market) — all skill/effort/content based, never wagering. Planned, in order:
- Sponsor-funded contests — data-driven contest config with sponsor slots (logo in-game, funded pot, daily + weekly windows); sponsorships are the sustainable inflow. (scores already indexed)
- Daily streaks + quests — consecutive-day play and simple on-chain quests with escalating rewards.
- Referrals + team pots — referral credit in the score op; community-team pooled leaderboards.
- Delegation-for-boosts — delegate HP for in-game perks; that stake also powers curation payouts.
Also considered: play-to-post (finishing posts an earning run-card) and curation-through-play (upvote the posts you collect).
Not affiliated with an official Hive team. "Hive" and the Hive logo belong to the Hive community.