Inspiration
Problem: ~39 friends wanted a competitive physics racer with real scores — not screenshots in the group chat. No accounts, no app store, no friction.
Why DynamoDB: Every access pattern is a keyed lookup — top scores by track, personal bests by player, run history by player. No joins, predictable reads, and a single-table model that still works if 39 friends becomes 39,000 players. That’s why I chose Amazon DynamoDB over Aurora for Gotham Knights.
The H0 stack (v0 + Vercel + AWS Databases) let me ship a Stonkrider-style game over a weekend while keeping a production data layer from day one.
What it does
Gotham Knights is a neon 2D physics Batmobile racer:
- 39 operatives — choose your rider (saved on device, no login)
- 10 Gotham districts (sigils 0–9) + 4 villain chase tracks
- Canvas physics — wheelies, jumps, air tricks, stunts, collectibles
- Live leaderboards — top scores per track; compare friends (e.g. drmark vs prez)
- Share to X — posts your score and the district #1 rival
- District-themed visuals — unique skylines, colors, and Knightwatch lore per district
- Mobile + desktop — touch controls, compact HUD, inline top-5 on track cards
Live app: https://gotham-rider-game.vercel.app
How I built it
Frontend (Vercel)
- Scaffolded with v0.app, then extended in Next.js 16, React, and TypeScript
- Custom Canvas game engine — terrain generation, physics, camera, district backgrounds
- Deployed on Vercel with serverless API routes
Database: Amazon DynamoDB
Connected via the Vercel Marketplace AWS integration. API routes authenticate with Vercel OIDC (awsCredentialsProvider) to assume an AWS IAM role at request time — no long-lived access keys in the repo.
Single-table composite key design:
| PK | SK | Purpose |
|---|---|---|
PLAYER#<slug> |
RUN#<timestamp>#<runId> |
Run history |
PLAYER#<slug> |
BEST#<trackId> |
Personal best per track |
LEADERBOARD#<trackId> |
PLAYER#<slug> |
Leaderboard row (query by track) |
When a run finishes, POST /api/runs writes the run, checks the player’s best, and updates the leaderboard row only if the score improved.
API routes:
POST /api/runs— persist run, update best + leaderboard when improvedGET /api/leaderboard?track=<id>— top scores for a track (Query onLEADERBOARD#<trackId>)GET /api/leaderboard?slug=<slug>— player bests across tracks (Query onPLAYER#<slug>)
Architecture
Browser / Mobile → Next.js on Vercel → API routes → Vercel OIDC (assume IAM role) → Amazon DynamoDB
Challenges I ran into
DynamoDB composite keys — Early deploys wrote items but leaderboards stayed empty. Root cause: PK/SK misalignment with the marketplace table schema. Fixed by matching composite keys and switching from Scan to Query.
Deploy pipeline — Hit v0 credit limits mid-build. Overrode the build command in
vercel.jsonand continued shipping via Vercel CLI.Mobile UX — Accidental text selection on HUD labels, overlapping controls, camera jitter on big jumps. Fixed with
user-select: none, HUD layout passes, and simplified camera follow.39 operative avatars — Processed custom bat-cowl PNGs, mapped slugs to display names, served from
/public/heads/.
Accomplishments I'm proud of
- A playable, deployed game friends can open on their phones today
- Real persistent leaderboards on DynamoDB — not localStorage
- Per-district theming with Gotham Knightwatch lore on track select and start screens
- Production patterns from a hackathon build: OIDC to AWS, composite DynamoDB keys, serverless API
- Share flow that names the district #1 rival when you post to X
What I learned
- v0 is strong for UI scaffolding; game physics and database correctness still need hands-on iteration
- DynamoDB single-table design fits leaderboards + history cleanly when you model PK/SK around how you query, not how tables look in SQL
- Vercel + AWS Marketplace OIDC is a credible hackathon → production path
- Small UX details (mute, mobile HUD, aligned track cards) matter as much as the core loop for a social game
What's next for Gotham Knights
- Longer tracks, branching paths, and trap mechanics
- Optional auth (magic link or passkey) for verified leaderboards
- Seasonal villain events and weekly district rotations
- OpenGraph share cards per track with live #1 score
Built With
- amazon-dynamodb
- aws-sdk
- canvas-api
- next.js
- node.js
- react
- tailwind-css
- typescript
- v0.app
- vercel

Log in or sign up for Devpost to join the conversation.