Real-time crypto trading interface engineered for zero layout shift and zero dropped frames. Streams live BTC/USDT from Binance over WebSocket — candlestick chart, depth-aware order book, virtualized trades tape — with a live perf overlay showing FPS and message rate.
React 18 · TypeScript 5.6 · Vite 5 · Lightweight Charts (TradingView's production chart library) · Binance public WebSocket API.
- Live candlestick chart — 1-second OHLCV candles built from the raw trade tick stream, rendered with TradingView's Lightweight Charts.
- Order book — top 15 levels of bids and asks with cumulative-depth bars, refreshing every 100ms from Binance's partial book stream.
- Trades tape — virtualized scrolling list of the most recent 500 trades, color-coded by aggressor side.
- Header ticker — last price, 24h change/high/low/volume.
- Performance overlay — live FPS, frame timing, dropped-frame count, message rate, latency, reconnects.
Measured against a sustained live Binance feed:
- 120 FPS sustained, 8.3ms average frame time, 9.3ms worst frame
- 0 dropped frames across 120 sampled frames
- 0 CLS (Cumulative Layout Shift) throughout the load sequence — every panel reserves height before data arrives
- ~12 msg/sec typical ingest rate from the multiplexed Binance stream, 19ms latency, 0 reconnects in a typical session
(Numbers above are what the live perf overlay reports in the linked demo.)
src/
├── lib/
│ ├── binanceClient.ts # Multiplexed WS client with reconnect + backpressure
│ ├── candleAggregator.ts # Trade ticks -> OHLCV candles
│ ├── orderBook.ts # Sorted book engine for depth events
│ └── frameTimer.ts # rAF-based FPS instrumentation
├── components/
│ ├── Chart.tsx # Candlestick chart
│ ├── OrderBook.tsx # Depth visualization
│ ├── TradesTape.tsx # Virtualized trade list
│ ├── PerfOverlay.tsx # FPS + msg/sec overlay
│ └── Header.tsx # Symbol + 24h stats
└── hooks/
├── useBinanceStream.ts
└── useFrameStats.ts
All streams (@trade, @depth20@100ms, @miniTicker) share one connection to wss://stream.binance.com:9443/stream via the BinanceClient. The client supports:
- SUBSCRIBE / UNSUBSCRIBE messages over a single socket
- Per-stream subscriber registries with fan-out to multiple listeners
- Ring-buffer backpressure: when a stream's buffer exceeds a cap, oldest messages are dropped (counted in stats) so a slow consumer can't OOM the page
- Automatic reconnect with exponential backoff (1s → 2s → 4s → … → 30s cap)
- On reconnect, all active stream subscriptions are re-registered
Every panel reserves its final height before data arrives:
- Chart container has
min-height: 300andcontain: strict - Order book reserves
(rowHeight × levels × 2) + spreadRoweven while empty - Trades tape reserves a fixed
heightand shows a placeholder until the first trade
This keeps Cumulative Layout Shift at 0 throughout the load sequence.
- Chart updates use
series.update()which mutates the live candle in place — no full series rebuild per tick - Trades tape throttles React state updates to 10 Hz; new trades accumulate in a ref between renders
- Order book updates at the rate of the depth feed (10 Hz) directly via setState — list size is bounded
- Trades list is virtualized (only visible rows + 4-row buffer mount)
- The performance overlay updates at ~5 Hz, not per-frame
pnpm install
pnpm devOpen http://localhost:5174/. You'll see live BTC/USDT data flowing in within a second or two — the chart fills as candles aggregate, the order book and trades tape populate immediately.
Requirements: A modern browser (Chrome, Safari, Firefox) and an outbound WebSocket connection to stream.binance.com. Some corporate networks block the connection.
If this were a real product, the next iterations would be:
- Multi-symbol — symbol picker, persistent watchlist, side-by-side micro-charts. The WS client already supports it; just needs UI.
- Server-side aggregation for older history — Binance's WS only gives you live data, so any "show me yesterday" view needs a REST backfill layer.
- Auth and saved layouts — let users customize panel sizes and remember positions across sessions.
- More granular perf telemetry — long-task tracking, paint timing breakdown, per-component render counts. The overlay is a good start but doesn't yet cover everything I'd want in production.
MIT
