Skip to content

Vikramb1/moonshot

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

26 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

MOONSHOT

The world's first generative game-engine powered by Liquid. Trade like never before.


The Problem

Trading terminals are intimidating.

Candlestick charts, order books, liquidation ladders β€” they're powerful tools, but they create a massive barrier for anyone trying to understand how markets actually move. Most people don't learn to trade because the interface punishes them before they even place their first order.

We asked: what if you could feel a price feed instead of just watching it?


What It Does

Moonshot maps a live crypto price stream into a real-time arcade game. You control a character β€” a spaceship, a surfer, or anything you can describe β€” and your vertical position on screen becomes your market prediction. Every second, the engine reads your position relative to the price line and fires a real 25Γ— leveraged market order in that direction on the Liquid exchange.

The market becomes the level. Your PnL becomes your score.

Core Mechanic β€” Tilt-Based Conviction Trading

A live price line scrolls across the screen in real time, fed by a sub-second WebSocket connection to Hyperliquid. You move your character with W/S or Arrow Keys. Your velocity creates a tilt angle β€” the engine's trade signal:

Tilt Conviction Trade Fired
Tilting upward (moving up) Price going up Opens a long
Tilting downward (moving down) Price going down Opens a short
Neutral (in the safe zone) No conviction No trade, stamina regenerates

A safe zone band tracks the price line. Staying inside it keeps you alive. Drifting away is where the money is made β€” or lost. Obstacles spawn everywhere except the safe zone, representing the noise you have to navigate through to hold your conviction.

Three Game Modes

                            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                            β”‚             MOONSHOT                β”‚
                            β”‚    Generative Trading Game Engine   β”‚
                            β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                   β”‚          β”‚          β”‚
                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”  β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”  β”Œβ–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                  β”‚   Orbit Space     β”‚  β”‚ Surf Shark  β”‚  β”‚ Describe a Game  β”‚
                  β”‚                   β”‚  β”‚             β”‚  β”‚                  β”‚
                  β”‚ Spaceship dodging β”‚  β”‚ Surfer      β”‚  β”‚ AI-generated     β”‚
                  β”‚ asteroids through β”‚  β”‚ riding the  β”‚  β”‚ avatar, world &  β”‚
                  β”‚ a star grid       β”‚  β”‚ price wave, β”‚  β”‚ obstacles from   β”‚
                  β”‚                   β”‚  β”‚ dodging     β”‚  β”‚ text prompts     β”‚
                  β”‚ Theme: pixel-     β”‚  β”‚ sharks      β”‚  β”‚                  β”‚
                  β”‚ retro orange      β”‚  β”‚             β”‚  β”‚ Claude generates β”‚
                  β”‚                   β”‚  β”‚ Theme:      β”‚  β”‚ Canvas 2D code   β”‚
                  β”‚ Config: Mission   β”‚  β”‚ ocean teal  β”‚  β”‚ at runtime       β”‚
                  β”‚ Control           β”‚  β”‚             β”‚  β”‚                  β”‚
                  β”‚                   β”‚  β”‚ Config:     β”‚  β”‚ Theme: forest    β”‚
                  β”‚                   β”‚  β”‚ Surf        β”‚  β”‚ green            β”‚
                  β”‚                   β”‚  β”‚ Station     β”‚  β”‚                  β”‚
                  β”‚                   β”‚  β”‚             β”‚  β”‚ Config: Game     β”‚
                  β”‚                   β”‚  β”‚             β”‚  β”‚ Studio (2-screen β”‚
                  β”‚                   β”‚  β”‚             β”‚  β”‚ wizard)          β”‚
                  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Orbit Space β€” The flagship mode. Pilot a spaceship through a star grid, dodge rotating asteroids, and ride the price feed. Pick your asset (BTC, ETH, SOL, DOGE), set your duration and position size, and launch from Mission Control.

Surf Shark β€” Same trading engine, ocean theme. You're a surfer riding the price wave, dodging sharks. Configure your session at the Surf Station.

Describe a Game β€” The generative mode. Type three descriptions β€” your avatar, your background, your obstacles β€” and Claude generates Canvas 2D drawing functions from pure text. A "Mario Kart racer dodging banana peels on Rainbow Road" becomes a playable trading game in seconds. The AI writes actual JavaScript rendering code that runs at 60fps.


System Architecture

