diff --git a/public_html/gmap.html b/public_html/gmap.html
index b9fff8d..54e2825 100644
--- a/public_html/gmap.html
+++ b/public_html/gmap.html
@@ -15,9 +15,10 @@
         <title>DUMP1090</title>
 	</head>
 	<body onload="initialize()">
-		<div id="dialog-modal" title="Basic modal dialog" style="display:none;">
-			<p>The settings feature is coming soon. Keep checking GitHub.</p>
-		</div>
+          <div id="loader" class="hidden">
+            <img src="spinny.gif" id="spinny">
+          </div>
+
 		<div id="map_container">
 			<div id="map_canvas"></div>
 		</div>
diff --git a/public_html/planeObject.js b/public_html/planeObject.js
index ea441f0..b60baba 100644
--- a/public_html/planeObject.js
+++ b/public_html/planeObject.js
@@ -75,8 +75,10 @@ function PlaneObject(icao) {
 
 // Appends data to the running track so we can get a visual tail on the plane
 // Only useful for a long running browser session.
-PlaneObject.prototype.updateTrack = function() {
+PlaneObject.prototype.updateTrack = function(estimate_time) {
         var here = this.position;
+        if (!here)
+                return;
 
         if (this.track_linesegs.length == 0) {
                 // Brand new track
@@ -98,7 +100,7 @@ PlaneObject.prototype.updateTrack = function() {
         var elapsed = (this.last_position_time - lastseg.head_update);
         
         var new_data = (here !== lastpos);
-        var est_track = (elapsed > 5);
+        var est_track = (elapsed > estimate_time);
         var ground_track = (this.altitude === "ground");
         
         if (!new_data)
@@ -256,7 +258,7 @@ PlaneObject.prototype.updateTick = function(receiver_timestamp) {
 	} else {
                 this.visible = true;
                 if (this.position !== null) {
-			if (this.updateTrack()) {
+			if (this.updateTrack(5)) {
                                 this.updateLines();
                                 this.updateMarker(true);
                         } else { 
diff --git a/public_html/script.js b/public_html/script.js
index 4e19b89..176c38a 100644
--- a/public_html/script.js
+++ b/public_html/script.js
@@ -41,6 +41,48 @@ var MessageCountHistory = [];
 var NBSP='\u00a0';
 var DEGREES='\u00b0'
 
+function processReceiverUpdate(data) {
+	// Loop through all the planes in the data packet
+        var now = data.now;
+        var acs = data.aircraft;
+
+        // Detect stats reset
+        if (MessageCountHistory.length > 0 && MessageCountHistory[MessageCountHistory.length-1].messages > data.messages) {
+                MessageCountHistory = [{'time' : MessageCountHistory[MessageCountHistory.length-1].time,
+                                        'messages' : 0}];
+        }
+
+        // Note the message count in the history
+        MessageCountHistory.push({ 'time' : now, 'messages' : data.messages});
+        // .. and clean up any old values
+        if ((now - MessageCountHistory[0].time) > 30)
+                MessageCountHistory.shift();
+
+	for (var j=0; j < acs.length; j++) {
+                var ac = acs[j];
+                var hex = ac.hex;
+                var plane = null;
+
+		// Do we already have this plane object in Planes?
+		// If not make it.
+
+		if (Planes[hex]) {
+			plane = Planes[hex];
+		} else {
+			plane = new PlaneObject(hex);
+                        plane.tr = PlaneRowTemplate.cloneNode(true);
+                        plane.tr.cells[0].textContent = hex; // this won't change
+                        plane.tr.addEventListener('click', selectPlaneByHex.bind(undefined,hex));
+                        
+                        Planes[hex] = plane;
+                        PlanesOrdered.push(plane);
+		}
+
+		// Call the function update
+		plane.updateData(now, ac);
+	}
+}
+
 function fetchData() {
         if (FetchPending !== null && FetchPending.state() == 'pending') {
                 // don't double up on fetches, let the last one resolve
@@ -52,45 +94,9 @@ function fetchData() {
                                 cache: false,
                                 dataType: 'json' });
         FetchPending.done(function(data) {
-		// Loop through all the planes in the data packet
                 var now = data.now;
-                var acs = data.aircraft;
 
-                // Detect stats reset
-                if (MessageCountHistory.length > 0 && MessageCountHistory[MessageCountHistory.length-1].messages > data.messages) {
-                        MessageCountHistory = [{'time' : MessageCountHistory[MessageCountHistory.length-1].time,
-                                                'messages' : 0}];
-                }
-
-                // Note the message count in the history
-                MessageCountHistory.push({ 'time' : now, 'messages' : data.messages});
-                // .. and clean up any old values
-                if ((now - MessageCountHistory[0].time) > 30)
-                        MessageCountHistory.shift();
-
-		for (var j=0; j < acs.length; j++) {
-                        var ac = acs[j];
-                        var hex = ac.hex;
-                        var plane = null;
-
-			// Do we already have this plane object in Planes?
-			// If not make it.
-                        
-			if (Planes[hex]) {
-				plane = Planes[hex];
-			} else {
-				plane = new PlaneObject(hex);
-                                plane.tr = PlaneRowTemplate.cloneNode(true);
-                                plane.tr.cells[0].textContent = hex; // this won't change
-                                plane.tr.addEventListener('click', selectPlaneByHex.bind(undefined,hex));
-
-                                Planes[hex] = plane;
-                                PlanesOrdered.push(plane);
-			}
-			
-			// Call the function update
-			plane.updateData(now, ac);
-		}
+                processReceiverUpdate(data);
 
                 // update timestamps, visibility, history track for all planes - not only those updated
                 for (var i = 0; i < PlanesOrdered.length; ++i) {
@@ -126,6 +132,7 @@ function fetchData() {
         });
 }
 
+var PositionHistorySize = 0;
 function initialize() {
         PlaneRowTemplate = document.getElementById("plane_row_template");
 
@@ -173,12 +180,102 @@ function initialize() {
                         
                         Dump1090Version = data.version;
                         RefreshInterval = data.refresh;
+                        PositionHistorySize = data.history;
                 })
-                .always(initialize_after_config);
+                .always(function() {
+                        initialize_map();
+                        start_load_history();
+                });
+}
+
+var CurrentHistoryFetch = null;
+var PositionHistoryBuffer = []
+function start_load_history() {
+        if (PositionHistorySize > 0) {
+                console.log("Starting to load history");
+                $("#loader").removeClass("hidden");
+                load_history_item(0);
+        } else {
+                endLoadHistory();
+        }
+}
+
+function load_history_item(i) {
+        if (i >= PositionHistorySize) {
+                end_load_history();
+                return;
+        }
+
+        console.log("Loading history #" + i);
+
+        $.ajax({ url: 'data/history_' + i + '.json',
+                 timeout: 5000,
+                 cache: false,
+                 dataType: 'json' })
+
+                .done(function(data) {
+                        PositionHistoryBuffer.push(data);
+                        load_history_item(i+1);
+                })
+
+                .fail(function(jqxhr, status, error) {
+                        // No more history
+                        end_load_history();
+                });
+}
+
+function end_load_history() {
+        $("#loader").addClass("hidden");
+
+        console.log("Done loading history");
+
+        if (PositionHistoryBuffer.length > 0) {
+                var now, last=0;
+
+                // Sort history by timestamp
+                console.log("Sorting history");
+                PositionHistoryBuffer.sort(function(x,y) { return (x.now - y.now); });
+
+                // Process history
+                for (var h = 0; h < PositionHistoryBuffer.length; ++h) {
+                        now = PositionHistoryBuffer[h].now;
+                        console.log("Applying history " + h + "/" + PositionHistoryBuffer.length + " at: " + now);
+                        processReceiverUpdate(PositionHistoryBuffer[h]);
+
+                        // update track
+                        console.log("Updating tracks at: " + now);
+                        for (var i = 0; i < PlanesOrdered.length; ++i) {
+                                var plane = PlanesOrdered[i];
+                                plane.updateTrack((now - last) + 1);
+                        }
+
+                        last = now;
+                }
+
+                // Final pass to update all planes to their latest state
+                console.log("Final history cleanup pass");
+                for (var i = 0; i < PlanesOrdered.length; ++i) {
+                        var plane = PlanesOrdered[i];
+                        plane.updateTick(now);
+                }
+        }
+
+        PositionHistoryBuffer = null;
+
+        console.log("Completing init");
+
+        refreshTableInfo();
+        refreshSelected();
+        reaper();
+
+        // Setup our timer to poll from the server.
+        window.setInterval(fetchData, RefreshInterval);
+        window.setInterval(reaper, 60000);
+
 }
 
 // Initalizes the map and starts up our timers to call various functions
-function initialize_after_config() {
+function initialize_map() {
         // Load stored map settings if present
         CenterLat = Number(localStorage['CenterLat']) || DefaultCenterLat;
         CenterLon = Number(localStorage['CenterLon']) || DefaultCenterLon;
@@ -329,15 +426,6 @@ function initialize_after_config() {
             }
         }
 	}
-	
-	// These will run after page is complitely loaded
-	$(window).load(function() {
-        $('#dialog-modal').css('display', 'inline'); // Show hidden settings-windows content
-    });
-
-	// Setup our timer to poll from the server.
-	window.setInterval(fetchData, RefreshInterval);
-        window.setInterval(reaper, 60000);
 }
 
 // This looks for planes to reap out of the master Planes variable
diff --git a/public_html/style.css b/public_html/style.css
index d25688b..2b215b0 100644
--- a/public_html/style.css
+++ b/public_html/style.css
@@ -15,6 +15,9 @@ div#update_error { position: absolute; bottom: 25px; left: 25px; border: 2px sol
     background-color: #FFFFA3; opacity: 0.75; filter:alpha(opacity=75); padding: 5px;
     text-align: center; }
 
+div#loader { z-index: 99; position: absolute; left: 0; top: 0; bottom: 0; right: 0; background: #000; opacity: 0.8; filter: alpha(opacity=80); }
+#spinny { width: 128px; height: 128px; position: absolute; top: 50%; left: 50%; margin: -64px 0 0 -64px; }
+
 #tableinfo, #sudo_buttons { font-size: x-small; font-family: monospace; }
 
 .vPosition  { font-weight: bold; background-color: #d5ffd5; }