Documentation · v0.1

Rug Patrol

A browser pixel game about catching a serial rug deployer, and the $HUNT token that lets you play it, stake into it, and put your own money behind an accusation. This document covers the whole system — the parts that are live today and the parts that are still design.

01

Overview

Rug Patrol is two things that share a premise. The first is a game: a top-down pixel town where somebody in the crowd is deploying tokens, pumping them and pulling the liquidity. You gather evidence, work out which passer-by he is, tackle him and file a report.

The second is the thing the game is a rehearsal for — a public, staked accusation registry for Robinhood Chain, where reporting a rug costs something and being wrong costs more. The token ties them together.

What is live right now

  • The game — complete, playable, free, no wallet required.
  • The site and these docs — including wallet connect for Robinhood Chain mainnet.
  • $HUNT in-game — earned and staked inside a round, reset each round.

What is not live yet

  • The $HUNT contract. Not deployed. There is no address to buy, and anything claiming otherwise is not ours.
  • On-chain staking. The tiers below describe intended design, not a shipped contract.
  • The report registry. Specified here, not written.

Read that twice. Everything on-chain is design, not deployment. The only thing you can do today is play. When the token exists, its address will appear on the home page and here, and it will be verified on the public explorer before it is announced anywhere.

02

Connect to Robinhood Chain

The Connect button in the header opens a menu with three options: any EIP-1193 browser wallet (MetaMask, Rabby, Frame — listed by name through EIP-6963 discovery, so several wallets never race each other), WalletConnect for phone wallets, and Sign in with X. See Online, wallets & X for what each is for and how to enable the last two.

On connect it requests accounts, checks the chain, and if you are somewhere else it asks your wallet to switch. If your wallet has never heard of Robinhood Chain it offers to add it using the parameters below.

Mainnet

FieldValue
Network nameRobinhood Chain
Chain ID4663 (0x1237)
RPC URLhttps://rpc.mainnet.chain.robinhood.com
WebSocketwss://feed.mainnet.chain.robinhood.com
CurrencyETH · 18 decimals
Explorerhttps://robinhoodchain.blockscout.com

Testnet

FieldValue
Network nameRobinhood Chain Testnet
Chain ID46630 (0xB626)
RPC URLhttps://rpc.testnet.chain.robinhood.com
Explorerhttps://explorer.testnet.chain.robinhood.com

The site targets mainnet. Testnet values are exported from RH.Chain.TESTNET if you want to point a fork at it. Official network documentation lives at docs.robinhood.com/chain.

Disconnect is a local concept. EIP-1193 has no real disconnect — clicking a connected button clears our side, but your wallet still lists the site under its own permissions. Revoke it there if you want it gone properly.

03

Run it locally

There is no build step, no bundler and no Node.js requirement. The whole project is static files with classic <script> tags, which means it also runs by double-clicking index.html.

A local server is still the better way to work — some browsers restrict things over file://, and it matches how it will be hosted. A dependency-free server ships in the repo:

powershell -ExecutionPolicy Bypass -File .\serve.ps1

  root  ...\game robinhood
  url   http://127.0.0.1:8123/
  stop  Ctrl+C

Pass -Port 8080 to move it. It uses TcpListener rather than HttpListener so it needs no administrator URL reservation.

Deploying

The game is static files; only Sign in with X needs PHP. On Spaceship (or any cPanel host) run set-domain.ps1 yourdomain, then make-upload.ps1, and upload release/rug-patrol-upload.zip into public_html with File Manager → Extract. The step-by-step guide is DEPLOY.md in the project folder.

File map