High-Level Overview

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        Frontend (Next.js 15)                        β”‚
β”‚                     HTML Canvas 2D Β· 60fps Β· TypeScript              β”‚
β”‚                                                                      β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚  / Landing   β”‚   β”‚  /lobby      β”‚   β”‚  /custom/create       β”‚    β”‚
β”‚  β”‚  Mode Select β”‚   β”‚  Mission     β”‚   β”‚  Game Studio          β”‚    β”‚
β”‚  β”‚              β”‚   β”‚  Control     β”‚   β”‚  (2-screen wizard)    β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β”‚         β”‚                  β”‚                       β”‚                 β”‚
β”‚         β–Ό                  β–Ό                       β–Ό                 β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚  /surf/lobby β”‚   β”‚  /game       β”‚   β”‚  /custom/game         β”‚    β”‚
β”‚  β”‚  Surf        β”‚   β”‚  Game.tsx    β”‚   β”‚  CustomGame.tsx       β”‚    β”‚
β”‚  β”‚  Station     β”‚   β”‚  ~850 lines  β”‚   β”‚  ~740 lines           β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚  Spaceship + β”‚   β”‚  AI-generated render  β”‚    β”‚
β”‚         β”‚           β”‚  Asteroids   β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β”‚         β–Ό           β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜               β”‚                β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”‚                       β”‚                β”‚
β”‚  β”‚  /surf/game  β”‚          β”‚                       β”‚                β”‚
β”‚  β”‚  SurfGame    β”‚          β”‚                       β”‚                β”‚
β”‚  β”‚  .tsx        β”‚          β”‚                       β”‚                β”‚
β”‚  β”‚  ~930 lines  β”‚          β”‚                       β”‚                β”‚
β”‚  β”‚  Surfer +    β”‚          β”‚                       β”‚                β”‚
β”‚  β”‚  Sharks      β”‚          β”‚                       β”‚                β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜          β”‚                       β”‚                β”‚
β”‚         β”‚                  β”‚                       β”‚                β”‚
β”‚         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                β”‚
β”‚                            β”‚                                        β”‚
β”‚                     β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”                                β”‚
β”‚                     β”‚ Shared Hooks β”‚                                β”‚
β”‚                     β”‚              β”‚                                β”‚
β”‚                     β”‚ useGameEngineβ”‚  State machine + lifecycle     β”‚
β”‚                     β”‚ useLiquid    β”‚  WebSocket price feed          β”‚
β”‚                     β”‚ useMockPrice β”‚  Offline fallback              β”‚
β”‚                     β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜                                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                             β”‚
              WebSocket (price ticks) + REST (trades, themes)
                             β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      Backend (FastAPI + uv)                        β”‚
β”‚                                                                     β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚   β”‚                    WebSocket Endpoints                     β”‚    β”‚
β”‚   β”‚                                                            β”‚    β”‚
β”‚   β”‚   /ws/price/{symbol}        Sub-second allMids relay       β”‚    β”‚
β”‚   β”‚   /ws/price-stream/{symbol} Liquid REST polling (legacy)   β”‚    β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β”‚                                                                     β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚   β”‚                      REST Endpoints                        β”‚    β”‚
β”‚   β”‚                                                            β”‚    β”‚
β”‚   β”‚   POST /api/trade            Market order β†’ 500ms close    β”‚    β”‚
β”‚   β”‚   POST /api/generate-theme   Claude theme generation       β”‚    β”‚
β”‚   β”‚   POST /api/place-order      Direct limit order            β”‚    β”‚
β”‚   β”‚   POST /api/close-all        Emergency kill switch         β”‚    β”‚
β”‚   β”‚   GET  /api/account          Balance & equity              β”‚    β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β”‚                                                                     β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚   β”‚  Hyperliquid WS     β”‚  β”‚ Liquid REST   β”‚  β”‚ Anthropic API  β”‚    β”‚
β”‚   β”‚  (price feed)       β”‚  β”‚ (orders)      β”‚  β”‚ (themes)       β”‚    β”‚
β”‚   β”‚  allMids channel    β”‚  β”‚ HMAC-SHA256   β”‚  β”‚ Claude Sonnet  β”‚    β”‚
β”‚   β”‚  sub-second latency β”‚  β”‚ 25Γ— leverage  β”‚  β”‚ Canvas 2D gen  β”‚    β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Dual Exchange Bridge

