In fleet management platforms (like Routz), tracking hundreds of active vehicles in real-time requires continuous data syncing. When raw coordinate updates stream in via WebSockets at sub-second intervals, the naive approach of re-rendering map markers instantly will bring the browser's main thread to a complete crawl.
To achieve smooth 60fps movement, we implemented three coordinate-culling optimization techniques:
1. Viewport Culling (Marker Pruning)
Instead of updating all vehicle markers globally, we check if the vehicle falls within the user's active map bounding box before dispatching DOM updates:
// src/utils/map.ts
export function updateVisibleMarkers(map, vehiclesData) {
const bounds = map.getBounds();
vehiclesData.forEach(vehicle => {
const position = [vehicle.lat, vehicle.lng];
if (bounds.contains(position)) {
// Render or update marker coordinates
updateVehicleMarker(vehicle.id, position);
} else {
// Prune off-screen marker from the DOM to reduce nodes
removeVehicleMarker(vehicle.id);
}
});
}
2. Haversine Distance Thresholding
WebSockets often broadcast coordinate updates even when a vehicle is idling at a red light or stuck in heavy traffic. To avoid redundant updates, we calculate the Haversine distance from the previous coordinate and ignore changes below a
5-meter threshold:
// src/utils/haversine.ts
export function getDistanceMeters(lat1, lon1, lat2, lon2) {
const R = 6371e3; // Earth radius in meters
const phi1 = lat1 * Math.PI / 180;
const phi2 = lat2 * Math.PI / 180;
const deltaPhi = (lat2 - lat1) * Math.PI / 180;
const deltaLambda = (lon2 - lon1) * Math.PI / 180;
const a = Math.sin(deltaPhi/2) * Math.sin(deltaPhi/2) +
Math.cos(phi1) * Math.cos(phi2) *
Math.sin(deltaLambda/2) * Math.sin(deltaLambda/2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
return R * c; // returns distance in meters
}
3. Coordinate Debouncing (Interpolation)
Instead of snapping the marker immediately to the new coordinate, we smooth the transitions using linear interpolation (lerp). This reduces visual jitter and creates a smooth moving effect even when WebSocket packets arrive asynchronously.
Implementing these constraints reduced overall main-thread scripting overhead by
~70%, enabling a highly performant real-time visualization dashboard.