PathWhat it is
index.htmlLanding page and the game's DOM shell
docs.htmlThis page
assets/css/base.cssDesign tokens, reset, buttons, primitives
assets/css/site.cssNav, hero, every landing section, footer
assets/css/game.cssHUD, dialogue box, creator, touch pad
assets/css/docs.cssDocumentation layout
assets/js/chain.jsRobinhood Chain wallet connection
assets/js/site.jsNav, reveals, hero snow, portraits, toasts
assets/js/game/font.js3×5 bitmap font used for every sign in the world
assets/js/game/sprites.jsCharacters, tiles and street props
assets/js/game/buildings.jsComposed building sprites and their doors
assets/js/game/interiors.jsThe four walk-in rooms and their collision
assets/js/game/world.jsTown layout, terraces, reachability, A* pathfinding, the baked ground layer
assets/js/game/tokens.jsTickers, vanity addresses, live prices, kiosks, The Tape, the contract inspector
assets/js/game/crowd.jsTraders, custom victims, Oracle and his calls, speech bubbles
assets/js/game/feed.jsStats bar, town feed and the trader roster
assets/js/game/markets.jsThe ODDS booth's three prediction markets
assets/js/game/agent.jsThe agentic scanner drone and its guardrails
assets/js/game/pnl.jsEnd-of-round PnL card, rendered in canvas
assets/css/feed.cssStats bar, feed, roster, ODDS, agent and PnL styles
assets/css/menu.cssPause menu, case picker, tutorial coach, quick chat, Connect menu
assets/js/config.jsThe one file you edit to deploy — WalletConnect id, X app, relay, RPC
assets/js/account.jsThe Connect menu: browser wallets, WalletConnect, Sign in with X
assets/js/walletconnect.jsLoads WalletConnect's provider on demand and points it at chain 4663
assets/js/xauth.jsSign in with X: OAuth 2.0 PKCE in the page, token exchange on the server
assets/js/net/mqtt.jsA small MQTT 3.1.1 client over WebSocket, no dependencies
api/x-token.phpPHP endpoint that swaps the X code for a handle, then revokes the token; reads its keys from api/x-secrets.php on the server
.htaccessHTTPS, www redirect, security headers, caching and blocked private files on Apache/LiteSpeed hosting
make-upload.ps1Builds release/rug-patrol-upload.zip for Spaceship's File Manager
set-domain.ps1Writes your domain into share previews, sitemap and config
assets/js/game/settings.jsSettings and key bindings; profile, ranks, unlocks, achievements, daily records
assets/js/game/audio.jsEvery sound and all music, synthesised with Web Audio
assets/js/game/fx.jsScreen shake, hit-stop, flashes, particles, the cuffs cinematic
assets/js/game/lineup.jsThe disguised deployer, the crowd, clues, accusations, the case file
assets/js/game/catch.jsThe tackle, stamina, cornering, the bridge escape, the Intern
assets/js/game/town.jsThe Chain Line subway; Dr. Vale, Pip Hart and Marta Kovac
assets/js/game/tutorial.jsThe staged first round and its coach card
assets/js/game/menu.jsCase picker, pause menu tabs, end-of-round progression
assets/js/game/net.jsOnline hunters, quick chat, the daily board
assets/js/game/dialogue.jsDialogue widget and the full script
assets/js/game/game.jsEngine: input, entities, AI, camera, round loop
dev/sprites.htmlLive sprite inspector for tweaking palettes
serve.ps1Zero-dependency local server
04

Controls

Click the town once to take control. The game only captures the keyboard while it has focus, so arrow keys keep scrolling the page everywhere else. Click away and it pauses.

Walk. WASD does the same.
Space
Talk, scan a contract, open a door, ride the subway, confirm a choice — and, when the named deployer is in reach, tackle.
C
Open the case file: the details Forensics has on him and how many people still match.
Esc / P
Pause menu — settings, controls, profile, daily board, online, account. Esc also backs out of a conversation.
T
Quick chat (online only): eight preset lines, picked with 18.
M
Mute.
Doors
Five landmarks open. Walk onto the mat by the EXIT sign inside to come back out.
Steps
The terrace wall is solid except at the step gaps — that is how you move between the shopfronts and the street.
Touch
A d-pad and ACT / ESC buttons appear on coarse pointers; force them on or off in Settings. The HUD has II, CASE and CHAT buttons.