The exchange we trade on (Liquid) doesn't offer a fast WebSocket price feed. So we built a bridge β€” prices stream in from Hyperliquid's allMids WebSocket at sub-second latency, while orders execute on Liquid's REST API with HMAC-SHA256 authentication.

  Hyperliquid              Backend               Frontend              Liquid
  (price feed)           (FastAPI)          (Canvas @ 60fps)         (exchange)
      β”‚                      β”‚                      β”‚                     β”‚
      β”‚  allMids tick        β”‚                      β”‚                     β”‚
      β”‚  (sub-second)        β”‚                      β”‚                     β”‚
      β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”‚                      β”‚                     β”‚
      β”‚                      β”‚  WS: {price,         β”‚                     β”‚
      β”‚                      β”‚   direction,          β”‚                     β”‚
      β”‚                      β”‚   timestamp}          β”‚                     β”‚
      β”‚                      β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”‚                     β”‚
      β”‚                      β”‚                      β”‚                     β”‚
      β”‚                      β”‚                      β”‚ Interpolate price   β”‚
      β”‚                      β”‚                      β”‚ (4-frame lerp)      β”‚
      β”‚                      β”‚                      β”‚                     β”‚
      β”‚                      β”‚                      β”‚ Render frame        β”‚
      β”‚                      β”‚                      β”‚ Check tilt angle    β”‚
      β”‚                      β”‚                      β”‚                     β”‚
      β”‚                      β”‚                      β”‚ Every 1s, if        β”‚
      β”‚                      β”‚                      β”‚ |tilt| > 0.01:      β”‚
      β”‚                      β”‚                      β”‚                     β”‚
      β”‚                      β”‚   POST /api/trade    β”‚                     β”‚
      β”‚                      β”‚   {symbol, side,      β”‚                     β”‚
      β”‚                      β”‚    size, leverage:25}  β”‚                     β”‚
      β”‚                      │◄──────────────────────                     β”‚
      β”‚                      β”‚                      β”‚                     β”‚
      β”‚                      β”‚     Market order (buy/sell)                β”‚
      β”‚                      β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”‚
      β”‚                      β”‚                      β”‚   Order confirmed   β”‚
      β”‚                      │◄────────────────────────────────────────── β”‚
      β”‚                      β”‚                      β”‚                     β”‚
      β”‚                      β”‚  {success, order_id,  β”‚                     β”‚
      β”‚                      β”‚   closes_in_ms: 500}  β”‚                     β”‚
      β”‚                      β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”‚                     β”‚
      β”‚                      β”‚                      β”‚                     β”‚
      β”‚                      β”‚  asyncio.sleep(0.5)  β”‚                     β”‚
      β”‚                      β”‚                      β”‚                     β”‚
      β”‚                      β”‚     Opposite market order (auto-close)    β”‚
      β”‚                      β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”‚
      β”‚                      β”‚                      β”‚                     β”‚

The Game Loop β€” 60fps Render Pipeline

