diff --git a/unpublishedScripts/marketplace/shapes/modules/createPalette.js b/unpublishedScripts/marketplace/shapes/modules/createPalette.js
index 0eea8379d6..38dad5b827 100644
--- a/unpublishedScripts/marketplace/shapes/modules/createPalette.js
+++ b/unpublishedScripts/marketplace/shapes/modules/createPalette.js
@@ -8,7 +8,7 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-/* global CreatePalette: true, App, Feedback, History, UIT */
+/* global CreatePalette:true, App, Feedback, History, Preload, UIT */
 
 CreatePalette = function (side, leftInputs, rightInputs, uiCommandCallback) {
     // Tool menu displayed on top of forearm.
@@ -332,6 +332,11 @@ CreatePalette = function (side, leftInputs, rightInputs, uiCommandCallback) {
     }
 
 
+    function getAssetURLs() {
+        return Preload.findURLs([PALETTE_HEADER_HEADING_PROPERTIES, PALETTE_HEADER_BAR_PROPERTIES, PALETTE_TITLE_PROPERTIES,
+            PALETTE_ITEMS]);
+    }
+
     function setHand(hand) {
         // Assumes UI is not displaying.
         var NUMBER_OF_HANDS = 2;
@@ -533,6 +538,7 @@ CreatePalette = function (side, leftInputs, rightInputs, uiCommandCallback) {
     }
 
     return {
+        assetURLs: getAssetURLs,
         setHand: setHand,
         overlayIDs: getOverlayIDs,
         setVisible: setVisible,
diff --git a/unpublishedScripts/marketplace/shapes/modules/preload.js b/unpublishedScripts/marketplace/shapes/modules/preload.js
new file mode 100644
index 0000000000..8ffc8dcce3
--- /dev/null
+++ b/unpublishedScripts/marketplace/shapes/modules/preload.js
@@ -0,0 +1,165 @@
+//
+//  preload.js
+//
+//  Created by David Rowe on 11 Oct 2017.
+//  Copyright 2017 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+/* global Preload:true, App */
+
+Preload = (function () {
+    // Provide facility to preload asset files so that they are in disk cache.
+    // Global object.
+
+    "use strict";
+
+    var loadTimer = null,
+        urlsToLoad = [],
+        nextURLToLoad = 0,
+        LOAD_INTERVAL = 50, // ms
+        overlays = [],
+        deleteTimer = null,
+        DELETE_INTERVAL = LOAD_INTERVAL; // ms
+
+    function findURLs(items) {
+        var urls = [],
+            i,
+            length;
+
+        function findURLsInObject(item) {
+            var property;
+
+            for (property in item) {
+                if (item.hasOwnProperty(property)) {
+                    if (property === "url") {
+                        urls.push(item.url);
+                    } else if (typeof item[property] === "object") {
+                        findURLsInObject(item[property]);
+                    }
+                }
+            }
+        }
+
+        for (i = 0, length = items.length; i < length; i++) {
+            switch (typeof items[i]) {
+                case "string":
+                    urls.push(items[i]);
+                    break;
+                case "object":
+                    findURLsInObject(items[i]);
+                    break;
+                default:
+                    App.log("ERROR: Cannot find URL in item type " + (typeof items[i]));
+            }
+        }
+
+        return urls;
+    }
+
+    function deleteOverlay() {
+        if (overlays.length === 0) { // Just in case.
+            deleteTimer = null;
+            return;
+        }
+
+        Overlays.deleteOverlay(overlays[0]);
+        overlays.shift();
+
+        if (overlays.length > 0) {
+            deleteTimer = Script.setTimeout(deleteOverlay, DELETE_INTERVAL);
+        } else {
+            deleteTimer = null;
+        }
+    }
+
+    function loadNextURL() {
+
+        function loadURL(url) {
+            var DOMAIN_CORNER = { x: -16382, y: -16382, z: -16382 }, // Near but not right on domain corner.
+                DUMMY_OVERLAY_PROPERTIES = {
+                    fbx: {
+                        overlay: "model",
+                        dimensions: { x: 0.001, y: 0.001, z: 0.001 },
+                        position: DOMAIN_CORNER,
+                        ignoreRayIntersection: false,
+                        alpha: 0.0,
+                        visible: false
+                    },
+                    svg: {
+                        overlay: "image3d",
+                        scale: 0.001,
+                        position: DOMAIN_CORNER,
+                        ignoreRayIntersection: true,
+                        alpha: 0.0,
+                        visible: false
+                    }
+                },
+                fileType,
+                properties;
+
+            fileType = url.slice(-3);
+            if (DUMMY_OVERLAY_PROPERTIES.hasOwnProperty(fileType)) {
+                properties = Object.clone(DUMMY_OVERLAY_PROPERTIES[fileType]);
+                properties.url = url;
+                overlays.push(Overlays.addOverlay(properties.overlay, properties));
+                if (deleteTimer === null) {
+                    // Can't delete overlay straight away otherwise asset load is abandoned.
+                    deleteTimer = Script.setTimeout(deleteOverlay, DELETE_INTERVAL);
+                }
+            } else {
+                App.log("ERROR: Cannot preload asset " + url);
+            }
+        }
+
+        // Find next URL that hasn't already been loaded;
+        while (nextURLToLoad < urlsToLoad.length && urlsToLoad.indexOf(urlsToLoad[nextURLToLoad]) < nextURLToLoad) {
+            nextURLToLoad += 1;
+        }
+
+        // Load the URL if there's one to load.
+        if (nextURLToLoad < urlsToLoad.length) {
+            // Load the URL.
+            loadURL(urlsToLoad[nextURLToLoad]);
+            nextURLToLoad += 1;
+
+            // Load the next, if any, after a short delay.
+            loadTimer = Script.setTimeout(loadNextURL, LOAD_INTERVAL);
+        } else {
+            loadTimer = null;
+        }
+    }
+
+    function load(urls) {
+        urlsToLoad = urlsToLoad.concat(urls);
+        if (loadTimer === null) {
+            loadNextURL();
+        }
+    }
+
+    function tearDown() {
+        var i,
+            length;
+
+        if (loadTimer) {
+            Script.clearTimeout(loadTimer);
+        }
+
+        if (deleteTimer) {
+            Script.clearTimeout(deleteTimer);
+            for (i = 0, length = overlays.length; i < length; i++) {
+                Overlays.deleteOverlay(overlays[i]);
+            }
+        }
+    }
+
+    Script.scriptEnding.connect(tearDown);
+
+    return {
+        findURLs: findURLs,
+        load: load
+    };
+
+}());
diff --git a/unpublishedScripts/marketplace/shapes/modules/toolIcon.js b/unpublishedScripts/marketplace/shapes/modules/toolIcon.js
index 1571a6b037..143d768466 100644
--- a/unpublishedScripts/marketplace/shapes/modules/toolIcon.js
+++ b/unpublishedScripts/marketplace/shapes/modules/toolIcon.js
@@ -72,6 +72,10 @@ ToolIcon = function (side) {
         return new ToolIcon();
     }
 
+    function getAssetURLs() {
+        return [MODEL_PROPERTIES.url];
+    }
+
     function setHand(side) {
         // Assumes UI is not displaying.
         if (side === LEFT_HAND) {
@@ -154,6 +158,7 @@ ToolIcon = function (side) {
     }
 
     return {
+        assetURLs: getAssetURLs,
         setHand: setHand,
         display: display,
         clear: clear,
diff --git a/unpublishedScripts/marketplace/shapes/modules/toolsMenu.js b/unpublishedScripts/marketplace/shapes/modules/toolsMenu.js
index 19c114c8e9..04bb88d040 100644
--- a/unpublishedScripts/marketplace/shapes/modules/toolsMenu.js
+++ b/unpublishedScripts/marketplace/shapes/modules/toolsMenu.js
@@ -8,7 +8,7 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-/* global ToolsMenu: true, App, Feedback, UIT */
+/* global ToolsMenu:true, App, Feedback, Preload, UIT */
 
 ToolsMenu = function (side, leftInputs, rightInputs, uiCommandCallback) {
     // Tool menu displayed on top of forearm.
@@ -2042,6 +2042,12 @@ ToolsMenu = function (side, leftInputs, rightInputs, uiCommandCallback) {
         return new ToolsMenu();
     }
 
+    function getAssetURLs() {
+        return Preload.findURLs([MENU_HEADER_HEADING_PROPERTIES, MENU_HEADER_BAR_PROPERTIES, MENU_HEADER_BACK_PROPERTIES,
+            MENU_HEADER_TITLE_PROPERTIES, MENU_HEADER_TITLE_BACK_URL, MENU_HEADER_ICON_PROPERTIES, UI_ELEMENTS,
+            PICKLIST_UP_ARROW, PICKLIST_DOWN_ARROW, OPTONS_PANELS, MENU_ITEMS, FOOTER_ITEMS]);
+    }
+
     controlHand = side === LEFT_HAND ? rightInputs.hand() : leftInputs.hand();
 
     function setHand(hand) {
@@ -3672,6 +3678,7 @@ ToolsMenu = function (side, leftInputs, rightInputs, uiCommandCallback) {
     }
 
     return {
+        assetURLs: getAssetURLs,
         COLOR_TOOL: COLOR_TOOL,
         SCALE_TOOL: SCALE_TOOL,
         CLONE_TOOL: CLONE_TOOL,
diff --git a/unpublishedScripts/marketplace/shapes/shapes.js b/unpublishedScripts/marketplace/shapes/shapes.js
index 290424f55e..cd5f119588 100644
--- a/unpublishedScripts/marketplace/shapes/shapes.js
+++ b/unpublishedScripts/marketplace/shapes/shapes.js
@@ -8,7 +8,7 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-/* global Feedback, History */
+/* global Feedback, History, Preload */
 
 (function () {
 
@@ -87,6 +87,7 @@
     Script.include("./modules/highlights.js");
     Script.include("./modules/history.js");
     Script.include("./modules/laser.js");
+    Script.include("./modules/preload.js");
     Script.include("./modules/selection.js");
     Script.include("./modules/toolIcon.js");
     Script.include("./modules/toolsMenu.js");
@@ -235,8 +236,12 @@
         }
 
         toolIcon = new ToolIcon(otherHand(side));
-        toolsMenu = new ToolsMenu(side, leftInputs, rightInputs, uiCommandCallback);
         createPalette = new CreatePalette(side, leftInputs, rightInputs, uiCommandCallback);
+        toolsMenu = new ToolsMenu(side, leftInputs, rightInputs, uiCommandCallback);
+
+        Preload.load(toolIcon.assetURLs());
+        Preload.load(createPalette.assetURLs());
+        Preload.load(toolsMenu.assetURLs());
 
         getIntersection = side === LEFT_HAND ? rightInputs.intersection : leftInputs.intersection;