Every key is rebindable under Pause → Controls, and the game pauses when it loses focus (switchable in Settings).

Reading the screen

  • Top left — your $HUNT, three evidence pips, and the liquidity pool meter. The meter turns amber below 55% and pulses red below 25%.
  • Top right — the district you are standing in, and the current objective.
  • Red glow — an unscanned rug contract. It is bleeding the pool for as long as you ignore it.
  • Edge arrows — small red ones point at contracts; a large one points at whoever you currently need.
  • Minimap — one pixel per tile, so the whole town fits. White is you, the box is your current view, green and blue are allies, red is an unscanned contract, green squares are subway stations, cyan dots are other hunters. The crowd is never on it, and the deployer only appears once he has been named. Elon's intel draws a red ring, not a dot.
05

Anatomy of a round

A round is a race between two clocks. One is how fast you can find and scan contracts. The other is how fast the contracts you have not found are draining the pool. There is no timer — the pool is the timer.

PhaseObjectiveWhat changes
BriefingFind Vlad and accept the contractNothing is draining yet. Explore freely.
HuntingThree pieces of evidenceHe changes clothes. Rugs and copycats appear near him; each piece of evidence reveals one detail of his outfit.
LineupFind and name himTalk to passers-by; the dialogue checks each against the case file. Accuse the one who matches.
ChaseTackle himHood up, he runs at 52 px/s from 132px. In the open a tackle only costs him stamina; cornered, it ends it. After a while he runs for the L1 bridge.
CaughtWalk him to Officer HaleDraining stops. The catch pays 700 $HUNT.
Won / LostReport filed for the bounty, or the pool hit zero. Either way the round pays XP.

The numbers

ValueDefaultNotes
Pool100%Round is lost at zero.
Drain per live contract0.62 %/sFive at once is 3.1 %/s — about half a minute to disaster.
Your speed50 px/sSlower than a fleeing Bad Actor on purpose.
Scan radius26 px45.5 px once the scanner is staked.
Scan reward220 $HUNTPaid per contract, whether or not it counts toward the three.
Intel cooldown26 sElon will not post again until the timeline resets.

The core tension: you cannot outrun the Bad Actor with starting kit, and a tackle in the open only makes him slip. Rounds are won by steering him into the canal, a wall, a dead-end alley — or onto the bridge deck after Marta has locked the gate. Staking for boots takes you to 66 px/s, which buys you the angle, not the catch.

06

The cast

Four characters spawn every round. Three are systems you interact with; the fourth is the target.

CharacterRoleWhat they actually do
Vlad TenevChain founder Issues the contract that starts the round, pays scan and catch bounties, and takes your $HUNT stake for the three upgrades.
Elon MuskX founder Broadcasts to the timeline and returns the district the Bad Actor is currently in, plus a 14-second marker. 26-second cooldown.
Officer HaleChain enforcement Will not act without three linked artefacts. Accepts the filed report, which is what releases the bounty.
The Bad ActorSerial rug deployer Changes clothes when the hunt starts and walks like everyone else, planting contracts near himself. Once named, pulls the hood up and runs.
Dr. Ines ValeChain Forensics Turns evidence into details of his outfit. Rushes the next detail for 300 $HUNT. Inside the lab is the case file terminal.
Pip HartThe Gazette A free rumour every 35 seconds — right seven times in ten, labelled unverified in the case file. Prints headlines in the feed.
Marta KovacBridge keeper Locks the L1 gate for 300 $HUNT staked, which turns his escape route into a dead end.
The InternAccomplice Chain Warden difficulty only. A green hood who plants copycats. One tackle, +400.
Passers-byThe crowd 6–17 ordinary people. Most share one or two details with him; nobody shares all three.

All of the characters added with the Harbor are fictional and are not based on real people.

The town has thirteen named districts — Listings Row, The Shopfronts, Main Street, Exchange Square, The Cold Storage, Liquidity Alley, The Dead End, Forensics Row, Launch District, Container Yard, The Harbor and the L1 Bridge — and Elon's intel draws a ring around part of one.

