A real-time Bitcoin price guessing game. Predict whether BTC/USD will be higher or lower after 10 seconds and earn points.
Live demo: up-or-down-eight.vercel.app
Players are shown the latest BTC/USD price and a 10-second countdown. Before the countdown ends, they submit a guess: UP or DOWN. When the timer hits zero, the new price is fetched and compared to the price at the time of the guess:
- Correct → +1 point
- Wrong → −1 point
After 3 seconds the result clears and a new round begins automatically. Only one guess can be active at a time.
Each player is identified by a UUID stored in localStorage, so scores persist across browser sessions: close the tab and come back, your score is still there.
Check the Figma. I designed the UI in Figma after a short but effective UX research phase: I looked at a potential user persona and studied existing products in the space like Kalshi and Polymarket.
| Layer | Choice |
|---|---|
| Framework | Next.js 16 (App Router) |
| Styling | Tailwind CSS |
| DB ORM | Drizzle ORM |
| Database | AWS RDS PostgreSQL |
| Data fetching | TanStack Query |
| Price feed | Binance REST API (GET /api/v3/ticker/price?symbol=BTCUSDT) |
| Chart | wani-graf (my product, embedded via iframe) |
| Deploy | Vercel |
Prerequisites: Node.js 18+ and npm.
git clone <repo>
cd up-or-down
npm installCopy the example env file:
cp env.local.example .env.localOpen .env.local and replace YOUR_PASSWORD in DATABASE_URL with the actual password — ask the project owner.
The RDS SSL certificate is already committed at certs/rds-ca.pem, no extra setup needed.
npm run devOpen http://localhost:3000.
| Variable | Description |
|---|---|
DATABASE_URL |
Postgres connection string (ask project owner for password) |
NEXT_PUBLIC_APP_URL |
App base URL |
Schema is defined in src/lib/schema.ts. DB client is in src/lib/db.ts.
The AWS RDS CA certificate is committed at certs/rds-ca.pem — no manual setup needed, Drizzle uses it automatically for secure SSL connections.
# generate a migration after changing schema.ts
npm run db:generate
# apply migrations to the DB
npm run db:migrateMigrations live in ./drizzle/. Never edit them by hand.
- Push to GitHub.
- Import the repo at vercel.com/new.
- Under Settings → Environment Variables, add:
DATABASE_URL— your RDS connection stringNEXT_PUBLIC_APP_URL— your Vercel deployment URL (e.g.https://up-or-down.vercel.app)
- Make sure the RDS security group allows inbound connections on port 5432 from
0.0.0.0/0. - Hit Deploy.
The vercel.json at the repo root is already configured — no extra setup needed.
| Method | Path | Description |
|---|---|---|
GET |
/api/price |
Live BTC/USD from Binance |
GET |
/api/score?id= |
Get player score |
POST |
/api/score |
Save player score (body: { id, score }) |
Binance (binance.com) blocks API requests from US-based IP addresses for regulatory reasons — the exchange is not licensed to operate in the United States and tightened geo-blocking following its 2023 FinCEN/DOJ settlement. Requests from US datacenter IPs return HTTP 451 Unavailable For Legal Reasons.
The vercel.json at the repo root pins all functions to Vercel's Stockholm region (arn1), which avoids the Binance block and co-locates the functions with the AWS RDS instance (also in eu-north-1), reducing database latency. No extra configuration needed — it's already committed.
Timers could move to the server for tamper resistance and consistent timing. Score validation could also live on the server so the game logic is not entirely client-driven. For a small app, client-side timing is fine; scaling would warrant server-side rounds and scoring.