Every game component runs a single requestAnimationFrame loop that processes physics, trading logic, and rendering in one tick. Game state lives in React refs β€” not useState β€” to prevent re-renders from killing frame rate.

    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚              requestAnimationFrame(tick)                      β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                               β”‚
                               β–Ό
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  1. PRICE INTERPOLATION                                      β”‚
    β”‚     4-frame lerp smoothing of WebSocket price ticks           β”‚
    β”‚     Eliminates network jitter β†’ fluid price line              β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                               β”‚
                               β–Ό
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  2. DYNAMIC RESCALING                                        β”‚
    β”‚     Trigger at Β±20% of visible range edge                    β”‚
    β”‚     Animate new range over 20 frames (lerp min/max)          β”‚
    β”‚     Remap entire price history buffer in same frame           β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                               β”‚
                               β–Ό
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  3. CHARACTER PHYSICS                                        β”‚
    β”‚     Velocity: INITIAL_SPEED=2.2 β†’ MAX_SPEED=6.5 px/frame    β”‚
    β”‚     Acceleration ramp: lerp(vel, target, 0.06)               β”‚
    β”‚     Boundary clamping: 60px from top/bottom edges            β”‚
    β”‚     Tilt angle: clamp(velocity * 0.18, -0.55, 0.55)         β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                               β”‚
                               β–Ό
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  4. SAFE ZONE DETECTION                                      β”‚
    β”‚     Β±32px band centered on current price Y-position          β”‚
    β”‚     Inside: stamina holds, shield alpha increases             β”‚
    β”‚     Outside: health drains at -0.04/frame                    β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                               β”‚
                               β–Ό
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  5. OBSTACLE MANAGEMENT                                      β”‚
    β”‚     Spawn interval: 28 frames β†’ 20 β†’ 14 (escalating)        β”‚
    β”‚     Collision: distance < radius + 8px β†’ 15 damage           β”‚
    β”‚     Excluded from safe zone (Β±80px clearance)                β”‚
    β”‚     Proximity glow: intensity scales within 200px range      β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                               β”‚
                               β–Ό
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  6. TRADE SIGNAL GENERATION                                  β”‚
    β”‚     Every 1s: if |tiltAngle| > 0.01 β†’ fire order             β”‚
    β”‚     tilt < 0 β†’ long (buy)  Β·  tilt > 0 β†’ short (sell)       β”‚
    β”‚     Order: {coinId, priceLevel, size, side, timestamp}       β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                               β”‚
                               β–Ό
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  7. PnL AGGREGATION                                          β”‚
    β”‚     For each order: (currentPrice - entryPrice) / entryPrice β”‚
    β”‚       Γ— positionSize Γ— 25 (leverage) Γ— direction             β”‚
    β”‚     Sum across all open orders β†’ totalPnL                    β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                               β”‚
                               β–Ό
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  8. END CONDITION CHECK                                      β”‚
    β”‚     health ≀ 0       β†’ endGame('health')                     β”‚
    β”‚     timeRemaining ≀ 0 β†’ endGame('time')                      β”‚
    β”‚     PnL β‰₯ threshold  β†’ endGame('profit')                     β”‚
    β”‚     PnL ≀ -threshold β†’ endGame('loss')                       β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                               β”‚
                               β–Ό
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  9. RENDER β€” BACKGROUND CANVAS (z-index: 0)                  β”‚
    β”‚     Orbit: grid lines, star particles, scan effect           β”‚
    β”‚     Surf: ocean gradient, wave lines                         β”‚
    β”‚     Custom: AI-generated bgCtx drawing code                  β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                               β”‚
                               β–Ό
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  10. RENDER β€” GAME CANVAS (z-index: 1)                       β”‚
    β”‚      Price line (quadratic bezier + neon glow + fill)         β”‚
    β”‚      Safe zone (dashed borders + fill + corner brackets)     β”‚
    β”‚      Obstacles (asteroids / sharks / AI-generated)           β”‚
    β”‚      Character (ship / surfer / AI-generated + shield ring)  β”‚
    β”‚      Trail particles, floating text, flash effects           β”‚
    β”‚      Darkness wall (gradient fade from character β†’ right)    β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                               β”‚
                               β–Ό
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  11. HUD OVERLAY (React DOM, z-index: 10)                    β”‚
    β”‚      Price display + direction arrow          (top-left)     β”‚
    β”‚      Stamina / hull health bar                (top-center)   β”‚
    β”‚      Countdown timer                          (top-right)    β”‚
    β”‚      Trade stats (on-target time, placed)     (bottom-left)  β”‚
    β”‚      Estimated PnL readout                    (bottom-right) β”‚
    β”‚      Trade log (last 8 orders)                (bottom)       β”‚
    β”‚      Price axis ladder                        (right edge)   β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                               β”‚
                               β–Ό
                        (next frame)

Price Line Rendering

The price line is the core visual. It renders as a smooth quadratic bezier curve with a neon glow, pulling from a rolling buffer of Y-coordinates. When price drifts too close to the canvas edge, the visible range dynamically rescales over 20 frames:

$$y_{\text{pixel}} = y_{\text{bottom}} - \frac{p - p_{\min}}{p_{\max} - p_{\min}} \cdot (y_{\text{bottom}} - y_{\text{top}})$$

The visible range is asset-specific β€” BTC uses Β±0.03% (~$25 at $84k) while SOL uses Β±0.1% β€” so micro-movements in any asset produce the same visual drama on screen.

PnL Calculation at 25Γ— Leverage

Small floating-point errors in price mapping get amplified 25Γ—. PnL is aggregated across all open orders every frame:

$$\text{PnL} = \sum_{i} \frac{p_{\text{current}} - p_i}{p_i} \times s_i \times 25 \times d_i$$

where $p_i$ is entry price, $s_i$ is position size, and $d_i \in {-1, +1}$ is direction.


Game Engine State Machine