Walking in

Each ally stands outside their own front door, and each door opens:

BuildingWho is insideConsole
Robinhood HQVlad TenevStaking terminal — the same three upgrades Vlad sells outside
X HQElon MuskTimeline feed — posts the intel request, same 26-second cooldown
Precinct 46Officer HaleCase board — evidence on file, pool integrity, bounty on release
The Rug Housenobody, deliberatelyDeploy rig — counts as one piece of evidence, and unlike the street contracts it cannot run away
Chain ForensicsDr. Ines ValeCase file terminal — the details on file and how many people still match them

Nothing is locked behind going indoors. Every mechanic is reachable from the street as well, so exploring is rewarded rather than required — except the deploy rig, which is the one free piece of evidence in the game and the reason to visit the worst house in town.

07

Copycats, traders & Oracle

Three kinds of contract

KioskWhat it isWhat to do
RedA rug — a fresh launch whose deployer already pulled liquidity. Drains the pool while live.Inspect and report. One piece of evidence, +220.
Green (listed)A real token in the Exchange registry. Four stand under The Tape all round.Leave it. Reporting it is a false report: −350, slashed.
Green (copycat)Same ticker, same first and last four address characters as a listed token. Pixel-identical kiosk.Expand the full address in the inspector, compare to the registry, report it. One piece of evidence, +260.

The copycat mechanic is how vanity-address copies work on real chains: the attacker grinds an address that shares the prefix and suffix people glance at. In the inspector both addresses read identically until you press E to expand them — then the middle 32 characters are completely different.

The traders

Ansem, Cented, Decu and Cupsey stand at The Trenches — the fire barrel in Exchange Square — plus up to four victims you add yourself from the roster under the game. Each has a bag and a temper: how likely they are to follow a call. When one walks to a contract:

  • Reach them first and tell them to stop: saved, +120. Warn someone off a real token and they just miss the gain.
  • Delist the contract before they arrive: they turn back, and it counts as saved.
  • They arrive while it is live: rekt. They lose 55–85% of the bag and the pool drops 6%.

Traders walk real paths across the terraces (A* over the tile grid), at 36 px/s against your 50, so there is always time if you move.

How real names are handled. The four default traders are parody cameos of well-known meme traders, cast as the people you protect. The game never writes posts or quotes under their names: in the world they say one-word bubbles, and everything in the feed about them is third-person narration. Nobody named has endorsed the game.

Oracle

A fictional KOL, @100xOracle, with a Telegram channel called Oracle Alpha Calls. Every 24–33 seconds during the hunt he posts a call: roughly half copycats, three in ten rugs, the rest real listings. It lands 220–560px from the fire barrel, appears in the feed as a post plus a Telegram mirror with the contract address, pumps the ticker on The Tape, and sends one or two traders walking. With two pieces of evidence you can confront him and show the payment trail — the calls stop and you take +500.

08

The lineup, the tackle & the bridge

Catching the deployer used to be a line of dialogue. Now it is two things you do with your own eyes and feet: pick him out of a crowd, then bring him down somewhere he cannot run.

Picking him out

The moment you take the contract he changes out of the hoodie. Every piece of evidence gives Chain Forensics one detail: his coat, his headwear or his hair, in an order that is fixed for the round. Walk up to anyone and press Space — the dialogue describes them and checks them against every detail on file.

Details on fileTypical number of matches
0Everyone. You cannot accuse yet.
1About two thirds of the crowd
2A shortlist of three or four
3Exactly one

The crowd is generated against the clue order, so every detail does real work. Accuse on fewer than three details and you are profiling: right pays +400 and closes the case file on the spot; wrong is a false accusation, −250, and that person is cleared. Dr. Vale will rush the next detail for 300 $HUNT, which does not count as evidence. Pip Hart's rumours are free and wrong three times in ten.

