diff --git a/README.md b/README.md
index b242a02..e764c5e 100644
--- a/README.md
+++ b/README.md
@@ -66,7 +66,7 @@ https://example.com/path/to/api.php?action=fetch-data&datetime=2019-01-03%2007:5
 
 Parameter			| Type		| Meaning
 --------------------|-----------|---------------------
-`only_with_location`| bool		| Optional. If present only devices with a defined location will be returned. Useful for getting a list of devices to place on a map.
+`only-with-location`| bool		| Optional. If present only devices with a defined location will be returned. Useful for getting a list of devices to place on a map.
 
 Examples:
 
diff --git a/client_src/css/main.css b/client_src/css/main.css
index ee2b892..65714b4 100644
--- a/client_src/css/main.css
+++ b/client_src/css/main.css
@@ -1,6 +1,10 @@
 @import "../../node_modules/leaflet/dist/leaflet.css";
+
 @import "../../node_modules/leaflet-fullscreen/dist/leaflet.fullscreen.css";
 
+@import "../../node_modules/leaflet.markercluster/dist/MarkerCluster.css";
+@import "../../node_modules/leaflet.markercluster/dist/MarkerCluster.Default.css";
+
 html, body { font-size: 100%; }
 body {
     font-family: sans-serif;
diff --git a/client_src/js/Config.mjs b/client_src/js/Config.mjs
index e4865fa..e3f7809 100644
--- a/client_src/js/Config.mjs
+++ b/client_src/js/Config.mjs
@@ -1,6 +1,10 @@
 "use strict";
 
 export default {
-	default_location: [ 53.7391, -0.3344 ],
-	default_zoom: 13
+	// The url of api.php. Can be relative.
+	api_root: "../api.php",
+	// The default location to show on the map when loading the page.
+	default_location: [ 53.76203,-0.35162 ],
+	// The default zoom level to use when loading the page.
+	default_zoom: 12
 }
diff --git a/client_src/js/Helpers/GetFromUrl.mjs b/client_src/js/Helpers/GetFromUrl.mjs
new file mode 100644
index 0000000..950d05e
--- /dev/null
+++ b/client_src/js/Helpers/GetFromUrl.mjs
@@ -0,0 +1,17 @@
+"use strict";
+
+// From https://gist.github.com/0b2aee73f8d168e36a353901193e7ff5
+
+export default function GetFromUrl(url) {
+	return new Promise(function(resolve, reject) {
+		let request = new XMLHttpRequest();
+		request.addEventListener("load", function() {
+			if (request.status > 199 && request.status < 300)
+				resolve(request.response);
+			else
+				reject(request.response);
+		});
+		request.open("GET", url, true);
+		request.send(null);
+	})
+}
diff --git a/client_src/js/LayerDeviceMarkers.mjs b/client_src/js/LayerDeviceMarkers.mjs
new file mode 100644
index 0000000..fbdf913
--- /dev/null
+++ b/client_src/js/LayerDeviceMarkers.mjs
@@ -0,0 +1,50 @@
+"use strict";
+
+import L from 'leaflet';
+import 'leaflet.markercluster';
+
+import Config from './Config.mjs';
+import GetFromUrl from './Helpers/GetFromUrl.mjs';
+
+class LayerDeviceMarkers {
+	constructor(in_map) {
+		this.map = in_map;
+	}
+	
+	async setup() {
+		// Create a new clustering layer
+		this.layer = L.markerClusterGroup();
+		
+		console.log("Loading device list");
+		// Fetch the device list
+		let device_list = JSON.parse(await GetFromUrl(
+			`${Config.api_root}?action=list-devices&only-with-location=yes`
+		));
+		
+		console.log("Device list loaded");
+		
+		// Add a marker for each device
+		for (let device of device_list) {
+			this.add_device_marker(device);
+		}
+		
+		console.log("Device markers created");
+		
+		// Display this layer
+		this.map.addLayer(this.layer);
+	}
+	
+	add_device_marker(device) {
+		let marker = L.marker(
+			L.latLng(device.latitude, device.longitude),
+			{ // See https://leafletjs.com/reference-1.4.0.html#marker
+				title: `Device: ${device.name}`,
+				autoPan: true,
+				autoPanPadding: L.point(100, 100)
+			}
+		);
+		this.layer.addLayer(marker);
+	}
+}
+
+export default LayerDeviceMarkers;
diff --git a/client_src/js/Map.mjs b/client_src/js/Map.mjs
index fc3ac61..81e2bf5 100644
--- a/client_src/js/Map.mjs
+++ b/client_src/js/Map.mjs
@@ -5,23 +5,32 @@ import L from 'leaflet';
 import 'leaflet-fullscreen';
 
 import Config from './Config.mjs';
+import LayerDeviceMarkers from './LayerDeviceMarkers.mjs';
 
 class Map {
 	constructor() {
-		
 	}
 	
 	setup() {
+		// Create the map
 		this.map = L.map("map", {
 			fullscreenControl: true
 		});
 		this.map.setView(Config.default_location, Config.default_zoom);
 		
+		// Add the OpenStreetMap tile layer
 		this.layer_openstreet = L.tileLayer("http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
 			id: "openstreetmap",
 			maxZoom: 19,
 			attribution: "&copy; <a href='https://www.openstreetmap.org/copyright'>OpenStreetMap contributors</a>"
 		}).addTo(this.map);
+		
+		// Add the device markers
+		console.info("[map] Loading device markers....");
+		this.device_markers = new LayerDeviceMarkers(this.map);
+		this.device_markers.setup().then(() => {
+			console.info("[map] Device markers loaded successfully.");
+		});
 	}
 }
 
diff --git a/logic/Actions/ListDevices.php b/logic/Actions/ListDevices.php
index e5a3efa..87adfc4 100644
--- a/logic/Actions/ListDevices.php
+++ b/logic/Actions/ListDevices.php
@@ -33,7 +33,7 @@ class ListDevices implements IAction {
 		$start_handle = microtime(true);
 		
 		// 1: Parse & validate parameters
-		$only_with_location = !empty($_GET["only_with_location"]);
+		$only_with_location = !empty($_GET["only-with-location"]);
 		
 		// 1: Pull data from database
 		$data = $this->device_repo->get_all_devices($only_with_location);
diff --git a/package-lock.json b/package-lock.json
index 98b22ec..2714b80 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1071,6 +1071,11 @@
       "resolved": "https://registry.npmjs.org/leaflet-webgl-heatmap/-/leaflet-webgl-heatmap-0.2.7.tgz",
       "integrity": "sha1-mf+/8wrmgDb/4t3tgZjPf+2QSg8="
     },
+    "leaflet.markercluster": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/leaflet.markercluster/-/leaflet.markercluster-1.4.1.tgz",
+      "integrity": "sha512-ZSEpE/EFApR0bJ1w/dUGwTSUvWlpalKqIzkaYdYB7jaftQA/Y2Jav+eT4CMtEYFj+ZK4mswP13Q2acnPBnhGOw=="
+    },
     "loader-utils": {
       "version": "0.2.17",
       "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz",
diff --git a/package.json b/package.json
index 004491d..32a1069 100644
--- a/package.json
+++ b/package.json
@@ -20,7 +20,8 @@
     "leaflet": "^1.4.0",
     "leaflet-fullscreen": "^1.0.2",
     "leaflet-timedimension": "^1.1.0",
-    "leaflet-webgl-heatmap": "^0.2.7"
+    "leaflet-webgl-heatmap": "^0.2.7",
+    "leaflet.markercluster": "^1.4.1"
   },
   "devDependencies": {
     "@types/leaflet": "^1.2.14",