The useGameEngine hook manages the full game lifecycle β€” a finite state machine with ref-mirrored state for zero-latency access from the render loop.

                              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                              β”‚   IDLE    β”‚
                              β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
                                    β”‚ startGame()
                                    β–Ό
                      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                      β”‚          PLAYING             β”‚
                      β”‚                              β”‚
                      β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
                      β”‚  β”‚      TICK LOOP         β”‚  β”‚
                      β”‚  β”‚                        β”‚  β”‚
                      β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚  β”‚
                      β”‚  β”‚  β”‚ Check Health     │──┼──┼──► health ≀ 0
                      β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚  β”‚      β”‚
                      β”‚  β”‚           β–Ό            β”‚  β”‚      β”‚
                      β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚  β”‚      β”‚
                      β”‚  β”‚  β”‚ Check Timer      │──┼──┼──► time ≀ 0
                      β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚  β”‚      β”‚
                      β”‚  β”‚           β–Ό            β”‚  β”‚      β”‚
                      β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚  β”‚      β”‚
                      β”‚  β”‚  β”‚ Check PnL        │──┼──┼──► profit/loss
                      β”‚  β”‚  β”‚ Thresholds       β”‚  β”‚  β”‚   threshold
                      β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚  β”‚      β”‚
                      β”‚  β”‚           β”‚            β”‚  β”‚      β”‚
                      β”‚  β”‚           β–Ό            β”‚  β”‚      β”‚
                      β”‚  β”‚     (next frame)       β”‚  β”‚      β”‚
                      β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚      β”‚
                      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β”‚
                                                            β”‚
                              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β–Ό
                      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                      β”‚    ENDED      β”‚
                      β”‚               β”‚
                      β”‚  endReason:   β”‚
                      β”‚  Β· time       β”‚
                      β”‚  Β· health     β”‚
                      β”‚  Β· profit     β”‚
                      β”‚  Β· loss       β”‚
                      β”‚               β”‚
                      β”‚  GameResult   β”‚
                      β”‚  ──────────   β”‚
                      β”‚  ordersPlaced β”‚
                      β”‚  netDirection β”‚
                      β”‚  totalSize    β”‚
                      β”‚  totalPnL     β”‚
                      β”‚  duration     β”‚
                      β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β–Ό
                      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                      β”‚ Reveal Screen β”‚
                      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Order Execution Flow

Every trade is a zero-duration spike β€” market order in, opposite market order out 500ms later. No position holding. The backend handles retries and fallback closure.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ Frontend ──────────────────────────────────┐
β”‚                                                                           β”‚
β”‚   Character tilt > 0.01                                                   β”‚
β”‚         β”‚                                                                 β”‚
β”‚         β–Ό (every 1s)                                                      β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                                 β”‚
β”‚   β”‚ Generate trade       β”‚                                                β”‚
β”‚   β”‚ signal               β”‚                                                β”‚
β”‚   β”‚                      β”‚                                                β”‚
β”‚   β”‚ tilt < 0 β†’ long      β”‚                                                β”‚
β”‚   β”‚ tilt > 0 β†’ short     β”‚                                                β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                                β”‚
β”‚              β”‚                                                            β”‚
β”‚              β–Ό                                                            β”‚
β”‚   β”Œβ”€β”€β”€β”€ Paper or Live? ────┐                                              β”‚
β”‚   β”‚                        β”‚                                              β”‚
β”‚   β–Ό                        β–Ό                                              β”‚
β”‚  PAPER                   LIVE                                             β”‚
β”‚  Record in               POST /api/trade ──────────────────────────────────
β”‚  ordersRef               {symbol, side, size, leverage: 25}               β”‚
β”‚  (local only)                                                             β”‚
β”‚                                                                           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                         β”‚
                                         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ Backend ───────────────────────────────────┐
β”‚                                                                           β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                          β”‚
β”‚   β”‚ Open market order          β”‚                                          β”‚
β”‚   β”‚ 25Γ— leverage               β”‚ ──── HMAC-SHA256 ────► Liquid Exchange   β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                              β”‚           β”‚
β”‚                β”‚                                              β”‚           β”‚
β”‚                β–Ό                                              β”‚           β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                              β”‚           β”‚
β”‚   β”‚ asyncio.sleep(0.5)         β”‚        Order confirmed β—„β”€β”€β”€β”€β”€β”˜           β”‚
β”‚   β”‚ (500ms hold)               β”‚                                          β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                          β”‚
β”‚                β”‚                                                          β”‚
β”‚                β–Ό                                                          β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                          β”‚
β”‚   β”‚ Close with opposite order  β”‚ ──── HMAC-SHA256 ────► Liquid Exchange   β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                          β”‚
β”‚                β”‚                                                          β”‚
β”‚           β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”                                                     β”‚
β”‚           β”‚ Success? β”‚                                                    β”‚
β”‚           β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜                                                     β”‚
β”‚           No   β”‚   Yes                                                    β”‚
β”‚           β”‚    β”‚    └──► Position closed                                   β”‚
β”‚           β–Ό    β”‚                                                          β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                                      β”‚
β”‚   β”‚ Retry up to 3Γ— β”‚                                                      β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                                      β”‚
β”‚      Stillβ”‚failing                                                        β”‚
β”‚           β–Ό                                                               β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                                β”‚
β”‚   β”‚ Fallback:            β”‚                                                β”‚
β”‚   β”‚ close_position()     β”‚                                                β”‚
β”‚   β”‚ (emergency closure)  β”‚                                                β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                                β”‚
β”‚                                                                           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

