Vishal Tyagi
Exit/

Live GPS Map with Socket.IO and Leaflet

5 min left

About this Codelab

What you'll build

A shared map where every browser with GPS permission shows up as a live marker.

What you'll need

Node.js 18+, two browsers or phones on the same network, and HTTPS (or localhost) for geolocation.

Duration: ~5 min6 stepsIntermediateUpdated 2024-07

Scaffold the app

mkdir live-map && cd live-map
npm init -y
npm install express socket.io ejs dotenv
mkdir -p public views

Create server.js, public/app.js, and views/index.ejs.

[!CHECKPOINT] Hello world node server.js and hit http://localhost:3000 with a static “ok” route before adding sockets.

Relay locations on the server

import express from 'express';
import { createServer } from 'node:http';
import { Server } from 'socket.io';

const app = express();
const server = createServer(app);
const io = new Server(server);

app.set('view engine', 'ejs');
app.use(express.static('public'));
app.get('/', (_req, res) => res.render('index'));

io.on('connection', (socket) => {
  let deviceInfo = null;
  socket.on('deviceInfo', (info) => { deviceInfo = info; });
  socket.on('sendLocation', (coords) => {
    if (!deviceInfo) return;
    io.emit('receiveLocation', { id: socket.id, deviceInfo, ...coords });
  });
  socket.on('disconnect', () => io.emit('userDisconnect', socket.id));
});

server.listen(process.env.PORT || 3000);

io.emit goes to everyone (including the sender) so the map stays consistent.

Collect GPS in the browser

const socket = io();
const map = L.map('map').setView([0, 0], 2);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '© OpenStreetMap',
}).addTo(map);

const markers = {};

navigator.geolocation.watchPosition(
  (pos) => {
    socket.emit('sendLocation', {
      latitude: pos.coords.latitude,
      longitude: pos.coords.longitude,
    });
  },
  (err) => console.error(err),
  { enableHighAccuracy: true },
);

socket.emit('deviceInfo', {
  platform: navigator.platform,
  screenResolution: `${screen.width}x${screen.height}`,
});

Update and remove markers

socket.on('receiveLocation', ({ id, latitude, longitude, deviceInfo }) => {
  if (!markers[id]) {
    markers[id] = L.marker([latitude, longitude])
      .bindPopup(deviceInfo.platform)
      .addTo(map);
  } else {
    markers[id].setLatLng([latitude, longitude]);
  }
  const bounds = L.latLngBounds(Object.values(markers).map((m) => m.getLatLng()));
  map.fitBounds(bounds, { padding: [40, 40] });
});

socket.on('userDisconnect', (id) => {
  if (markers[id]) {
    map.removeLayer(markers[id]);
    delete markers[id];
  }
});

Wire the HTML shell

<!doctype html>
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
    <style>#map { height: 100vh; margin: 0; }</style>
  </head>
  <body>
    <div id="map"></div>
    <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
    <script src="/socket.io/socket.io.js"></script>
    <script src="/app.js"></script>
  </body>
</html>

[!CHECKPOINT] Two-device test Open the URL on phone + laptop (same Wi-Fi). Allow location. Walk. Markers should move; closing a tab should drop one marker.

Ship notes

Geolocation needs HTTPS off localhost. Put TLS in front (Caddy/nginx) before demos outside your LAN. Full reference: project page and GitHub.