Real-Time GPS Tracking with Socket.io and Leaflet.js: Lessons from Production
Building real-time vehicle tracking for FAMS V5 was one of the most technically demanding features I've implemented. Handling thousands of GPS-enabled vehicles broadcasting their positions every few seconds, and rendering them live on a map — here's how I did it and what I learned.
The Architecture
The system has three main layers:
- Data ingestion — GPS devices send coordinates via TCP/MQTT to a Node.js ingestion server
- Real-time distribution — Socket.io broadcasts position updates to connected web clients
- Map rendering — Leaflet.js displays vehicles as interactive markers on the map
Why Socket.io Over Raw WebSockets
While raw WebSockets have lower overhead, Socket.io provides critical production features:
- Automatic reconnection — When a user's connection drops (common on mobile), Socket.io reconnects seamlessly
- Room-based broadcasting — Each fleet/organization subscribes to their own "room," so vehicles only broadcast to their owner
- Fallback transports — Some corporate firewalls block WebSockets; Socket.io falls back to long-polling automatically
- Binary support — GPS data can be sent as compressed binary for lower bandwidth
Optimizing Leaflet.js for Thousands of Markers
Rendering 5,000+ markers on a Leaflet map will destroy performance if you use default markers. Here's what I did:
- Marker clustering — Used
Leaflet.markerclusterto group nearby vehicles at lower zoom levels - Canvas renderer — Switched from SVG to Canvas rendering for 10x better performance with many markers
- Viewport culling — Only render markers visible in the current map bounds
- Throttled updates — Batch position updates and render at 30fps instead of on every incoming event
Handling Scale: Connection Management
With thousands of concurrent Socket.io connections, memory and CPU become real concerns:
// Use Redis adapter for horizontal scaling
const { createAdapter } = require("@socket.io/redis-adapter");
const pubClient = createClient({ url: REDIS_URL });
const subClient = pubClient.duplicate();
io.adapter(createAdapter(pubClient, subClient));
The Redis adapter allows multiple Node.js processes (or servers) to share Socket.io state, enabling horizontal scaling behind a load balancer.
Geofencing: Server-Side Efficiency
Geofence calculations (is this vehicle inside this polygon?) are done server-side using the point-in-polygon algorithm. Processing this on the client would be too slow and insecure. When a vehicle crosses a geofence boundary, the server emits an alert event to the relevant dashboard users.
Key Takeaways
Real-time systems require thinking differently about state management, rendering performance, and network resilience. The combination of Socket.io for reliable real-time communication and Leaflet.js for efficient map rendering gives you a solid, production-ready stack for any tracking or logistics application.