AI Theme Generation Pipeline

The "Describe a Game" mode uses Claude to generate real Canvas 2D rendering code from natural language. A 400-line system prompt with reference implementations ensures quality output.

  User              Game Studio           Backend            Claude            localStorage
   β”‚                (2-screen)           (FastAPI)          (Sonnet)
   β”‚                    β”‚                    β”‚                  β”‚                    β”‚
   β”‚  Screen 1:         β”‚                    β”‚                  β”‚                    β”‚
   β”‚  avatar desc       β”‚                    β”‚                  β”‚                    β”‚
   β”‚  background desc   β”‚                    β”‚                  β”‚                    β”‚
   β”‚  obstacle desc     β”‚                    β”‚                  β”‚                    β”‚
   β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”‚                    β”‚                  β”‚                    β”‚
   β”‚                    β”‚                    β”‚                  β”‚                    β”‚
   β”‚  Screen 2:         β”‚                    β”‚                  β”‚                    β”‚
   β”‚  asset, duration,  β”‚                    β”‚                  β”‚                    β”‚
   β”‚  position size,    β”‚                    β”‚                  β”‚                    β”‚
   β”‚  trading mode      β”‚                    β”‚                  β”‚                    β”‚
   β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”‚                    β”‚                  β”‚                    β”‚
   β”‚                    β”‚                    β”‚                  β”‚                    β”‚
   β”‚                    β”‚  POST /api/        β”‚                  β”‚                    β”‚
   β”‚                    β”‚  generate-theme    β”‚                  β”‚                    β”‚
   β”‚                    β”‚  {avatar, bg,      β”‚                  β”‚                    β”‚
   β”‚                    β”‚   obstacles}       β”‚                  β”‚                    β”‚
   β”‚                    β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”‚                  β”‚                    β”‚
   β”‚                    β”‚                    β”‚                  β”‚                    β”‚
   β”‚                    β”‚                    β”‚  System prompt   β”‚                    β”‚
   β”‚                    β”‚                    β”‚  (400 lines) +   β”‚                    β”‚
   β”‚                    β”‚                    β”‚  user descs      β”‚                    β”‚
   β”‚                    β”‚                    β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”‚                    β”‚
   β”‚                    β”‚                    β”‚                  β”‚                    β”‚
   β”‚                    β”‚                    β”‚                  β”‚ Generate 3 Canvas  β”‚
   β”‚                    β”‚                    β”‚                  β”‚ 2D function bodies β”‚
   β”‚                    β”‚                    β”‚                  β”‚ + color palette    β”‚
   β”‚                    β”‚                    β”‚                  β”‚ + labels           β”‚
   β”‚                    β”‚                    β”‚                  β”‚ (15-30+ draw calls β”‚
   β”‚                    β”‚                    β”‚                  β”‚  per function)     β”‚
   β”‚                    β”‚                    β”‚                  β”‚                    β”‚
   β”‚                    β”‚                    β”‚   Raw JSON       β”‚                    β”‚
   β”‚                    β”‚                    │◄──────────────────                    β”‚
   β”‚                    β”‚                    β”‚                  β”‚                    β”‚
   β”‚                    β”‚  {drawAvatar,      β”‚                  β”‚                    β”‚
   β”‚                    β”‚   drawObstacle,    β”‚                  β”‚                    β”‚
   β”‚                    β”‚   drawBackground,  β”‚                  β”‚                    β”‚
   β”‚                    β”‚   colors, labels}  β”‚                  β”‚                    β”‚
   β”‚                    │◄────────────────────                  β”‚                    β”‚
   β”‚                    β”‚                    β”‚                  β”‚                    β”‚
   β”‚                    β”‚  Save theme ──────────────────────────────────────────────►│
   β”‚                    β”‚                    β”‚                  β”‚                    β”‚
   β”‚                    β”‚  Navigate to       β”‚                  β”‚                    β”‚
   β”‚                    β”‚  /custom/game      β”‚                  β”‚                    β”‚
   β”‚                    β”‚  ?id=theme_id      β”‚                  β”‚                    β”‚
   β”‚                    β”‚                    β”‚                  β”‚                    β”‚

Theme Execution Safety

