diff --git a/reader/templates/reader/book_read/epub_viewer.html b/reader/templates/reader/book_read/epub_viewer.html
index 30f7e5cbdd371ca8d081c70f1b386a06dc79a658..568fb00707b4e1dc0519782daa460f0c8d26b92c 100644
--- a/reader/templates/reader/book_read/epub_viewer.html
+++ b/reader/templates/reader/book_read/epub_viewer.html
@@ -35,6 +35,7 @@
     <div id="next" class="epr-arrow">›</div>
 
     <script>
+        let partial_md5 = "{{ book.partial_md5 }}";
         let book = ePub("{{ book.original_file.url }}");
         let rendition = book.renderTo("viewer", {
             manager: "continuous",
@@ -49,11 +50,26 @@
             let displayed = rendition.display();
         {% endif %}
 
+        // Generate locations
+        book.ready.then(function(){
+            const stored = localStorage.getItem(partial_md5 + "-locations");
+            if (stored) {
+                return book.locations.load(stored);
+            } else {
+                return book.locations.generate(300);
+            }
+        }).then(function(location) {
+            localStorage.setItem(partial_md5 + "-locations", book.locations.save());
+            console.log("Locations generated");
+        });
+
         {% include "reader/book_read/bookreader_save.js" %}
 
         rendition.on("relocated", function(location){
             console.log(location);
-            saveProgress(location.start.cfi, rendition.location.start.percentage / 100);
+            let percentage = location.start.percentage;
+            if (location.atEnd) percentage = 1; // Fix for last page
+            saveProgress(location.start.cfi, percentage);
         });
 
         displayed.then(function(renderer){