Bringing him down

  • Lunge. Within reach, Space becomes TACKLE. Miss and you stumble for 0.4 s.
  • In the open he twists free and loses a point of stamina (the red pips over his head). Hunter has 2, Marshal and Warden 3, Rookie and the tutorial 1. Let him get 220 px away for nine seconds and he gets a point back.
  • Cornered — at most one clear direction that is not straight at you, two tiles deep — a single tackle ends it. Walls, the canal, dead-end alleys and the bridge deck all count.
  • The cuffs — hit-stop, a closing ring, the click. The catch pays 700.

The L1 bridge

Named and running, after 24–50 seconds depending on difficulty he makes for the drawbridge at the east end of the Harbor. If the gate is open he crosses, the pool loses 15%, and he walks back into town somewhere far away in a completely new outfit — the case file refills with the new details at once, but you have to find him again. If Marta Kovac has locked the gate, the deck is a dead end: he is trapped, his stamina drops to one, and the next tackle takes him.

The Chain Line

The town is 168 × 84 tiles, so there is a subway. Six stations — Canal West, Exchange Square, Forensics Row, Liquidity Alley, The Dead End and The Harbor. Press Space at the stairs, pick a stop, and four seconds of game time pass while you ride. The escorted deployer comes with you.

09

Tutorial, daily cases & ranks

Choosing a case

ModeWhat it is
TutorialA staged first round: one rug, one copycat, one call, the case file, the lineup brought to you, the tackle, the report. Nothing random spawns, the pool drains at 35%. Offered first until you finish or skip it.
Daily caseEvery system that rolls dice — deploys, calls, the market, the disguise and the crowd — is seeded from the UTC date, with a separate stream per system so your choices in one do not reshuffle another. Always Hunter difficulty. Your first attempt of the day is the ranked one; replays only improve your personal best.
PracticeA random case at the difficulty you choose. Consecutive wins add heat (up to 3): +8% drain, one more person in the crowd and 6% faster calls per level, and +10% XP.

Difficulty

LevelDrainCallsCrowdStaminaXP
Rookie×0.70×1.25 gap61×0.8
Hunter×1.00×1.0092×1.0
Marshal×1.25×0.85123×1.3
Chain Warden×1.45×0.72143×1.6, plus the Intern

Ranks, unlocks, achievements

A round pays 50 + 150 for a win + 20 per trader saved + 25 per rug or copycat + 100 for exposing Oracle − 30 per false report − 40 per wrong accusation, times the difficulty multiplier and heat, never less than 10. A daily's ranked attempt adds 50%. Seventeen achievements add their own XP once.

Ranks run Rookie → Patrol (300) → Detective (900) → Sergeant (2,000) → Inspector (3,800) → Chief (6,500) → Chain Warden (10,000). Each rank from Patrol up unlocks one cosmetic in the character creator: the fedora, a gold coat, a dispatch headset, a top hat, a chain-green coat and the Warden crown. Unlocks are cosmetic only. Everything lives in this browser's local storage and is re-validated on load.

Your rank and the case type are printed on the PnL card, and the share text says Daily Case #N so results from the same day can be compared.

10

Online, wallets & X

Other hunters

Turn on Online in the case picker or the pause menu and you share your hunter name, rank, look, position and quick-chat through a public MQTT relay. Other players appear walking the same streets with name tags, on the minimap, and in the feed when they catch their deployer. There is no free text — quick chat is eight fixed lines — so there is nothing to moderate.

  • Rooms: the public town, today's daily case (everyone on the same seed), or a private room code.
  • Rounds stay yours. Each player simulates their own case; what travels is presence.
  • The daily board is one retained message per player per day, posted after your ranked attempt. It is self-reported and labelled unverified.
  • Everything received is untrusted: names are reduced to a safe alphabet, numbers clamped, colours must be hex, senders are rate-limited, and nothing is written to the page as HTML.

Two tabs on the same machine also see each other through a BroadcastChannel, which is the quickest way to try it. For a real launch, point netBrokers in assets/js/config.js at a broker you run and restrict the topic prefix.

Connect menu

OptionWhat it is forTo enable
Browser walletStaking and the registry once they ship. Every EIP-6963 wallet is listed by name.Works out of the box.
Robinhood Wallet & phone walletsRobinhood Wallet is a phone app: on a computer it connects by scanning a WalletConnect QR code; inside Robinhood Wallet's own browser the page connects directly as a browser wallet. Loads @walletconnect/ethereum-provider 2.24.0 on demand.Free project id from cloud.reown.com → walletConnectProjectId; add your domain to the project allowlist.
Sign in with XPuts your real handle on the PnL card and board. Read-only scopes; nothing is posted.An X app with OAuth 2.0 → xClientId; on the server, fill in api/x-secrets.php.

api/x-token.php runs on any PHP 7.4+ host, including Spaceship's cPanel hosting. Copy api/x-secrets.example.php to api/x-secrets.php on the server and fill in the client id, client secret and allowed_redirects (the exact callback URL, which must also be registered in the X portal). That file is never committed and .htaccess refuses to serve it. The endpoint exchanges the code, reads the handle, revokes the token and returns only { username, name, avatar } — no token reaches the browser.

The stats bar reads the live block height from the public RPC; nothing about your wallet is sent anywhere.

11

Feed, ODDS, agents & PnL

The page around the game

A live stats bar sits above the stage (phase, block, traders, calls, rekt, saved, rugs, copycats, clock), the feed sits beside it, and the roster sits under it with every trader's bag and state updating a few times a second. The layout borrows from living-world sites where the town talks in a feed and you can watch every resident's balance — because a copycat only matters once you watch a bag drop 70%.

The ODDS booth

MarketSettles when
Bad Actor caught before the pool drops below 50%?On the catch (YES if the pool is ≥ 50%) or the moment it dips below 50% (NO).
Which trader gets rekt next?On the next rekt. Reopens straight away. "Nobody" wins if the round is won first.
Is Oracle's next call a copycat?On the next call. Reopens. Closes if Oracle is exposed.

Stakes of 50, 150 or 400. Shares pay 1 $HUNT each if right, locked at the price when bought. If you win a bet on a trader getting rekt, it is written into the case file and printed on your PnL card as BET ON A VICTIM. That is on purpose.

Agentic account

From Vlad or the staking terminal. Deploying stakes 600 $HUNT and commits a fee budget of 200, 400 or 800; each action costs 40. You set three guardrails:

  • Scope — rugs only (never files a false report), or also rule on copycats (faster, but misreads one address in six, and a wrong call on a real token is slashed from your stake).
  • Budget — how many actions before it stops.
  • Range — how far from you it goes looking.

In rugs-only mode it still warns you when one ticker has two live contracts nearby, without saying which is fake.

The PnL card

Every round starts at 1,000 in-game $HUNT. At the end, open markets settle, then: position = balance + staked (×0.6 if the pool drained), and PnL = position − 1,000. The card is drawn in canvas — case ticker, the big number, PnL %, entry and position, traders saved and rekt, copycats and rugs, flags, your pixel character, and the X handle from the character creator — and can be downloaded, copied to the clipboard, or posted to X with a prefilled message.

The report bounty is no longer flat: 1,800 + 150 × saved − 100 × rekt, never below 600.

12

$HUNT

One token doing three jobs. Nothing about it is decorative.

JobWhat it gates
PlayRanked rounds, seasons and leaderboard entry. Practice rounds are free forever and always will be.
StakeScanner range, movement speed and pool reinforcement. Staked, not spent — unstake and the capability goes with it.
ReportFiling a rug report requires a stake. Accepted reports return it plus a bounty; false ones are slashed.

Supply

1,000,000,000 $HUNT, fixed, no mint function.

AllocationShareTerms
Liquidity40%Added and locked at launch
Bounty & reward treasury25%Funds catch and report bounties
Staking emissions15%Released over 24 months
Development12%12-month vest
Community & partnerships8%Unlocked, used for sponsored districts and events

Ticker collision. A separate Robinhood Chain project, stockhunt.fun, already uses the ticker $HUNT. Tickers are not unique on any EVM chain, so both can exist — but check the contract address, never the symbol, before you interact with anything.

13

Staking

Staking here is not a yield box. Every tier unlocks something you feel while playing, and unstaking takes it back the same block.

TierStakedUnlocks
Bystander0Free practice rounds, full city, full cast. No leaderboard.
Hunter25,000Ranked rounds, leaderboard, scanner range +75%, one free report stake per week.
Marshal150,000Movement +32%, pool reinforcement, double vote weight in registry disputes.
Chain Warden750,000Share of bounty treasury flow, and the right to propose and sponsor a city district.

Not final. Thresholds and the emission schedule are current design. They are not committed until the staking contract is written, audited and deployed, and they describe intent rather than any promise of return.

14

Rug report registry

The in-game report is a rehearsal. The real one is a public accusation log for Robinhood Chain that does not decide who is guilty — it decides who was willing to put money on being right.

Lifecycle

  1. File. Submit token address, deployer wallet and the withdrawal transaction, with a stake attached.
  2. Challenge. 72 hours in which anyone, including the accused, can stake against the report with counter-evidence.
  3. Resolve. Weighted by staked $HUNT. Marshals and Wardens carry more weight because they have more at risk.
  4. Settle. The winning side splits the losing side's stake. An accepted report also draws a bounty from the treasury.

Reporting is expensive on purpose. A registry that costs nothing to write to becomes a harassment tool inside a week. Making it expensive to be confidently wrong is the only part that makes the output worth reading.

The registry is intended to be readable by anything — a small read-only widget any Robinhood Chain project can embed to show whether a token has open reports against it. That widget is Phase 03.

15

Anti-abuse

Three separate problems, three separate answers.

Griefing the registry

Cost, challenge window and slashing, as above. There is no free write path, and no path where a losing accuser keeps their stake.

Farming the game for tokens

Practice rounds pay nothing on-chain — the $HUNT you earn in a round is round-local score and resets. Only ranked rounds, gated behind a stake, will ever touch the treasury, and the stake is what makes botting a losing trade.

Sybil identities

Vote weight in disputes is a function of staked balance, not wallet count. Splitting a stake across a hundred wallets gets you the same weight as holding it in one, minus a hundred lots of gas.

Honest limitation: none of this stops a well-funded actor from being wrong loudly and expensively. It only makes it cost them. Treat the registry as a signal with a price attached, not as a verdict.

16

How the engine works

There is not a single image file in this project. Every pixel is generated by the page at load time, which is why the whole thing is a few hundred kilobytes of text.

Characters

A character is three ASCII templates plus a palette. BODY holds rows 0–11 — head and shoulders, one grid per facing. LIMBS holds rows 12–23 — arms and legs, three animation frames per facing. Left is the right-facing sprite mirrored. A new character costs about twelve lines of colour.

'....hssssssh....'   h hair   s skin
'....sseesees....'   e eye
'......wwww......'   w collar
'...cccccccccc...'   c coat   d coat shadow
'..ddaaaaaaaadd..'   a accent (hi-vis band / badge / logo)

Hats are separate overlays painted on rows 0–6, so any character can wear any of beanie, hardhat, cap, hood, cowboy or none. Chest emblems work the same way on rows 12–17.

The 1px outline is computed, not drawn: the sprite is rendered, its silhouette expanded by one pixel in the four-neighbourhood, and the expansion filled dark. That is why every character and every hat gets a consistent outline for free.

Coat shadow adapts to the palette. Subtracting brightness from a near-black coat gives you another near-black and the arms disappear into the torso, so below 0.3 luma the shadow brightens instead and reads as a rim light. Every template row is length-checked at boot — a miscounted row would otherwise silently shear the sprite.

Buildings