Every Claude-generated draw function is sandboxed with two layers of error boundaries:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Theme Execution Pipeline                            β”‚
β”‚                                                                        β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                 β”‚
β”‚   β”‚  Generated function body         β”‚                                 β”‚
β”‚   β”‚  (string from Claude)            β”‚                                 β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                 β”‚
β”‚                  β”‚                                                      β”‚
β”‚                  β–Ό                                                      β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚   β”‚  new Function(                   β”‚    β”‚                         β”‚  β”‚
β”‚   β”‚    'ctx', 'frame', 'tilt',       │───►│  FALLBACK (Layer 1)    β”‚  β”‚
β”‚   β”‚    body                          β”‚err β”‚  Constructor failed     β”‚  β”‚
β”‚   β”‚  )                               β”‚    β”‚  β†’ colored circle       β”‚  β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚             OK   β”‚                                                      β”‚
β”‚                  β–Ό                                                      β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚   β”‚  Execute in game loop            β”‚    β”‚                         β”‚  β”‚
β”‚   β”‚  (called 60Γ— per second)         │───►│  FALLBACK (Layer 2)    β”‚  β”‚
β”‚   β”‚                                  β”‚err β”‚  Runtime error          β”‚  β”‚
β”‚   β”‚  try { fn(ctx, frame, tilt) }    β”‚    β”‚  β†’ colored circle       β”‚  β”‚
β”‚   β”‚  catch { fallback }              β”‚    β”‚  (per-frame try-catch)  β”‚  β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚             OK   β”‚                                                      β”‚
β”‚                  β–Ό                                                      β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                 β”‚
β”‚   β”‚  Canvas 2D rendering             β”‚                                 β”‚
β”‚   β”‚  at 60fps                        β”‚                                 β”‚
β”‚   β”‚  βœ“ Game never crashes            β”‚                                 β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                 β”‚
β”‚                                                                        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Frontend Component Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ App Router Pages ──────────────────────────┐
β”‚                                                                          β”‚
β”‚  /                    /lobby              /surf/lobby        /custom/     β”‚
β”‚  Landing Page         Mission Control     Surf Station       create      β”‚
β”‚  (mode select)        (Orbit config)      (Surf config)     Game Studio  β”‚
β”‚                                                             (wizard)     β”‚
β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
     β”‚                      β”‚                    β”‚                β”‚
     β”‚              β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”
     β”‚              β”‚   /game      β”‚     β”‚  /surf/game  β”‚ β”‚ /custom/game β”‚
     β”‚              β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
     β”‚                      β”‚                    β”‚               β”‚
     β”‚         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚         β”‚ Game.tsx  (~850 ln)  β”‚ β”‚ SurfGame.tsx    β”‚ β”‚ CustomGame   β”‚
     β”‚         β”‚ Spaceship + Asteroid β”‚ β”‚ (~930 ln)       β”‚ β”‚ .tsx (~740)  β”‚
     β”‚         β”‚ Canvas renderer      β”‚ β”‚ Surfer + Sharks β”‚ β”‚ AI-generated β”‚
     β”‚         β”‚                      β”‚ β”‚ Canvas renderer β”‚ β”‚ Canvas renderβ”‚
     β”‚         β””β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”˜ β””β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”¬β”€β”€β”˜ β””β”€β”€β”¬β”€β”€β”€β”€β”¬β”€β”€β”¬β”€β”€β”˜
     β”‚            β”‚         β”‚     β”‚        β”‚      β”‚    β”‚        β”‚    β”‚  β”‚
     β”‚            β”‚    β”Œβ”€β”€β”€β”€β–Όβ”€β”   β”‚        β”‚ β”Œβ”€β”€β”€β”€β–Όβ”€β”€β” β”‚        β”‚ β”Œβ”€β”€β–Όβ”€β”€β–Όβ”€β”
     β”‚            β”‚    β”‚HUD   β”‚   β”‚        β”‚ β”‚Surf   β”‚ β”‚        β”‚ β”‚Custom β”‚
     β”‚            β”‚    β”‚.tsx  β”‚   β”‚        β”‚ β”‚HUD    β”‚ β”‚        β”‚ β”‚HUD    β”‚
     β”‚            β”‚    β””β”€β”€β”€β”€β”€β”€β”˜   β”‚        β”‚ β”‚.tsx   β”‚ β”‚        β”‚ β”‚.tsx   β”‚
     β”‚            β”‚               β”‚        β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚        β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”˜
     β”‚            β”‚               β”‚        β”‚           β”‚        β”‚
     β”‚         β”Œβ”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”
     β”‚         β”‚              Shared Hooks                         β”‚
     β”‚         β”‚                                                   β”‚
     β”‚         β”‚  useGameEngine.ts  ─── State machine + lifecycle  β”‚
     β”‚         β”‚  useLiquid.ts      ─── WebSocket price feed       β”‚
     β”‚         β”‚  useMockPrice.ts   ─── Offline mock price fallbackβ”‚
     β”‚         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
     β”‚
     β”‚         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚         β”‚              Reveal Screens                       β”‚
     β”‚         β”‚                                                   β”‚
     β”‚         β”‚  RevealScreen.tsx      ─── Orbit Space results    β”‚
     β”‚         β”‚  SurfRevealScreen.tsx  ─── Surf Shark results     β”‚
     β”‚         β”‚  CustomRevealScreen.tsx ── Custom game results    β”‚
     β”‚         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Double Canvas Rendering Stack

