un-gameable geo-prediction game · live

GeoCast

Predict where Earth will quake next.

One un-gameable question. One pin. Closest to the next M5+ epicentre wins.

SIWE · no email, no password, no tracking

scroll · how it works ↓

How it works

One question, every round. Un-gameable. One pin.

for example —

01

Sign in with your wallet

SIWE: sign a one-time message. No password, no email, no tracking. Your address IS your account.

02

Drop one pin during the 24h

The map is open for 24 hours. Click anywhere to commit your prediction. One USDC, one pin, no take-backs. You can't see other players' pins until you've committed.

03

Wait for the next quake

After the round closes, the live USGS feed is watched in real time. The first M5+ earthquake to strike anywhere on Earth is the truth — and the answer is impossible to know at commit time.

04

Closest pin wins biggest

Haversine distance to the epicentre, ranked. The pool splits by inverse distance — closest pin takes the largest share, but even a 5,000 km miss earns a sliver. Settled on-chain.

Why GeoCast can't be gamed

The answer hasn't happened yet
when you commit your pin.

Most prediction games pick an answer that's already determined — the hottest city is in tomorrow's forecast, the biggest wildfire is already burning. GeoCast doesn't. The round opens, the map is open for 24 hours, then the round closes — and only THEN does the clock start on the question. The answer is the first M5+ earthquake to strike anywhere on Earth after that moment.

Earthquakes are the canonical un-predictable natural event — the USGS itself has spent 60+ years failing to forecast them. No API watching helps. No "wait until hour 23 and check what already happened" trick. Reading the resolver source code (it's open) just tells you the rules — not the answer. Pure prediction. Pure skill.

Resolution pipeline

  1. 01

    Round closes

    No more pins accepted

  2. 02

    Clock starts

    Live USGS feed watched in real time

  3. 03

    First M5+ hits

    Epicentre coords become the truth

  4. 04

    Payout

    Haversine rank → pool split → on-chain

The resolver framework is open-source — every question template is a single PHP class that implements ResolverInterface with a suggest() and a resolve() method. You can read exactly what query is being made, with what parameters, to which endpoint.

The algorithm · end to end

Every step, open and verifiable.

The cron pipeline below runs on a 1-minute heartbeat. No part of this is private — the resolver source code, the USGS endpoint, the on-chain contract, and the Merkle tree builder are all open. Anyone can replay the exact API calls that decided their round.

Round lifecycle

T+0h

Round opens

cron creates the next round + mirrors on-chain via cast send

T+0→24h

Pinning

players commit 1 USDC + (lat, lng) to GeoCastPool

T+24h

Pins frozen

contract rejects any further commitBet() calls

T+24h+

USGS watch

cron polls every minute for first M5+ event

event

Settle + claim

Merkle root posted on-chain, players claim via proof

Step 1 · The resolver loop (PHP, every 1 min)

# AftershockResolver::resolve($params, $now)
$searchStart = $params['windowEnd']     # the round's closesAt
$elapsed     = $now - $searchStart        # how long we've waited
$minMag      = magnitudeFloorFor($elapsed)

# call USGS — same endpoint anyone can hit
GET earthquake.usgs.gov/fdsnws/event/1/query
  ?starttime=$searchStart
  &endtime=$now
  &minmagnitude=$minMag
  &orderby=time-asc   # chronologically first
  &limit=1

# if features=[] → throw, cron retries next tick
# else → AnswerPoint(lat, lng, place) wins the round

Step 2 · Magnitude floor walks down

~22% of random 24h windows have zero M5+ events. The floor drops over time so the round always resolves within 24h — M4+ globally is ~50 events/day.

  • 0–4hM5.0+~92% hit
  • 4–8hM4.5+~99% hit
  • 8–24hM4.0+guaranteed
  • 24h+M3.5+last resort

Step 3 · Distance ranking (MariaDB, ST_Distance_Sphere)

For every prediction in the round, the server computes haversine distance on a 6,371 km sphere between the player pin and the epicentre. Indexed SPATIAL POINT column, sub-second for thousands of pins.

SELECT id,
  ST_Distance_Sphere(coords, POINT(?,?)) / 1000 AS km
FROM predictions
WHERE round_id = ?
ORDER BY km ASC

Step 4 · Inverse-distance payout

5% rake to treasury. The remaining 95% of the pool is distributed by raw_score share — the closest pin earns the most, but the curve is long-tailed (a 5,000 km miss still earns a sliver).

raw_score_i = 1 / (1 + d_i)

# d_i is your distance in km
# Σ raw_score_j across every player

payout_i = floor(
  0.95 × pool_usdc ×
  raw_score_i / Σ raw_score_j
)

Step 5 · Settle on-chain (Foundry · cast send)

# 1. Build a Merkle tree of (player, payout) leaves
#    Leaf = bytes.concat(keccak256(abi.encode(player, amount)))
#    Matches OZ MerkleProof.verify with sorted-pair encoding

$merkleRoot = SettlementBuilder::settle($round, $lat, $lng)

# 2. Server-held resolver wallet posts the root on-chain
#    Round.resolve(answer_lat, answer_lng, merkleRoot)

cast send $POOL "resolve(uint64,int256,int256,bytes32)" \
  $round $lat_e7 $lng_e7 $merkleRoot \
  --private-key $RESOLVER_KEY --rpc-url $RPC

# 3. Player claims their payout via Merkle proof
#    GeoCastPool.claim(amount, proof) → USDC transferred
#    Idempotent: claim bit-set in storage, can't claim twice

Read AftershockResolver.php on GitHub300 lines, no hidden logic.

The math

Scoring is inverse distance. Long-tail friendly.

Every pin costs 1 USDC and goes into the round pool. A 5% rake is taken to treasury; the remaining 95% is distributed back to players by inverse distance. For a pin at haversine distance d km from the truth, raw score = 1 / (1 + d) and your USDC payout is floor(0.95 × pool × your_raw_score / Σ all_raw_scores). The closest pin always takes the biggest share, but a pin 10,000 km off still earns a cent — long-tail engagement, no zero-sum trap.

Truth (answer)WinnerOther players · brighter = closer = bigger share

100 USDC pool · 12 players · 5% rake → treasury

truth: 38.72, -9.14Lisbon (truth)

#playerkm offraw_scorepayout
1Coimbra1760.00564+43.19 USDC
2Madrid5020.00199+15.21 USDC
3Casablanca5890.00169+12.98 USDC
4Paris1,4530.00069+5.26 USDC
5London1,5850.00063+4.83 USDC
6Rome1,8630.00054+4.11 USDC
7Istanbul3,2370.00031+2.36 USDC
8Lagos3,7950.00026+2.01 USDC
9Cairo3,7960.00026+2.01 USDC
10New York5,4220.00018+1.41 USDC
11Cape Town8,5630.00012+0.89 USDC
12Tokyo11,1430.00009+0.68 USDC
Treasury rake (5%)5.00 USDC
Paid to players94.94 USDC
Rounding → protocol bucket0.06 USDC

Live activity

The map is always playing.

Pins this week

1,247

Active rounds

1

Total explorers

3,812

Last winner

8.3 km off

0x7f4c…a3b1 · +47 cr

loading…

ready to predict?

One pin per round.
The world is your guess.

on Base Sepolia · test USDC, mint on demand

About

The only un-gameable geo-prediction game.

GeoCast asks one question, every round, forever: Where will the first M5+ earthquake strike in the 24 hours after this round closes? Players have 24 hours of an open map to commit a single pin. Then the round closes, the clock starts, and the live USGS earthquake feed is watched in real time. The first M5+ event to strike anywhere on Earth is the truth. Closest pin to the epicentre takes the biggest share of the pool — inverse-distance scoring, never zero-sum, even a 5,000 km miss still pays a sliver.

The whole canvas is a full-screen MapLibre vector map (Carto Dark Matter tiles, no API key required), with glassmorphic panels floating on top. Sign in with your Ethereum wallet (SIWE, EIP-4361) — no email, no password, no tracking — and your address is your account. Real-time presence dots show other players' cursors as they hover the map; pin placements broadcast over Pusher to every connected viewer. When the first M5+ hits, the answer pin drops, a great-circle line draws from your pin to the truth, and your distance badge pulses in.

The answer doesn't exist when you commit. That's the difference. Most prediction games pick a winner from data that already determines the outcome — tomorrow's weather forecast, today's burning wildfire, this week's hottest capital. GeoCast doesn't. The answer event physically hasn't happened yet, and earthquakes are the canonical un-predictable natural event. The resolver source code is open on GitHub so anyone can verify the rules. Reading the code tells you how the answer is fetched — not what it will be.

Built solo as a portfolio piece for the senior full-stack surface: Next.js 16 App Router + Tailwind 4 + MapLibre + Framer Motion on the front; Symfony 7.4 + API Platform 4 + Doctrine ORM + MariaDB SPATIAL POINT + Predis on the back; Pusher Channels for real-time; Foundry + OpenZeppelin contracts on Base for the optional on-chain pool. Dockerised end-to-end, deployable to a single Hetzner box with one command. Source is open at github.com/kindrakevich-agency/GeoCast.

Frequently asked questions

What is GeoCast?

GeoCast is a geo-prediction game with one question, forever: "Where will the first M5+ earthquake strike in the 24 hours after this round closes?" The map is open for 24 hours of commits. Players each drop a single pin. The round closes, and only then does the live USGS earthquake feed start being watched in real time — the first M5+ event to strike anywhere on Earth wins, and the closest pin to its epicentre takes the biggest share of the pool.

Why one question forever — isn't variety more fun?

Variety is fun until it becomes a leaky abstraction. We tested questions about hottest capitals, wildfires, rainfall — every weather/forecast question is gameable: an analyst with API access can query Open-Meteo's 24h forecast and basically know the answer when the round opens. Earthquakes are different. Seismologists at USGS have spent 60+ years trying to forecast them and still publish the disclaimer 'no method has been proven successful'. The event hasn't happened at commit time. Pure prediction, pure skill. One question is the right number when the question is right.

Can I cheat by querying the USGS API myself?

No — and the resolver source code is public so you can verify. The 24h commit window is BEFORE the answer event happens. There's no data to scrape — the M5+ that decides the round hasn't been detected yet. After the round closes, you could watch the same USGS feed the cron watches, but by then commits are closed.

What if no M5+ earthquake happens in 24 hours after close?

Global M5+ rate is ~4 per day, so the expected wait is ~6 hours. About 92% of 4h windows have an M5+; about 99% of 8h windows do. As a safety net the magnitude floor walks down: M5+ for the first 4 hours, then M4.5+ for the next 4, then M4+ for the rest of the 24h window. M4+ globally is ~50/day so resolution within 24h is essentially guaranteed.

Is GeoCast free to play?

GeoCast runs on Base Sepolia using test USDC, not real money. Each pin costs 1 test USDC into the GeoCastPool contract; after the M5+ epicentre is determined, the auto-resolver settles the pool on-chain and winners claim their share via Merkle proof. If your wallet is short on test USDC there's a one-click faucet button on the round page — no top-ups, no purchases, no subscriptions.

Do I need a crypto wallet?

Yes — GeoCast uses Sign-In with Ethereum (SIWE, EIP-4361) for authentication. There is no email, no password, and no tracking. Your wallet address is your account. Any EVM-compatible wallet works: MetaMask, Rabby, Coinbase Wallet, Rainbow, or any WalletConnect-compatible wallet on desktop or mobile.

How are pin distances calculated?

Using the haversine formula on a sphere of radius 6,371 km — the standard great-circle distance between two latitude/longitude points. The math runs server-side in MariaDB via ST_Distance_Sphere on indexed SPATIAL POINT columns, so ranking thousands of pins on round resolution is sub-second.

How is the prize pool split?

Each prediction earns a raw score of 1 / (1 + distance_km). Your payout is your share of 95% of the pool (5% goes to the protocol treasury): floor(0.95 × pool × your_raw_score ÷ sum_of_raw_scores). This long-tail curve guarantees that the closest pin always wins the biggest share, while still rewarding pins thousands of km away — no zero-sum trap.

Is GeoCast a prediction market?

No. GeoCast is a geo-prediction game, not a regulated prediction market. There are no derivatives, no shares, no real-money speculation. The pool is split pro-rata by accuracy of a geographic guess. The on-chain mode is testnet-only and exists to demonstrate the architecture for a senior full-stack portfolio.

What does the leaderboard show?

Three rankings — today, this week, and all-time — sourced from a Redis sorted set so reads stay sub-millisecond even with thousands of players. The all-time score is the cumulative sum of raw scores across every resolved round you've played: distance-weighted, so consistent close guesses beat one lucky hit.

Who builds GeoCast?

GeoCast is built solo by Vitalii Kindrakevych (kindrakevich-agency) as a senior full-stack portfolio piece. Stack: Next.js 16 + Tailwind 4 + MapLibre + wagmi on the front; Symfony 7.4 + API Platform + MariaDB SPATIAL + Predis on the back; Pusher Channels for real-time presence; Foundry + OpenZeppelin contracts for the on-chain pool on Base. Source is open at github.com/kindrakevich-agency/GeoCast.

keywords: geo prediction game · daily map game · pin-drop game · haversine ranking · web3 prediction game · siwe game · MapLibre game