Buildings are not made of tiles. Each one is composed at load time into its own canvas out of architectural primitives — clapboard, brick, stoneCourse, cornice, gable, sashWindow, archWindow, doorway, awning, signPlate, snowLedge — and then placed in the world as one tall sprite with a solid footprint, a door, and its own light sources.

Shop signs are set in a 3×5 bitmap font written for the project (font.js), which auto-sizes to fit its plate. That is why the fronts read ROBINHOOD, PRECINCT 46 and CINEMA rather than carrying a blurry texture.

Adding a building is a function that draws it plus a returned { tw, th, door, interior, lights }. The three filler types are parametric, so a new shop is a palette and a sign string.

Interiors

Four landmarks open. A room is exactly one screen — 24×13 tiles, 384×208 — so there is no camera scroll indoors. Each is baked to a single canvas, and its collision grid is derived from the furniture rectangles as they are drawn, so nobody hand-authors a mask.

The town and every room expose the same interface — solid(tx,ty), open, pxW, pxH — so movement, collision and the camera never branch on which one you are standing in. Only rendering does.

The town

120×84 tiles at 16px — 1920×1344 pixels — laid out in bands the way a real street section is: a row of buildings on a raised pavement, a retaining wall with step gaps, the lower street, the frozen canal, then the next terrace. That vertical layering is what stops a top-down town reading as a flat carpet.

The whole ground layer is baked once into an offscreen canvas, so the per-frame ground pass is a single drawImage instead of thousands of tile blits. Props, buildings and characters are depth-sorted by their feet and culled to the viewport before drawing.

Making a bounded map feel bounded

The first version of this town reused seven building types across thirty-two plots, and the streets ran off the last tile into nothing. It was bounded, but it read as an endlessly repeating street. Three things fix that:

  • 26 distinct fronts. Filler is drawn from a shuffled bag and only reshuffled once the bag is empty, so two identical shopfronts are never in view together.
  • A treeline. Four tiles of impassable spruce wrap all four edges, so walking outward ends at a wall of forest instead of trailing off.
  • A minimap. One pixel per tile puts the entire 120×84 town, its boundary and your position in it into the corner of the HUD.

Lighting

A night pass fills a scratch canvas with cold blue, punches holes at every lamp with destination-out radial gradients, and composites it over the scene. Warm glow is then added back additively, and unscanned contracts throb red so they read from across a block.

Performance

The game renders at 384×216 internally and is scaled up by CSS with image-rendering: pixelated, so it costs the same on a 4K monitor as on a laptop. It pauses via IntersectionObserver when scrolled out of view, and the cast portraits on the home page do the same.

17

Customising the game

Recolour a character

Edit RH.Sprites.CAST in assets/js/game/sprites.js and reload. Open dev/sprites.html to see every character, tile and prop rendered live at 3× and 6× — that page reads straight from the same file, so it is the fastest way to judge a palette.

Add a fifth character

  1. Add an entry to CAST with a palette, a hat and an accentStyle.
  2. Add a spawn in Game.prototype.newRound in game.js.
  3. Add a script function under the same id in RH.Script in dialogue.js.

The script function receives the live game state and returns { lines, choices }. Choice actions are strings the engine switches on in Game.prototype.onChoice.

Retune the difficulty

Everything lives in one object — the this.G = { … } block in newRound. drainPerTerminal, scanRange, cost and reward are all there.

Change the city

world.js takes a seed. Change the number passed in buildWorld and you get a different town with the same road skeleton; edit the blocks array to change the skeleton itself.

18

Status & roadmap

PhaseContentsStatus
00 · Cold OpenEngine, city, cast, round loop, creator, site, docs, wallet connect Shipped
01 · The Token$HUNT deployed and verified on 4663, liquidity locked, balance read from the wallet In progress
02 · StakingAudited staking contract, tiers bound on-chain, ranked rounds and leaderboard Next
03 · The RegistryOn-chain reports, challenge window, slashing, treasury, embeddable widget Planned
04 · Open CityShared rounds, seasonal cities, sponsored districts, play as the Bad Actor Exploring