Real-Time WebSocket Chat
A real-time chat application built from scratch to learn WebSocket patterns, Go concurrency, and SPA architecture. Features include instant messaging, online user presence, message history persistence, and a custom vanilla JavaScript router. Deployed on Fly.io with full mobile support.
Screenshots



Tech Stack
Problem
Build a production-ready real-time chat to deeply understand WebSocket communication, Go concurrency patterns (channels, goroutines), and single-page application architecture without frameworks.
Solution
Designed a Hub-and-Spoke architecture using Go channels for safe concurrent access. Implemented a custom SPA router in vanilla JavaScript to avoid framework overhead while learning core concepts. WebSocket connections handle bidirectional real-time messaging with automatic reconnection and message history.
Key Features
- ✓Real-time bidirectional messaging via WebSocket
- ✓Hub pattern with Go channels (no mutexes needed)
- ✓Fan-in/fan-out concurrency for message broadcasting
- ✓Message history (last 20 messages) persisted in-memory
- ✓Online users tracking with live updates
- ✓Join/leave notifications
- ✓Custom SPA router (home → chat navigation)
- ✓Mobile-responsive design with toggle sidebar
- ✓Dynamic WebSocket URL for local/production environments
- ✓Auto-scroll to latest messages
Architecture
Backend: Go with Hub pattern managing all client connections in a single goroutine. Channels (register, unregister, broadcast) provide safe message passing between goroutines. WebSocket connections run ReadPump and WritePump in separate goroutines per client. Message history stored as []byte slice in Hub (last 20). Frontend: Vanilla JavaScript SPA with custom router handling URL changes and view rendering. WebSocket connects dynamically (ws:// local, wss:// production). Tailwind CSS for responsive mobile-first design.
Challenges & Solutions
- Understanding Go channels vs mutexes for safe concurrent state
- Preventing race conditions in multi-user scenarios
- Building SPA routing from scratch without React Router
- WebSocket connection handling across page refreshes
- Making sidebar overlay work smoothly on mobile
- Deploying WebSocket apps (different from HTTP-only apps)
What I Learned
- •Go concurrency: channels serialize access, eliminating mutex needs
- •Hub pattern: single goroutine owns all shared state
- •WebSocket lifecycle: open, message, close, error handling
- •SPA principles: history.pushState, popstate events, dynamic rendering
- •Deployment: Dockerfile multi-stage builds, environment variables
- •Why "share memory by communicating" is powerful in Go