Each game mode uses two stacked canvases for rendering performance β€” a background layer for atmospheric elements and a game layer for dynamic objects:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  z:10  β”‚  Price Axis (React DOM)                                     β”‚
β”‚        β”‚  Right-edge price ladder with current price highlight badge  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  z:10  β”‚  HUD Overlay (React DOM)                                    β”‚
β”‚        β”‚  Price, timer, stamina bar, trade log, PnL readout          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  z:1   β”‚  Game Canvas (HTML Canvas)                                  β”‚
β”‚        β”‚  Price line, safe zone, obstacles, character, particles,    β”‚
β”‚        β”‚  floating text, flash effects, darkness wall                β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  z:0   β”‚  Background Canvas (HTML Canvas)                            β”‚
β”‚        β”‚  Grid, stars, ocean waves, or AI-generated background       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Challenges

Keeping 60fps With Real-Time Data

WebSocket price updates arrive at irregular intervals with variable latency. Raw updates caused visible jitter in the price line. We solved this with a 4-frame linear interpolation buffer β€” each new price target is lerped over ~67ms, smoothing network jank into fluid motion.

Dynamic Price Scaling Without Disorientation

When BTC moves $50 in seconds, the price can drift off-canvas. Auto-rescaling triggers at Β±20% of the visible range and animates over 20 frames. The tricky part: rescaling the entire price history buffer simultaneously so the curve doesn't jump β€” every stored Y-coordinate gets remapped in the same frame.

AI Theme Generation That Actually Works

Getting Claude to produce Canvas 2D code that renders well at 60fps required a 400-line system prompt with reference implementations, color constraints (backgrounds must stay under brightness 40), and minimum draw-call counts (15–30+ calls per object). Every generated function is wrapped in error boundaries so a hallucinated API call degrades to a default shape rather than crashing the game loop.

Zero-Duration Trade Safety

All live trades are market orders that auto-close after 500ms. The backend uses a 3-retry loop with exponential backoff, and falls back to close_position() as a last resort. Paper mode is the default β€” live trading requires explicit opt-in.


What We Learned

Markets are games. They have rules, opponents, risk, and reward. But traditional trading UIs hide the game behind spreadsheets. By mapping price action to spatial movement, players develop genuine intuition for volatility, momentum, and risk management β€” all while having fun.

The biggest technical lesson: when real money is involved, every abstraction layer is a liability. We kept the frontend stateless for trading (all orders go through the backend), used paper mode as the default, and made live mode require an explicit opt-in. The game is fun. Losing real money is not.


Tech Stack

Next.js 15 Β· TypeScript Β· HTML Canvas 2D Β· Python Β· FastAPI Β· uv Β· Anthropic Claude Β· Hyperliquid WebSocket Β· Liquid Exchange API Β· Tailwind CSS Β· WebSockets


Running Locally

# Backend
cd backend
uv run uvicorn main:app --port 8000 --reload

# Frontend
cd frontend
npm install
npm run dev    # starts on port 3000

Environment variables (.env in /backend):

LIQUID_API_KEY=
LIQUID_API_SECRET=
ANTHROPIC_API_KEY=

What's Next

  • More game modes β€” The generative engine can skin any game mechanic. We want 10+ themes, all driven by the same trading engine underneath.
  • Leaderboards β€” Best PnL per session, per asset, per game mode. The highest score is the best trade.
  • Mobile β€” Touch controls, full PWA support. The best trading interface should live on your home screen.
  • More assets β€” Any asset with a live price feed becomes a playable level.
  • Multiplayer β€” Same price line, multiple players, competing PnL. Watch another trader's conviction in real time.

Navigate the noise. Find the signal.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors