A few weeks ago I started wondering, what if you could turn live crypto price data into a competitive multiplayer game? No wallets, no tokens, no risk. Just raw price feeds and your instincts.
That became Price Royale.
What it is
Price Royale is a free-to-play PvP game where players predict whether ETH, BTC, or SOL will go UP or DOWN within a configurable time window (15–90 seconds). Multiple players join the same room, everyone locks in their prediction, and a live Pyth oracle price feed settles the outcome when time runs out.
The thing I'm most proud of: your entry price is recorded at the exact moment you click,`` not the round start price. So if you wait 20 seconds before committing, you're betting from a different baseline than someone who clicked immediately. It creates real strategy around timing.
Why Pyth specifically
I needed price feeds that were:
Fast enough to update during a 60-second game round
Accurate enough that players would trust the settlement
Easy to integrate without blockchain complexity
Pyth's Hermes REST API fit perfectly. Three feeds, one endpoint, polling every 3 seconds:
jsconst res = await axios.get(https://hermes.pyth.network/v2/updates/price/latest?ids[]=${feedId}`{% endraw %}
);
const price = res.data.parsed[0].price.price * Math.pow(10, expo);{% raw %}jsconst confBps = (conf / price) * 10000;
But the feature I didn't expect to love: the confidence interval. Every Pyth price comes with a conf value representing the spread of oracle reports. I use this as a score multiplier — when the CI is wide (market is uncertain/volatile), a correct prediction earns up to 1.5× points. Players who time their commits during high-volatility moments get rewarded more.
let ciMultiplier = 1.0;
if (confBps >= 50) ciMultiplier = 1.5;
else if (confBps >= 25) ciMultiplier = 1.25;
else if (confBps >= 10) ciMultiplier = 1.1;`
It's a small touch but it makes the game feel genuinely connected to real market conditions.
The architecture
Backend is Node.js + Express + Socket.io. Frontend is Vite + React. No blockchain, no wallet connection required — just username/password or Discord OAuth.
The real-time game loop runs entirely over WebSockets:
Host creates a room, shares the 6-letter code
Players join and see the live price ticker
Host starts, a 30-second commit window opens
Each player picks UP or DOWN (their personal entry price is snapped at that moment)
Chart keeps running after commit window closes
At round end, Pyth settles, result screen shows who was right, with CI/speed/streak bonuses
Three game modes: custom rooms, Quick Royale (auto-start when 2+ join), and Tournament (bracket elimination, 4–32 players).
Hardest bug I hit
The scariest moment was discovering that socket.join() in Socket.io v4 is async, it returns a Promise. I wasn't awaiting it. So when a player's socket reconnected and I transferred their room membership to the new socket before disconnecting the old one, the join hadn't actually completed yet. The game:starting broadcast fired, but the new socket wasn't in the room channel. Players would click Start and just... sit there. Spent way too long on this one.
Fix was two lines:
jsawait socket.join(prevSocket.currentRoomId);
*Stack
*
Frontend: React 18 (Vite), Socket.io-client, TradingView Lightweight Charts
Backend: Node.js, Express, Socket.io
Database: MongoDB Atlas
Oracle: Pyth Network Hermes REST API
Auth: JWT + Discord OAuth2
Deployed: Railway (backend), Vercel (frontend)
Try it
Live: https://price-royale.vercel.app
Code: https://github.com/waleedbhattiii/price-royale
Would love feedback, especially if you find edge cases in the tournament bracket logic. That part was its own adventure.
Top comments (0)