VIRYA AREA, a browser game played in the real world

How I built VIRYA AREA, a location-based browser game on the Virya website, and the backend problems hiding behind "go here and claim a reward".

3 min read
A phone detecting a glowing geolocated signal on a dark city street

I wanted to put something on the Virya website that people could enter physically. Another gallery or "click here to stream the album" button wasn't it. I wanted a small game living somewhere between the phone and the city.

That became VIRYA AREA. A signal appears in a city, the player goes to the location, opens the page and tries to unlock one of a limited number of rewards. No native app, no QR sticker, no NFC tag. The browser is the interface.

Location readings can't be trusted

At first it sounds easy: ask the Geolocation API for latitude and longitude, calculate the distance and hand out the prize. But a browser gives me a reading, and a reading isn't proof. The position may be old, inaccurate, affected by buildings or simply spoofed. A phone can report the wrong place in good faith, and a dishonest client can send anything it wants.

The server makes every decision

So the frontend handles the friendly part. It asks for permission, collects readings, shows whether accuracy is improving and tells the player that the signal is close. The server makes every decision: is the drop active, is the challenge fresh, was it already used, is the reading recent enough, is the accuracy acceptable, is the player inside the radius, is there still capacity, and has this person already claimed it?

Keeping coordinates out of the bundle

I also stopped shipping exact live coordinates in the JavaScript bundle. Hiding them doesn't make GPS impossible to fake (a browser game will never give me that guarantee), but at least the easiest solution is no longer "open DevTools and read the answer".

Claiming the last reward

The last available reward was another trap. Two phones can pass the distance check at the same time and both see one slot left. So a claim became a reservation committed together with the wallet entry and protected by an idempotency key. A retry returns the same result instead of creating another voucher.

Vouchers and Stripe checkout

The voucher follows the same logic. Opening Stripe checkout doesn't consume it, because the payment may fail or the person may close the tab. The reward is reserved when checkout begins and consumed only after the Stripe webhook confirms the order.

What AREA stores

AREA doesn't store a route history. I need the readings used for the current decision, their accuracy and the outcome, not a map of where somebody spent the afternoon. The audit trail explains why a claim passed or failed without turning the game into a tracking system.

So under a small browser game sits a fair number of backend problems: untrusted clients, expiring challenges, concurrent limits, retries, partial failures and webhook-driven state. Apparently that's what I choose to do for fun. :)

Location rules for the rest of Virya

AREA also set the rule for how the rest of the Virya ecosystem treats location: the public client may guide and explain, but durable reward state belongs in CrowdRelay. That split makes it possible to offer location-aware experiences through other frontends without making any one of them the authority.

Loading…