diff --git a/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java b/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java
index 6428044df0..a7bda3c29b 100644
--- a/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java
+++ b/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java
@@ -24,6 +24,7 @@ import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Vibrator;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -54,6 +55,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
     public static final String DOMAIN_URL = "url";
     public static final String EXTRA_GOTO_USERNAME = "gotousername";
     private static final String TAG = "Interface";
+    public static final String EXTRA_ARGS = "args";
     private static final int WEB_DRAWER_RIGHT_MARGIN = 262;
     private static final int WEB_DRAWER_BOTTOM_MARGIN = 150;
     private static final int NORMAL_DPI = 160;
@@ -78,6 +80,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
 
     private boolean nativeEnterBackgroundCallEnqueued = false;
     private SlidingDrawer mWebSlidingDrawer;
+    private boolean mStartInDomain;
 //    private GvrApi gvrApi;
     // Opaque native pointer to the Application C++ object.
     // This object is owned by the InterfaceActivity instance and passed to the native methods.
@@ -93,8 +96,14 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
     public void onCreate(Bundle savedInstanceState) {
         super.isLoading = true;
         Intent intent = getIntent();
-        if (intent.hasExtra(DOMAIN_URL) && !intent.getStringExtra(DOMAIN_URL).isEmpty()) {
+        if (intent.hasExtra(DOMAIN_URL) && !TextUtils.isEmpty(intent.getStringExtra(DOMAIN_URL))) {
             intent.putExtra("applicationArguments", "--url " + intent.getStringExtra(DOMAIN_URL));
+        } else if (intent.hasExtra(EXTRA_ARGS)) {
+            String args = intent.getStringExtra(EXTRA_ARGS);
+            if (!TextUtils.isEmpty(args)) {
+                mStartInDomain = true;
+                intent.putExtra("applicationArguments", args);
+            }
         }
         super.onCreate(savedInstanceState);
         getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
@@ -125,7 +134,10 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
                 getActionBar().hide();
             }
         });
-        startActivity(new Intent(this, SplashActivity.class));
+        Intent splashIntent = new Intent(this, SplashActivity.class);
+        splashIntent.putExtra(SplashActivity.EXTRA_START_IN_DOMAIN, mStartInDomain);
+        startActivity(splashIntent);
+        
         mVibrator = (Vibrator) this.getSystemService(VIBRATOR_SERVICE);
         headsetStateReceiver = new HeadsetStateReceiver();
     }
diff --git a/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java b/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java
index 78a6421746..ef9876c71a 100644
--- a/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java
+++ b/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java
@@ -9,6 +9,7 @@ import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.View;
+import android.text.TextUtils;
 
 import org.json.JSONException;
 import org.json.JSONObject;
@@ -27,9 +28,14 @@ public class PermissionChecker extends Activity {
     private static final boolean CHOOSE_AVATAR_ON_STARTUP = false;
     private static final String TAG = "Interface";
 
+    private static final String EXTRA_ARGS = "args";
+    private String mArgs;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        mArgs =(getIntent().getStringExtra(EXTRA_ARGS));
+
         Intent myIntent = new Intent(this, BreakpadUploaderService.class);
         startService(myIntent);
         if (CHOOSE_AVATAR_ON_STARTUP) {
@@ -76,6 +82,11 @@ public class PermissionChecker extends Activity {
 
     private void launchActivityWithPermissions(){
         Intent i = new Intent(this, InterfaceActivity.class);
+        
+        if (!TextUtils.isEmpty(mArgs)) {
+            i.putExtra(EXTRA_ARGS, mArgs);
+        }
+
         startActivity(i);
         finish();
     }
diff --git a/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/SplashActivity.java b/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/SplashActivity.java
index bb42467ace..536bf23603 100644
--- a/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/SplashActivity.java
+++ b/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/SplashActivity.java
@@ -7,6 +7,9 @@ import android.view.View;
 
 public class SplashActivity extends Activity {
 
+    public static final String EXTRA_START_IN_DOMAIN = "start-in-domain";
+    private boolean mStartInDomain;
+
     private native void registerLoadCompleteListener();
 
     @Override
@@ -36,13 +39,27 @@ public class SplashActivity extends Activity {
     }
 
     public void onAppLoadedComplete() {
-        if (HifiUtils.getInstance().isUserLoggedIn()) {
-            startActivity(new Intent(this, MainActivity.class));
-        } else {
-            Intent menuIntent =  new Intent(this, LoginMenuActivity.class);
-            menuIntent.putExtra(LoginMenuActivity.EXTRA_FINISH_ON_BACK, true);
-            startActivity(menuIntent);
+        if (!mStartInDomain) {
+            if (HifiUtils.getInstance().isUserLoggedIn()) {
+                startActivity(new Intent(this, MainActivity.class));
+            } else {
+                Intent menuIntent = new Intent(this, LoginMenuActivity.class);
+                menuIntent.putExtra(LoginMenuActivity.EXTRA_FINISH_ON_BACK, true);
+                startActivity(menuIntent);
+            }
         }
         SplashActivity.this.finish();
     }
+   
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putBoolean(EXTRA_START_IN_DOMAIN, mStartInDomain);
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle savedInstanceState) {
+        super.onRestoreInstanceState(savedInstanceState);
+        mStartInDomain = savedInstanceState.getBoolean(EXTRA_START_IN_DOMAIN, false);
+    }
 }
diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp
index 8b7c8771e8..a6542689e0 100644
--- a/assignment-client/src/entities/EntityTreeSendThread.cpp
+++ b/assignment-client/src/entities/EntityTreeSendThread.cpp
@@ -111,7 +111,7 @@ bool EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O
         int32_t lodLevelOffset = nodeData->getBoundaryLevelAdjust() + (viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
         newView.lodScaleFactor = powf(2.0f, lodLevelOffset);
         
-        startNewTraversal(newView, root);
+        startNewTraversal(newView, root, isFullScene);
 
         // When the viewFrustum changed the sort order may be incorrect, so we re-sort
         // and also use the opportunity to cull anything no longer in view
@@ -220,9 +220,10 @@ bool EntityTreeSendThread::addDescendantsToExtraFlaggedEntities(const QUuid& fil
     return hasNewChild || hasNewDescendants;
 }
 
-void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root) {
+void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root,
+                                             bool forceFirstPass) {
 
-    DiffTraversal::Type type = _traversal.prepareNewTraversal(view, root);
+    DiffTraversal::Type type = _traversal.prepareNewTraversal(view, root, forceFirstPass);
     // there are three types of traversal:
     //
     //      (1) FirstTime = at login --> find everything in view
diff --git a/assignment-client/src/entities/EntityTreeSendThread.h b/assignment-client/src/entities/EntityTreeSendThread.h
index 199769ca09..7eedc2f1ba 100644
--- a/assignment-client/src/entities/EntityTreeSendThread.h
+++ b/assignment-client/src/entities/EntityTreeSendThread.h
@@ -42,7 +42,7 @@ private:
     bool addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData);
     bool addDescendantsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData);
 
-    void startNewTraversal(const DiffTraversal::View& viewFrustum, EntityTreeElementPointer root);
+    void startNewTraversal(const DiffTraversal::View& viewFrustum, EntityTreeElementPointer root, bool forceFirstPass = false);
     bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) override;
 
     void preDistributionProcessing() override;
diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt
index 0bfed88d9c..74e25662c7 100644
--- a/interface/CMakeLists.txt
+++ b/interface/CMakeLists.txt
@@ -333,7 +333,11 @@ if (APPLE)
     COMMAND "${CMAKE_COMMAND}" -E copy_directory
       "${PROJECT_SOURCE_DIR}/resources/fonts"
       "${RESOURCES_DEV_DIR}/fonts"
-    # add redirect json to macOS builds.
+     #copy serverless for android
+     COMMAND "${CMAKE_COMMAND}" -E copy_directory
+     "${PROJECT_SOURCE_DIR}/resources/serverless"
+     "${RESOURCES_DEV_DIR}/serverless"
+      # add redirect json to macOS builds.
     COMMAND "${CMAKE_COMMAND}" -E copy_if_different
       "${PROJECT_SOURCE_DIR}/resources/serverless/redirect.json"
       "${RESOURCES_DEV_DIR}/serverless/redirect.json"
diff --git a/interface/resources/qml/+webengine/Browser.qml b/interface/resources/qml/+webengine/Browser.qml
deleted file mode 100644
index 52157bdf42..0000000000
--- a/interface/resources/qml/+webengine/Browser.qml
+++ /dev/null
@@ -1,275 +0,0 @@
-import QtQuick 2.5
-import QtWebChannel 1.0
-import QtWebEngine 1.5
-
-import controlsUit 1.0
-import stylesUit 1.0
-import "qrc:////qml//windows"
-
-ScrollingWindow {
-    id: root
-    HifiConstants { id: hifi }
-    //HifiStyles.HifiConstants { id: hifistyles }
-    title: "Browser"
-    resizable: true
-    destroyOnHidden: true
-    width: 800
-    height: 600
-    property variant permissionsBar: {'securityOrigin':'none','feature':'none'}
-    property alias url: webview.url
-    property alias webView: webview
-
-    signal loadingChanged(int status)
-
-    x: 100
-    y: 100
-
-    Component.onCompleted: {
-        focus = true
-        shown = true
-        addressBar.text = webview.url
-    }
-
-    function setProfile(profile) {
-        webview.profile = profile;
-    }
-
-    function showPermissionsBar(){
-        permissionsContainer.visible=true;
-    }
-
-    function hidePermissionsBar(){
-      permissionsContainer.visible=false;
-    }
-
-    function allowPermissions(){
-        webview.grantFeaturePermission(permissionsBar.securityOrigin, permissionsBar.feature, true);
-        hidePermissionsBar();
-    }
-
-    function setAutoAdd(auto) {
-        desktop.setAutoAdd(auto);
-    }
-
-    Item {
-        id:item
-        width: pane.contentWidth
-        implicitHeight: pane.scrollHeight
-
-        Row {
-            id: buttons
-            spacing: 4
-            anchors.top: parent.top
-            anchors.topMargin: 8
-            anchors.left: parent.left
-            anchors.leftMargin: 8
-            HiFiGlyphs {
-                id: back;
-                enabled: webview.canGoBack;
-                text: hifi.glyphs.backward
-                color: enabled ? hifi.colors.text : hifi.colors.disabledText
-                size: 48
-                MouseArea { anchors.fill: parent;  onClicked: webview.goBack() }
-            }
-
-            HiFiGlyphs {
-                id: forward;
-                enabled: webview.canGoForward;
-                text: hifi.glyphs.forward
-                color: enabled ? hifi.colors.text : hifi.colors.disabledText
-                size: 48
-                MouseArea { anchors.fill: parent;  onClicked: webview.goForward() }
-            }
-
-            HiFiGlyphs {
-                id: reload;
-                enabled: webview.canGoForward;
-                text: webview.loading ? hifi.glyphs.close : hifi.glyphs.reload
-                color: enabled ? hifi.colors.text : hifi.colors.disabledText
-                size: 48
-                MouseArea { anchors.fill: parent;  onClicked: webview.goForward() }
-            }
-
-        }
-
-        Item {
-            id: border
-            height: 48
-            anchors.top: parent.top
-            anchors.topMargin: 8
-            anchors.right: parent.right
-            anchors.rightMargin: 8
-            anchors.left: buttons.right
-            anchors.leftMargin: 8
-    
-            Item {
-                id: barIcon
-                width: parent.height
-                height: parent.height
-                Image {
-                    source: webview.icon;
-                    x: (parent.height - height) / 2
-                    y: (parent.width - width) / 2
-                    sourceSize: Qt.size(width, height);
-                    verticalAlignment: Image.AlignVCenter;
-                    horizontalAlignment: Image.AlignHCenter
-                }
-            }
-
-            TextField {
-                id: addressBar
-                anchors.right: parent.right
-                anchors.rightMargin: 8
-                anchors.left: barIcon.right
-                anchors.leftMargin: 0
-                anchors.verticalCenter: parent.verticalCenter
-                focus: true
-                colorScheme: hifi.colorSchemes.dark
-                placeholderText: "Enter URL"
-                Component.onCompleted: ScriptDiscoveryService.scriptsModelFilter.filterRegExp = new RegExp("^.*$", "i")
-                Keys.onPressed: {
-                    switch(event.key) {
-                        case Qt.Key_Enter:
-                        case Qt.Key_Return:
-                            event.accepted = true
-                            if (text.indexOf("http") != 0) {
-                                text = "http://" + text;
-                            }
-                            root.hidePermissionsBar();
-                            root.keyboardRaised = false;
-                            webview.url = text;
-                            break;
-                    }
-                }
-            }
-        }
-
-        Rectangle {
-            id:permissionsContainer
-            visible:false
-            color: "#000000"
-            width:  parent.width
-            anchors.top: buttons.bottom
-            height:40
-            z:100
-            gradient: Gradient {
-                GradientStop { position: 0.0; color: "black" }
-                GradientStop { position: 1.0; color: "grey" }
-            }
-
-            RalewayLight {
-                    id: permissionsInfo
-                    anchors.right:permissionsRow.left
-                    anchors.rightMargin: 32
-                    anchors.topMargin:8
-                    anchors.top:parent.top
-                    text: "This site wants to use your microphone/camera"
-                    size: 18
-                    color: hifi.colors.white
-            }
-         
-            Row {
-                id: permissionsRow
-                spacing: 4
-                anchors.top:parent.top
-                anchors.topMargin: 8
-                anchors.right: parent.right
-                visible: true
-                z:101
-                
-                Button {
-                    id:allow
-                    text: "Allow"
-                    color: hifi.buttons.blue
-                    colorScheme: root.colorScheme
-                    width: 120
-                    enabled: true
-                    onClicked: root.allowPermissions(); 
-                    z:101
-                }
-
-                Button {
-                    id:block
-                    text: "Block"
-                    color: hifi.buttons.red
-                    colorScheme: root.colorScheme
-                    width: 120
-                    enabled: true
-                    onClicked: root.hidePermissionsBar();
-                    z:101
-                }
-            }
-        }
-
-        WebView {
-            id: webview
-            url: "https://highfidelity.com/"
-            profile: FileTypeProfile;
-
-            // Create a global EventBridge object for raiseAndLowerKeyboard.
-            WebEngineScript {
-                id: createGlobalEventBridge
-                sourceCode: eventBridgeJavaScriptToInject
-                injectionPoint: WebEngineScript.Deferred
-                worldId: WebEngineScript.MainWorld
-            }
-
-            // Detect when may want to raise and lower keyboard.
-            WebEngineScript {
-                id: raiseAndLowerKeyboard
-                injectionPoint: WebEngineScript.Deferred
-                sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
-                worldId: WebEngineScript.MainWorld
-            }
-
-            userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
-
-            anchors.top: buttons.bottom
-            anchors.topMargin: 8
-            anchors.bottom: parent.bottom
-            anchors.left: parent.left
-            anchors.right: parent.right
-
-            onFeaturePermissionRequested: {
-                if (feature == 2) { // QWebEnginePage::MediaAudioCapture
-                    grantFeaturePermission(securityOrigin, feature, true);
-                } else {
-                    permissionsBar.securityOrigin = securityOrigin;
-                    permissionsBar.feature = feature;
-                    root.showPermissionsBar();
-                }
-            }
-
-            onLoadingChanged: {
-                if (loadRequest.status === WebEngineView.LoadSucceededStatus) {
-                    addressBar.text = loadRequest.url
-                }
-                root.loadingChanged(loadRequest.status);
-            }
-
-            onWindowCloseRequested: {
-                root.destroy();
-            }
-
-            Component.onCompleted: {
-                webChannel.registerObject("eventBridge", eventBridge);
-                webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
-                desktop.initWebviewProfileHandlers(webview.profile);
-            }
-        }
-
-    } // item
-
-
-    Keys.onPressed: {
-        switch(event.key) {
-            case Qt.Key_L:
-                if (event.modifiers == Qt.ControlModifier) {
-                    event.accepted = true
-                    addressBar.selectAll()
-                    addressBar.forceActiveFocus()
-                }
-                break;
-        }
-    }
-} // dialog
diff --git a/interface/resources/qml/+webengine/BrowserWebView.qml b/interface/resources/qml/+webengine/BrowserWebView.qml
new file mode 100644
index 0000000000..5c5cf2cfb9
--- /dev/null
+++ b/interface/resources/qml/+webengine/BrowserWebView.qml
@@ -0,0 +1,58 @@
+import QtQuick 2.5
+import QtWebChannel 1.0
+import QtWebEngine 1.5
+
+import controlsUit 1.0
+
+WebView {
+    id: webview
+    url: "https://highfidelity.com/"
+    profile: FileTypeProfile;
+
+    property var parentRoot: null
+
+    // Create a global EventBridge object for raiseAndLowerKeyboard.
+    WebEngineScript {
+        id: createGlobalEventBridge
+        sourceCode: eventBridgeJavaScriptToInject
+        injectionPoint: WebEngineScript.Deferred
+        worldId: WebEngineScript.MainWorld
+    }
+
+    // Detect when may want to raise and lower keyboard.
+    WebEngineScript {
+        id: raiseAndLowerKeyboard
+        injectionPoint: WebEngineScript.Deferred
+        sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
+        worldId: WebEngineScript.MainWorld
+    }
+
+    userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
+
+    onFeaturePermissionRequested: {
+        if (feature == 2) { // QWebEnginePage::MediaAudioCapture
+            grantFeaturePermission(securityOrigin, feature, true);
+        } else {
+            permissionsBar.securityOrigin = securityOrigin;
+            permissionsBar.feature = feature;
+            parentRoot.showPermissionsBar();
+        }
+    }
+
+    onLoadingChanged: {
+        if (loadRequest.status === WebEngineView.LoadSucceededStatus) {
+            addressBar.text = loadRequest.url
+        }
+        parentRoot.loadingChanged(loadRequest.status);
+    }
+
+    onWindowCloseRequested: {
+        parentRoot.destroy();
+    }
+
+    Component.onCompleted: {
+        webChannel.registerObject("eventBridge", eventBridge);
+        webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
+        desktop.initWebviewProfileHandlers(webview.profile);
+    }
+}
diff --git a/interface/resources/qml/+webengine/InfoView.qml b/interface/resources/qml/+webengine/InfoView.qml
deleted file mode 100644
index eb190c3c45..0000000000
--- a/interface/resources/qml/+webengine/InfoView.qml
+++ /dev/null
@@ -1,50 +0,0 @@
-//
-//  InfoView.qml
-//
-//  Created by Bradley Austin Davis on 27 Apr 2015
-//  Copyright 2015 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
-//
-
-import QtQuick 2.5
-import Hifi 1.0 as Hifi
-
-import controlsUit 1.0
-import "qrc:////qml//windows" as Windows
-
-Windows.ScrollingWindow {
-    id: root
-    width: 800
-    height: 800
-    resizable: true
-    
-    Hifi.InfoView {
-        id: infoView
-        width: pane.contentWidth
-        implicitHeight: pane.scrollHeight
-
-        WebView {
-            id: webview
-            objectName: "WebView"
-            anchors.fill: parent
-            url: infoView.url
-        }
-    }
-
-    Component.onCompleted: {
-        centerWindow(root);
-    }
-
-    onVisibleChanged: {
-        if (visible) {
-            centerWindow(root);
-        }
-    }
-
-    function centerWindow() {
-        desktop.centerOnVisible(root);
-    }
-
-}
diff --git a/interface/resources/qml/+webengine/QmlWebWindow.qml b/interface/resources/qml/+webengine/QmlWebWindow.qml
deleted file mode 100644
index 2e3718f6f5..0000000000
--- a/interface/resources/qml/+webengine/QmlWebWindow.qml
+++ /dev/null
@@ -1,108 +0,0 @@
-//
-//  QmlWebWindow.qml
-//
-//  Created by Bradley Austin Davis on 17 Dec 2015
-//  Copyright 2015 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
-//
-
-import QtQuick 2.5
-import QtWebEngine 1.1
-import QtWebChannel 1.0
-
-import "qrc:////qml//windows" as Windows
-import controlsUit 1.0 as Controls
-import stylesUit 1.0
-
-Windows.ScrollingWindow {
-    id: root
-    HifiConstants { id: hifi }
-    title: "WebWindow"
-    resizable: true
-    shown: false
-    // Don't destroy on close... otherwise the JS/C++ will have a dangling pointer
-    destroyOnCloseButton: false
-    property alias source: webview.url
-    property alias scriptUrl: webview.userScriptUrl
-
-    // This is for JS/QML communication, which is unused in a WebWindow,
-    // but not having this here results in spurious warnings about a 
-    // missing signal
-    signal sendToScript(var message);
-
-    signal moved(vector2d position);
-    signal resized(size size);
-
-    function notifyMoved() {
-        moved(Qt.vector2d(x, y));
-    }
-
-    function notifyResized() {
-        resized(Qt.size(width, height));
-    }
-
-    onXChanged: notifyMoved();
-    onYChanged: notifyMoved();
-
-    onWidthChanged: notifyResized();
-    onHeightChanged: notifyResized();
-
-    onShownChanged: {
-        keyboardEnabled = HMD.active;
-    }
-
-    Item {
-        width: pane.contentWidth
-        implicitHeight: pane.scrollHeight
-
-        Controls.WebView {
-            id: webview
-            url: "about:blank"
-            anchors.fill: parent
-            focus: true
-            profile: HFWebEngineProfile;
-
-            property string userScriptUrl: ""
-
-            // Create a global EventBridge object for raiseAndLowerKeyboard.
-            WebEngineScript {
-                id: createGlobalEventBridge
-                sourceCode: eventBridgeJavaScriptToInject
-                injectionPoint: WebEngineScript.DocumentCreation
-                worldId: WebEngineScript.MainWorld
-            }
-
-            // Detect when may want to raise and lower keyboard.
-            WebEngineScript {
-                id: raiseAndLowerKeyboard
-                injectionPoint: WebEngineScript.Deferred
-                sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
-                worldId: WebEngineScript.MainWorld
-            }
-
-            // User script.
-            WebEngineScript {
-                id: userScript
-                sourceUrl: webview.userScriptUrl
-                injectionPoint: WebEngineScript.DocumentReady  // DOM ready but page load may not be finished.
-                worldId: WebEngineScript.MainWorld
-            }
-
-            userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
-
-            function onWebEventReceived(event) {
-                if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") {
-                    ApplicationInterface.addAssetToWorldFromURL(event.slice(18));
-                }
-            }
-
-            Component.onCompleted: {
-                webChannel.registerObject("eventBridge", eventBridge);
-                webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
-                eventBridge.webEventReceived.connect(onWebEventReceived);
-            }
-        }
-    }
-}
diff --git a/interface/resources/qml/+webengine/QmlWebWindowView.qml b/interface/resources/qml/+webengine/QmlWebWindowView.qml
new file mode 100644
index 0000000000..d2f1820e9a
--- /dev/null
+++ b/interface/resources/qml/+webengine/QmlWebWindowView.qml
@@ -0,0 +1,53 @@
+import QtQuick 2.5
+import QtWebEngine 1.1
+import QtWebChannel 1.0
+
+import controlsUit 1.0 as Controls
+import stylesUit 1.0
+Controls.WebView {
+    id: webview
+    url: "about:blank"
+    anchors.fill: parent
+    focus: true
+    profile: HFWebEngineProfile;
+
+    property string userScriptUrl: ""
+
+    // Create a global EventBridge object for raiseAndLowerKeyboard.
+    WebEngineScript {
+        id: createGlobalEventBridge
+        sourceCode: eventBridgeJavaScriptToInject
+        injectionPoint: WebEngineScript.DocumentCreation
+        worldId: WebEngineScript.MainWorld
+    }
+
+    // Detect when may want to raise and lower keyboard.
+    WebEngineScript {
+        id: raiseAndLowerKeyboard
+        injectionPoint: WebEngineScript.Deferred
+        sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
+        worldId: WebEngineScript.MainWorld
+    }
+
+    // User script.
+    WebEngineScript {
+        id: userScript
+        sourceUrl: webview.userScriptUrl
+        injectionPoint: WebEngineScript.DocumentReady  // DOM ready but page load may not be finished.
+        worldId: WebEngineScript.MainWorld
+    }
+
+    userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
+
+    function onWebEventReceived(event) {
+        if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") {
+            ApplicationInterface.addAssetToWorldFromURL(event.slice(18));
+        }
+    }
+
+    Component.onCompleted: {
+        webChannel.registerObject("eventBridge", eventBridge);
+        webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
+        eventBridge.webEventReceived.connect(onWebEventReceived);
+    }
+}
diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml
index 78ffb51e67..496209a2a8 100644
--- a/interface/resources/qml/Browser.qml
+++ b/interface/resources/qml/Browser.qml
@@ -1,14 +1,13 @@
 import QtQuick 2.5
-
 import controlsUit 1.0
 import stylesUit 1.0
-
 import "windows"
+import "."
 
 ScrollingWindow {
     id: root
     HifiConstants { id: hifi }
-    //HifiStyles.HifiConstants { id: hifistyles }
+
     title: "Browser"
     resizable: true
     destroyOnHidden: true
@@ -30,6 +29,7 @@ ScrollingWindow {
     }
 
     function setProfile(profile) {
+        webview.profile = profile;
     }
 
     function showPermissionsBar(){
@@ -41,6 +41,7 @@ ScrollingWindow {
     }
 
     function allowPermissions(){
+        webview.grantFeaturePermission(permissionsBar.securityOrigin, permissionsBar.feature, true);
         hidePermissionsBar();
     }
 
@@ -198,10 +199,15 @@ ScrollingWindow {
             }
         }
 
-        ProxyWebView {
+        BrowserWebView {
             id: webview
-            anchors.centerIn: parent
-            url: "https://highfidelity.com/"
+            parentRoot: root
+
+            anchors.top: buttons.bottom
+            anchors.topMargin: 8
+            anchors.bottom: parent.bottom
+            anchors.left: parent.left
+            anchors.right: parent.right
         }
 
     } // item
diff --git a/interface/resources/qml/BrowserWebView.qml b/interface/resources/qml/BrowserWebView.qml
new file mode 100644
index 0000000000..6db016c284
--- /dev/null
+++ b/interface/resources/qml/BrowserWebView.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.5
+import controlsUit 1.0
+
+ProxyWebView {
+    property var parentRoot: null
+
+    function grantFeaturePermission(origin, feature) {}
+}
diff --git a/interface/resources/qml/InfoView.qml b/interface/resources/qml/InfoView.qml
index 5c2c7fcff9..2fd0ddf925 100644
--- a/interface/resources/qml/InfoView.qml
+++ b/interface/resources/qml/InfoView.qml
@@ -24,7 +24,7 @@ Windows.ScrollingWindow {
         width: pane.contentWidth
         implicitHeight: pane.scrollHeight
 
-        ProxyWebView {
+        BaseWebView {
             id: webview
             objectName: "WebView"
             anchors.fill: parent
diff --git a/interface/resources/qml/QmlWebWindow.qml b/interface/resources/qml/QmlWebWindow.qml
index a40168039e..7ee4b1c50c 100644
--- a/interface/resources/qml/QmlWebWindow.qml
+++ b/interface/resources/qml/QmlWebWindow.qml
@@ -11,6 +11,7 @@
 import QtQuick 2.5
 
 import "windows" as Windows
+import "."
 import controlsUit 1.0 as Controls
 import stylesUit 1.0
 
@@ -55,12 +56,8 @@ Windows.ScrollingWindow {
         width: pane.contentWidth
         implicitHeight: pane.scrollHeight
 
-        Controls.WebView {
+        QmlWebWindowView {
             id: webview
-            url: "about:blank"
-            property string userScriptUrl: ""
-            anchors.fill: parent
-            focus: true
         }
     }
 }
diff --git a/interface/resources/qml/QmlWebWindowView.qml b/interface/resources/qml/QmlWebWindowView.qml
new file mode 100644
index 0000000000..9210468ae2
--- /dev/null
+++ b/interface/resources/qml/QmlWebWindowView.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.5
+import controlsUit 1.0
+
+BaseWebView {
+}
diff --git a/interface/resources/qml/controlsUit/ProxyWebView.qml b/interface/resources/qml/controlsUit/ProxyWebView.qml
index adcc472831..2b13760962 100644
--- a/interface/resources/qml/controlsUit/ProxyWebView.qml
+++ b/interface/resources/qml/controlsUit/ProxyWebView.qml
@@ -18,6 +18,7 @@ Rectangle {
 
     property bool safeLoading: false
     property bool loadingLatched: false
+    property bool loading: false
     property var loadingRequest: null
 
 
diff --git a/interface/resources/qml/hifi/avatarapp/Settings.qml b/interface/resources/qml/hifi/avatarapp/Settings.qml
index 39c48646d3..c59fe42608 100644
--- a/interface/resources/qml/hifi/avatarapp/Settings.qml
+++ b/interface/resources/qml/hifi/avatarapp/Settings.qml
@@ -21,7 +21,7 @@ Rectangle {
     HifiControlsUit.Keyboard {
         id: keyboard
         z: 1000
-        raised: parent.keyboardEnabled && parent.keyboardRaised
+        raised: parent.keyboardEnabled && parent.keyboardRaised && HMD.active
         numeric: parent.punctuationMode
         anchors {
             left: parent.left
diff --git a/interface/resources/qml/hifi/tablet/+webengine/BlocksWebView.qml b/interface/resources/qml/hifi/tablet/+webengine/BlocksWebView.qml
new file mode 100644
index 0000000000..050515da37
--- /dev/null
+++ b/interface/resources/qml/hifi/tablet/+webengine/BlocksWebView.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+import QtWebEngine 1.5
+import "../../controls" as Controls
+
+Controls.TabletWebView {
+	profile: WebEngineProfile { httpUserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36"}  
+}
diff --git a/interface/src/AboutUtil.h b/interface/src/AboutUtil.h
index 767e69842d..1495045b85 100644
--- a/interface/src/AboutUtil.h
+++ b/interface/src/AboutUtil.h
@@ -20,7 +20,8 @@
  *
  * @hifi-interface
  * @hifi-client-entity
- * 
+ * @hifi-avatar
+ *
  * @property {string} buildDate
  * @property {string} buildVersion
  * @property {string} qtVersion
diff --git a/interface/src/AvatarBookmarks.h b/interface/src/AvatarBookmarks.h
index f1bc6820eb..4623e7d929 100644
--- a/interface/src/AvatarBookmarks.h
+++ b/interface/src/AvatarBookmarks.h
@@ -21,6 +21,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  *
  */
 
diff --git a/interface/src/LODManager.h b/interface/src/LODManager.h
index 6206fd3539..559bae1779 100644
--- a/interface/src/LODManager.h
+++ b/interface/src/LODManager.h
@@ -36,6 +36,7 @@ class AABox;
   *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  *
  * @property {number} presentTime <em>Read-only.</em>
  * @property {number} engineRunTime <em>Read-only.</em>
diff --git a/interface/src/LocationBookmarks.h b/interface/src/LocationBookmarks.h
index 70ea50e2e7..8cd8e40634 100644
--- a/interface/src/LocationBookmarks.h
+++ b/interface/src/LocationBookmarks.h
@@ -21,6 +21,7 @@
  *
  * @hifi-client-entity
  * @hifi-interface
+ * @hifi-avatar
  */
 
 class LocationBookmarks : public Bookmarks, public  Dependency  {
diff --git a/interface/src/SpeechRecognizer.h b/interface/src/SpeechRecognizer.h
index b22ab73837..7e2acdb8ac 100644
--- a/interface/src/SpeechRecognizer.h
+++ b/interface/src/SpeechRecognizer.h
@@ -27,6 +27,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  */
 class SpeechRecognizer : public QObject, public Dependency {
     Q_OBJECT
diff --git a/interface/src/audio/AudioScope.h b/interface/src/audio/AudioScope.h
index 41cee8d17d..912e337670 100644
--- a/interface/src/audio/AudioScope.h
+++ b/interface/src/audio/AudioScope.h
@@ -31,6 +31,7 @@ class AudioScope : public QObject, public Dependency {
      *
      * @hifi-interface
      * @hifi-client-entity
+     * @hifi-avatar
      *
      * @property {number} scopeInput <em>Read-only.</em>
      * @property {number} scopeOutputLeft <em>Read-only.</em>
diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h
index 50d9e80e8b..51352ec861 100644
--- a/interface/src/avatar/AvatarManager.h
+++ b/interface/src/avatar/AvatarManager.h
@@ -46,6 +46,7 @@ using SortedAvatar = std::pair<float, std::shared_ptr<Avatar>>;
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  *
  * @borrows AvatarList.getAvatarIdentifiers as getAvatarIdentifiers
  * @borrows AvatarList.getAvatarsInRange as getAvatarsInRange
diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h
index c53eae65d4..1a6833b988 100755
--- a/interface/src/avatar/MyAvatar.h
+++ b/interface/src/avatar/MyAvatar.h
@@ -66,6 +66,7 @@ class MyAvatar : public Avatar {
      *
      * @hifi-interface
      * @hifi-client-entity
+     * @hifi-avatar
      *
      * @property {Vec3} qmlPosition - A synonym for <code>position</code> for use by QML.
      * @property {boolean} shouldRenderLocally=true - If <code>true</code> then your avatar is rendered for you in Interface,
diff --git a/interface/src/devices/DdeFaceTracker.h b/interface/src/devices/DdeFaceTracker.h
index 4fe36b582e..282a347dcc 100644
--- a/interface/src/devices/DdeFaceTracker.h
+++ b/interface/src/devices/DdeFaceTracker.h
@@ -32,6 +32,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  */
 
 class DdeFaceTracker : public FaceTracker, public Dependency {
diff --git a/interface/src/raypick/LaserPointerScriptingInterface.h b/interface/src/raypick/LaserPointerScriptingInterface.h
index d85e329e9a..b02213340c 100644
--- a/interface/src/raypick/LaserPointerScriptingInterface.h
+++ b/interface/src/raypick/LaserPointerScriptingInterface.h
@@ -27,6 +27,7 @@ class LaserPointerScriptingInterface : public QObject, public Dependency {
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  */
 public:
 
diff --git a/interface/src/raypick/PickScriptingInterface.h b/interface/src/raypick/PickScriptingInterface.h
index e795068cd3..1c7c8cc6d6 100644
--- a/interface/src/raypick/PickScriptingInterface.h
+++ b/interface/src/raypick/PickScriptingInterface.h
@@ -23,6 +23,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  *
  * @property {number} PICK_ENTITIES A filter flag. Include domain and avatar entities when intersecting. <em>Read-only.</em>.  Deprecated.
  * @property {number} PICK_OVERLAYS A filter flag. Include local entities when intersecting. <em>Read-only.</em>. Deprecated.
diff --git a/interface/src/raypick/PointerScriptingInterface.h b/interface/src/raypick/PointerScriptingInterface.h
index a21c1f2470..49b13d6ce6 100644
--- a/interface/src/raypick/PointerScriptingInterface.h
+++ b/interface/src/raypick/PointerScriptingInterface.h
@@ -22,6 +22,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  */
 
 class PointerScriptingInterface : public QObject, public Dependency {
diff --git a/interface/src/raypick/RayPickScriptingInterface.h b/interface/src/raypick/RayPickScriptingInterface.h
index 3ad0efd439..2311cd21c8 100644
--- a/interface/src/raypick/RayPickScriptingInterface.h
+++ b/interface/src/raypick/RayPickScriptingInterface.h
@@ -25,6 +25,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  *
  * @property {number} PICK_ENTITIES <em>Read-only.</em>
  * @property {number} PICK_OVERLAYS <em>Read-only.</em>
diff --git a/interface/src/scripting/AccountServicesScriptingInterface.h b/interface/src/scripting/AccountServicesScriptingInterface.h
index fb3c329def..55445a864d 100644
--- a/interface/src/scripting/AccountServicesScriptingInterface.h
+++ b/interface/src/scripting/AccountServicesScriptingInterface.h
@@ -42,6 +42,7 @@ class AccountServicesScriptingInterface : public QObject {
      * 
      * @hifi-interface
      * @hifi-client-entity
+     * @hifi-avatar
      *
      * @namespace AccountServices
      * @property {string} username <em>Read-only.</em>
diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h
index 63758d0633..e4dcba9130 100644
--- a/interface/src/scripting/Audio.h
+++ b/interface/src/scripting/Audio.h
@@ -32,6 +32,7 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
      *
      * @hifi-interface
      * @hifi-client-entity
+     * @hifi-avatar
      * @hifi-server-entity
      * @hifi-assignment-client
      *
diff --git a/interface/src/scripting/ClipboardScriptingInterface.h b/interface/src/scripting/ClipboardScriptingInterface.h
index f6a0b29779..42f2205861 100644
--- a/interface/src/scripting/ClipboardScriptingInterface.h
+++ b/interface/src/scripting/ClipboardScriptingInterface.h
@@ -24,6 +24,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  */
 class ClipboardScriptingInterface : public QObject {
     Q_OBJECT
diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h
index b063e98992..f8adbd5c12 100644
--- a/interface/src/scripting/ControllerScriptingInterface.h
+++ b/interface/src/scripting/ControllerScriptingInterface.h
@@ -199,6 +199,7 @@ class ScriptEngine;
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  *
  * @property {Controller.Actions} Actions - Predefined actions on Interface and the user's avatar. These can be used as end
  *     points in a {@link RouteObject} mapping. A synonym for <code>Controller.Hardware.Actions</code>.
diff --git a/interface/src/scripting/DesktopScriptingInterface.h b/interface/src/scripting/DesktopScriptingInterface.h
index db42b5ca54..c8e251eb3e 100644
--- a/interface/src/scripting/DesktopScriptingInterface.h
+++ b/interface/src/scripting/DesktopScriptingInterface.h
@@ -24,7 +24,8 @@
  *
  * @hifi-interface
  * @hifi-client-entity
- * 
+ * @hifi-avatar
+ *
  * @property {number} width
  * @property {number} height
  * @property {number} ALWAYS_ON_TOP - InteractiveWindow flag for always showing a window on top
diff --git a/interface/src/scripting/GooglePolyScriptingInterface.h b/interface/src/scripting/GooglePolyScriptingInterface.h
index fb5aed9759..ad44ad6223 100644
--- a/interface/src/scripting/GooglePolyScriptingInterface.h
+++ b/interface/src/scripting/GooglePolyScriptingInterface.h
@@ -21,6 +21,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  */
 
 class GooglePolyScriptingInterface : public QObject, public Dependency {
diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h
index 81f85409cc..f00e600858 100644
--- a/interface/src/scripting/HMDScriptingInterface.h
+++ b/interface/src/scripting/HMDScriptingInterface.h
@@ -31,6 +31,7 @@ class QScriptEngine;
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  *
  * @property {Vec3} position - The position of the HMD if currently in VR display mode, otherwise
  *     {@link Vec3(0)|Vec3.ZERO}. <em>Read-only.</em>
diff --git a/interface/src/scripting/KeyboardScriptingInterface.h b/interface/src/scripting/KeyboardScriptingInterface.h
index acee10669e..68582d9bd9 100644
--- a/interface/src/scripting/KeyboardScriptingInterface.h
+++ b/interface/src/scripting/KeyboardScriptingInterface.h
@@ -24,6 +24,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  *
  * @property {bool} raised - <code>true</code> If the keyboard is visible <code>false</code> otherwise
  * @property {bool} password - <code>true</code> Will show * instead of characters in the text display <code>false</code> otherwise
diff --git a/interface/src/scripting/MenuScriptingInterface.h b/interface/src/scripting/MenuScriptingInterface.h
index 81cf775de8..02d5c84138 100644
--- a/interface/src/scripting/MenuScriptingInterface.h
+++ b/interface/src/scripting/MenuScriptingInterface.h
@@ -35,6 +35,7 @@ class MenuItemProperties;
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  */
 
 /**
diff --git a/interface/src/scripting/SelectionScriptingInterface.h b/interface/src/scripting/SelectionScriptingInterface.h
index 83aec63ee2..3217efd10a 100644
--- a/interface/src/scripting/SelectionScriptingInterface.h
+++ b/interface/src/scripting/SelectionScriptingInterface.h
@@ -88,6 +88,7 @@ protected:
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  *
  * @example <caption>Outline an entity when it is grabbed by a controller.</caption>
  * // Create a box and copy the following text into the entity's "Script URL" field.
diff --git a/interface/src/scripting/SettingsScriptingInterface.h b/interface/src/scripting/SettingsScriptingInterface.h
index 32d868bb24..e907e550f3 100644
--- a/interface/src/scripting/SettingsScriptingInterface.h
+++ b/interface/src/scripting/SettingsScriptingInterface.h
@@ -21,6 +21,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  */
 
 class SettingsScriptingInterface : public QObject {
diff --git a/interface/src/scripting/WalletScriptingInterface.h b/interface/src/scripting/WalletScriptingInterface.h
index 36ee021b29..005af7f558 100644
--- a/interface/src/scripting/WalletScriptingInterface.h
+++ b/interface/src/scripting/WalletScriptingInterface.h
@@ -34,6 +34,7 @@ public:
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  *
  * @property {number} walletStatus
  * @property {bool} limitedCommerce
diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h
index c5e558eb3a..baff6444e1 100644
--- a/interface/src/scripting/WindowScriptingInterface.h
+++ b/interface/src/scripting/WindowScriptingInterface.h
@@ -31,6 +31,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  *
  * @property {number} innerWidth - The width of the drawable area of the Interface window (i.e., without borders or other
  *     chrome), in pixels. <em>Read-only.</em>
diff --git a/interface/src/ui/AvatarInputs.h b/interface/src/ui/AvatarInputs.h
index e67d35e59f..6569792807 100644
--- a/interface/src/ui/AvatarInputs.h
+++ b/interface/src/ui/AvatarInputs.h
@@ -29,6 +29,7 @@ class AvatarInputs : public QObject {
      *
      * @hifi-interface
      * @hifi-client-entity
+     * @hifi-avatar
      *
      * @property {boolean} cameraEnabled <em>Read-only.</em>
      * @property {boolean} cameraMuted <em>Read-only.</em>
diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h
index 8fc05775bd..77bdfd4ac1 100644
--- a/interface/src/ui/Snapshot.h
+++ b/interface/src/ui/Snapshot.h
@@ -42,6 +42,7 @@ private:
  * 
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  */
 
 class Snapshot : public QObject, public Dependency {
diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h
index ae608cfddb..5488da9115 100644
--- a/interface/src/ui/Stats.h
+++ b/interface/src/ui/Stats.h
@@ -27,6 +27,7 @@ private: \
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  * @hifi-server-entity
  * @hifi-assignment-client
  *
diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h
index 208fc8d78d..14e30b3b22 100644
--- a/interface/src/ui/overlays/Overlays.h
+++ b/interface/src/ui/overlays/Overlays.h
@@ -87,6 +87,7 @@ public:
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  *
  * @property {Uuid} keyboardFocusOverlay - Get or set the {@link Overlays.OverlayType|web3d} overlay that has keyboard focus.
  *     If no overlay has keyboard focus, get returns <code>null</code>; set to <code>null</code> or {@link Uuid|Uuid.NULL} to 
diff --git a/libraries/animation/CMakeLists.txt b/libraries/animation/CMakeLists.txt
index 30addadcaa..1ab54ed342 100644
--- a/libraries/animation/CMakeLists.txt
+++ b/libraries/animation/CMakeLists.txt
@@ -4,5 +4,6 @@ link_hifi_libraries(shared graphics fbx)
 include_hifi_library_headers(networking)
 include_hifi_library_headers(gpu)
 include_hifi_library_headers(hfm)
+include_hifi_library_headers(image)
 
 target_nsight()
diff --git a/libraries/animation/src/AnimationCache.h b/libraries/animation/src/AnimationCache.h
index eea64475df..41742c7485 100644
--- a/libraries/animation/src/AnimationCache.h
+++ b/libraries/animation/src/AnimationCache.h
@@ -50,6 +50,7 @@ Q_DECLARE_METATYPE(AnimationPointer)
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  * @hifi-server-entity
  * @hifi-assignment-client
  *
diff --git a/libraries/animation/src/AnimationCacheScriptingInterface.h b/libraries/animation/src/AnimationCacheScriptingInterface.h
index 1f5735dd0f..06db5ef352 100644
--- a/libraries/animation/src/AnimationCacheScriptingInterface.h
+++ b/libraries/animation/src/AnimationCacheScriptingInterface.h
@@ -30,6 +30,7 @@ class AnimationCacheScriptingInterface : public ScriptableResourceCache, public
      *
      * @hifi-interface
      * @hifi-client-entity
+     * @hifi-avatar
      * @hifi-assignment-client
      *
      * @property {number} numTotal - Total number of total resources. <em>Read-only.</em>
diff --git a/libraries/audio-client/src/AudioIOStats.h b/libraries/audio-client/src/AudioIOStats.h
index 45fcf365da..ffd7163586 100644
--- a/libraries/audio-client/src/AudioIOStats.h
+++ b/libraries/audio-client/src/AudioIOStats.h
@@ -44,6 +44,7 @@ class AudioStreamStatsInterface : public QObject {
      *
      * @hifi-interface
      * @hifi-client-entity
+     * @hifi-avatar
      *
      * @property {number} lossRate <em>Read-only.</em>
      * @property {number} lossCount <em>Read-only.</em>
@@ -192,6 +193,7 @@ class AudioStatsInterface : public QObject {
      *
      * @hifi-interface
      * @hifi-client-entity
+     * @hifi-avatar
      *
      * @property {number} pingMs <em>Read-only.</em>
      * @property {number} inputReadMsMax <em>Read-only.</em>
diff --git a/libraries/audio/src/AudioEffectOptions.h b/libraries/audio/src/AudioEffectOptions.h
index 4bc7957142..e090832510 100644
--- a/libraries/audio/src/AudioEffectOptions.h
+++ b/libraries/audio/src/AudioEffectOptions.h
@@ -25,6 +25,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  * @hifi-server-entity
  * @hifi-assignment-client
  *
diff --git a/libraries/audio/src/Sound.h b/libraries/audio/src/Sound.h
index 836e28d582..f5f47add32 100644
--- a/libraries/audio/src/Sound.h
+++ b/libraries/audio/src/Sound.h
@@ -61,7 +61,8 @@ class Sound : public Resource {
 
 public:
     Sound(const QUrl& url, bool isStereo = false, bool isAmbisonic = false);
-    
+    Sound(const Sound& other) : Resource(other), _audioData(other._audioData), _numChannels(other._numChannels) {}
+
     bool isReady() const { return (bool)_audioData; }
 
     bool isStereo() const { return _audioData ? _audioData->isStereo() : false; }
@@ -132,6 +133,7 @@ typedef QSharedPointer<Sound> SharedSoundPointer;
  * 
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  * @hifi-server-entity
  * @hifi-assignment-client
  *
diff --git a/libraries/audio/src/SoundCacheScriptingInterface.h b/libraries/audio/src/SoundCacheScriptingInterface.h
index c985e8c211..9caa7a8066 100644
--- a/libraries/audio/src/SoundCacheScriptingInterface.h
+++ b/libraries/audio/src/SoundCacheScriptingInterface.h
@@ -30,6 +30,7 @@ class SoundCacheScriptingInterface : public ScriptableResourceCache, public Depe
      *
      * @hifi-interface
      * @hifi-client-entity
+     * @hifi-avatar
      * @hifi-server-entity
      * @hifi-assignment-client
      *
diff --git a/libraries/avatars/src/ClientTraitsHandler.cpp b/libraries/avatars/src/ClientTraitsHandler.cpp
index 3e188afbdf..bcbe5308c7 100644
--- a/libraries/avatars/src/ClientTraitsHandler.cpp
+++ b/libraries/avatars/src/ClientTraitsHandler.cpp
@@ -107,10 +107,11 @@ int ClientTraitsHandler::sendChangedTraitsToMixer() {
 
             if (initialSend || *simpleIt == Updated) {
                 if (traitType == AvatarTraits::SkeletonModelURL) {
+                    bytesWritten += _owningAvatar->packTrait(traitType, *traitsPacketList);
+
                     // keep track of our skeleton version in case we get an override back
                     _currentSkeletonVersion = _currentTraitVersion;
                 }
-                bytesWritten += _owningAvatar->packTrait(traitType, *traitsPacketList);
             }
 
             ++simpleIt;
diff --git a/libraries/baking/src/TextureBaker.cpp b/libraries/baking/src/TextureBaker.cpp
index 2516323c37..6407ce1846 100644
--- a/libraries/baking/src/TextureBaker.cpp
+++ b/libraries/baking/src/TextureBaker.cpp
@@ -154,7 +154,7 @@ void TextureBaker::processTexture() {
             gpu::BackendTarget::GLES32
         }};
         for (auto target : BACKEND_TARGETS) {
-            auto processedTexture = image::processImage(buffer, _textureURL.toString().toStdString(),
+            auto processedTexture = image::processImage(buffer, _textureURL.toString().toStdString(), image::ColorChannel::NONE,
                                                         ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, true,
                                                         target, _abortProcessing);
             if (!processedTexture) {
@@ -197,7 +197,7 @@ void TextureBaker::processTexture() {
     // Uncompressed KTX
     if (_textureType == image::TextureUsage::Type::CUBE_TEXTURE) {
         buffer->reset();
-        auto processedTexture = image::processImage(std::move(buffer), _textureURL.toString().toStdString(),
+        auto processedTexture = image::processImage(std::move(buffer), _textureURL.toString().toStdString(), image::ColorChannel::NONE,
                                                     ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, false, gpu::BackendTarget::GL45, _abortProcessing);
         if (!processedTexture) {
             handleError("Could not process texture " + _textureURL.toString());
diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h
index 5bc7357dd7..845e19f6c3 100644
--- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h
+++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h
@@ -57,6 +57,7 @@ class UserInputMapper;
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  */
 
 /**jsdoc
diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h
index 804709ebfa..eb610af78a 100644
--- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h
+++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h
@@ -39,6 +39,7 @@ class ScriptingInterface;
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  */
 
 // TODO migrate functionality to a RouteBuilder class and make the proxy defer to that 
diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.h b/libraries/display-plugins/src/display-plugins/CompositorHelper.h
index e25d30109f..9c7d01082c 100644
--- a/libraries/display-plugins/src/display-plugins/CompositorHelper.h
+++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.h
@@ -178,6 +178,7 @@ private:
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  *
  * @property {boolean} allowMouseCapture
  * @property {number} depth
diff --git a/libraries/entities/src/DiffTraversal.cpp b/libraries/entities/src/DiffTraversal.cpp
index 36d1f41267..dc32419ed0 100644
--- a/libraries/entities/src/DiffTraversal.cpp
+++ b/libraries/entities/src/DiffTraversal.cpp
@@ -193,7 +193,8 @@ DiffTraversal::DiffTraversal() {
     _path.reserve(MIN_PATH_DEPTH);
 }
 
-DiffTraversal::Type DiffTraversal::prepareNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root) {
+DiffTraversal::Type DiffTraversal::prepareNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root,
+                                                       bool forceFirstPass) {
     assert(root);
     // there are three types of traversal:
     //
@@ -212,7 +213,7 @@ DiffTraversal::Type DiffTraversal::prepareNewTraversal(const DiffTraversal::View
 
     Type type;
     // If usesViewFrustum changes, treat it as a First traversal
-    if (_completedView.startTime == 0 || _currentView.usesViewFrustums() != _completedView.usesViewFrustums()) {
+    if (forceFirstPass || _completedView.startTime == 0 || _currentView.usesViewFrustums() != _completedView.usesViewFrustums()) {
         type = Type::First;
         _currentView.viewFrustums = view.viewFrustums;
         _currentView.lodScaleFactor = view.lodScaleFactor;
diff --git a/libraries/entities/src/DiffTraversal.h b/libraries/entities/src/DiffTraversal.h
index d62c7b8ee1..e1107ec930 100644
--- a/libraries/entities/src/DiffTraversal.h
+++ b/libraries/entities/src/DiffTraversal.h
@@ -61,7 +61,7 @@ public:
 
     DiffTraversal();
 
-    Type prepareNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root);
+    Type prepareNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root, bool forceFirstPass = false);
 
     const View& getCurrentView() const { return _currentView; }
 
diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp
index c5d72d2d7b..7e5be384a7 100644
--- a/libraries/entities/src/EntityItem.cpp
+++ b/libraries/entities/src/EntityItem.cpp
@@ -2640,15 +2640,8 @@ bool EntityItem::matchesJSONFilters(const QJsonObject& jsonFilters) const {
 
     static const QString SERVER_SCRIPTS_PROPERTY = "serverScripts";
 
-    foreach(const auto& property, jsonFilters.keys()) {
-        if (property == SERVER_SCRIPTS_PROPERTY  && jsonFilters[property] == EntityQueryFilterSymbol::NonDefault) {
-            // check if this entity has a non-default value for serverScripts
-            if (_serverScripts != ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS) {
-                return true;
-            } else {
-                return false;
-            }
-        }
+    if (jsonFilters[SERVER_SCRIPTS_PROPERTY] == EntityQueryFilterSymbol::NonDefault) {
+        return _serverScripts != ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS;
     }
 
     // the json filter syntax did not match what we expected, return a match
diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h
index 0e96cb2d25..7950025ff1 100644
--- a/libraries/entities/src/EntityScriptingInterface.h
+++ b/libraries/entities/src/EntityScriptingInterface.h
@@ -109,6 +109,7 @@ public:
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  * @hifi-server-entity
  * @hifi-assignment-client
  *
diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index d6de3d4b25..82a4361723 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -32,6 +32,7 @@
 #include <NetworkAccessManager.h>
 #include <ResourceManager.h>
 #include <PathUtils.h>
+#include <image/ColorChannel.h>
 
 #include "FBXSerializer.h"
 
@@ -1146,8 +1147,10 @@ void GLTFSerializer::setHFMMaterial(HFMMaterial& fbxmat, const GLTFMaterial& mat
         }
         if (material.pbrMetallicRoughness.defined["metallicRoughnessTexture"]) {
             fbxmat.roughnessTexture = getHFMTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]);
+            fbxmat.roughnessTexture.sourceChannel = image::ColorChannel::GREEN;
             fbxmat.useRoughnessMap = true;
             fbxmat.metallicTexture = getHFMTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]);
+            fbxmat.metallicTexture.sourceChannel = image::ColorChannel::BLUE;
             fbxmat.useMetallicMap = true;
         }
         if (material.pbrMetallicRoughness.defined["roughnessFactor"]) {
diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h
index d410671481..26ff86af9c 100755
--- a/libraries/gpu/src/gpu/Texture.h
+++ b/libraries/gpu/src/gpu/Texture.h
@@ -397,8 +397,6 @@ public:
     bool isDefined() const { return _defined; }
 
     Texture(TextureUsageType usageType);
-    Texture(const Texture& buf); // deep copy of the sysmem texture
-    Texture& operator=(const Texture& buf); // deep copy of the sysmem texture
     ~Texture();
 
     Stamp getStamp() const { return _stamp; }
@@ -693,8 +691,10 @@ class TextureSource {
 public:
     TextureSource(const QUrl& url, int type = 0) : _imageUrl(url), _type(type) {}
 
+    void setUrl(const QUrl& url) { _imageUrl = url; }
     const QUrl& getUrl() const { return _imageUrl; }
     const gpu::TexturePointer getGPUTexture() const { return _gpuTexture; }
+    void setType(int type) { _type = type; }
     int getType() const { return _type; }
 
     void resetTexture(gpu::TexturePointer texture);
diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h
index 1ec60c4244..163e317ffa 100644
--- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h
+++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h
@@ -27,6 +27,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  */
 
 class GraphicsScriptingInterface : public QObject, public QScriptable, public Dependency {
diff --git a/libraries/hfm/CMakeLists.txt b/libraries/hfm/CMakeLists.txt
index 553fd935d9..be3d866b70 100644
--- a/libraries/hfm/CMakeLists.txt
+++ b/libraries/hfm/CMakeLists.txt
@@ -5,3 +5,4 @@ link_hifi_libraries(shared)
 
 include_hifi_library_headers(gpu)
 include_hifi_library_headers(graphics)
+include_hifi_library_headers(image)
diff --git a/libraries/hfm/src/hfm/HFM.h b/libraries/hfm/src/hfm/HFM.h
index cccfaa3f7d..9f3de3302c 100644
--- a/libraries/hfm/src/hfm/HFM.h
+++ b/libraries/hfm/src/hfm/HFM.h
@@ -25,6 +25,8 @@
 #include <graphics/Geometry.h>
 #include <graphics/Material.h>
 
+#include <image/ColorChannel.h>
+
 #if defined(Q_OS_ANDROID)
 #define HFM_PACK_NORMALS 0
 #else
@@ -119,10 +121,12 @@ public:
 /// A texture map.
 class Texture {
 public:
+
     QString id;
     QString name;
     QByteArray filename;
     QByteArray content;
+    image::ColorChannel sourceChannel { image::ColorChannel::NONE };
 
     Transform transform;
     int maxNumPixels { MAX_NUM_PIXELS_FOR_FBX_TEXTURE };
diff --git a/libraries/image/src/image/ColorChannel.h b/libraries/image/src/image/ColorChannel.h
new file mode 100644
index 0000000000..e1d107018b
--- /dev/null
+++ b/libraries/image/src/image/ColorChannel.h
@@ -0,0 +1,26 @@
+//
+//  ColorChannel.h
+//  libraries/image/src/image
+//
+//  Created by Sabrina Shanman on 2019/02/12.
+//  Copyright 2019 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
+//
+
+#ifndef hifi_image_ColorChannel_h
+#define hifi_image_ColorChannel_h
+
+namespace image {
+    enum class ColorChannel {
+        NONE,
+        RED,
+        GREEN,
+        BLUE,
+        ALPHA,
+        COUNT
+    };
+};
+
+#endif // hifi_image_ColorChannel_h
diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp
index ac2813667f..a2161caec9 100644
--- a/libraries/image/src/image/Image.cpp
+++ b/libraries/image/src/image/Image.cpp
@@ -16,6 +16,7 @@
 #include <QtCore/QtGlobal>
 #include <QUrl>
 #include <QImage>
+#include <QRgb>
 #include <QBuffer>
 #include <QImageReader>
 
@@ -221,7 +222,45 @@ QImage processRawImageData(QIODevice& content, const std::string& filename) {
     return QImage();
 }
 
-gpu::TexturePointer processImage(std::shared_ptr<QIODevice> content, const std::string& filename,
+void mapToRedChannel(QImage& image, ColorChannel sourceChannel) {
+    // Change format of image so we know exactly how to process it
+    if (image.format() != QImage::Format_ARGB32) {
+        image = image.convertToFormat(QImage::Format_ARGB32);
+    }
+
+    for (int i = 0; i < image.height(); i++) {
+        QRgb* pixel = reinterpret_cast<QRgb*>(image.scanLine(i));
+        // Past end pointer
+        QRgb* lineEnd = pixel + image.width();
+
+        // Transfer channel data from source to target
+        for (; pixel < lineEnd; pixel++) {
+            int colorValue;
+            switch (sourceChannel) {
+            case ColorChannel::RED:
+                colorValue = qRed(*pixel);
+                break;
+            case ColorChannel::GREEN:
+                colorValue = qGreen(*pixel);
+                break;
+            case ColorChannel::BLUE:
+                colorValue = qBlue(*pixel);
+                break;
+            case ColorChannel::ALPHA:
+                colorValue = qAlpha(*pixel);
+                break;
+            default:
+                colorValue = qRed(*pixel);
+                break;
+            }
+
+            // Dump the color in the red channel, ignore the rest
+            *pixel = qRgba(colorValue, 0, 0, 255);
+        }
+    }
+}
+
+gpu::TexturePointer processImage(std::shared_ptr<QIODevice> content, const std::string& filename, ColorChannel sourceChannel,
                                  int maxNumPixels, TextureUsage::Type textureType,
                                  bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
 
@@ -252,6 +291,11 @@ gpu::TexturePointer processImage(std::shared_ptr<QIODevice> content, const std::
             QSize(originalWidth, originalHeight) << " to " <<
             QSize(imageWidth, imageHeight) << ")";
     }
+
+    // Re-map to image with single red channel texture if requested
+    if (sourceChannel != ColorChannel::NONE) {
+        mapToRedChannel(image, sourceChannel);
+    }
     
     auto loader = TextureUsage::getTextureLoaderForType(textureType);
     auto texture = loader(std::move(image), filename, compress, target, abortProcessing);
diff --git a/libraries/image/src/image/Image.h b/libraries/image/src/image/Image.h
index ae72a183b3..40c31eeeff 100644
--- a/libraries/image/src/image/Image.h
+++ b/libraries/image/src/image/Image.h
@@ -16,6 +16,8 @@
 
 #include <gpu/Texture.h>
 
+#include "ColorChannel.h"
+
 class QByteArray;
 class QImage;
 
@@ -81,7 +83,7 @@ gpu::TexturePointer processCubeTextureColorFromImage(QImage&& srcImage, const st
 
 const QStringList getSupportedFormats();
 
-gpu::TexturePointer processImage(std::shared_ptr<QIODevice> content, const std::string& url,
+gpu::TexturePointer processImage(std::shared_ptr<QIODevice> content, const std::string& url, ColorChannel sourceChannel,
                                  int maxNumPixels, TextureUsage::Type textureType,
                                  bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing = false);
 
diff --git a/libraries/midi/src/Midi.h b/libraries/midi/src/Midi.h
index e5c44c6b7e..081a44f7b6 100644
--- a/libraries/midi/src/Midi.h
+++ b/libraries/midi/src/Midi.h
@@ -25,6 +25,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  */
 
 class Midi : public QObject, public Dependency {
diff --git a/libraries/model-baker/CMakeLists.txt b/libraries/model-baker/CMakeLists.txt
index 6fa7c1815a..aabd6eba3a 100644
--- a/libraries/model-baker/CMakeLists.txt
+++ b/libraries/model-baker/CMakeLists.txt
@@ -2,3 +2,5 @@ set(TARGET_NAME model-baker)
 setup_hifi_library()
 
 link_hifi_libraries(shared task gpu graphics hfm)
+
+include_hifi_library_headers(image)
diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp
index a4eba0c7a9..90925d17c3 100644
--- a/libraries/model-networking/src/model-networking/ModelCache.cpp
+++ b/libraries/model-networking/src/model-networking/ModelCache.cpp
@@ -322,7 +322,7 @@ private:
 void GeometryDefinitionResource::setExtra(void* extra) {
     const GeometryExtra* geometryExtra = static_cast<const GeometryExtra*>(extra);
     _mapping = geometryExtra ? geometryExtra->mapping : QVariantHash();
-    _textureBaseUrl = resolveTextureBaseUrl(_url, geometryExtra ? geometryExtra->textureBaseUrl : QUrl());
+    _textureBaseUrl = geometryExtra ? resolveTextureBaseUrl(_url, geometryExtra->textureBaseUrl) : QUrl();
     _combineParts = geometryExtra ? geometryExtra->combineParts : true;
 }
 
@@ -599,7 +599,7 @@ graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl
     }
 
     const auto url = getTextureUrl(baseUrl, hfmTexture);
-    const auto texture = DependencyManager::get<TextureCache>()->getTexture(url, type, hfmTexture.content, hfmTexture.maxNumPixels);
+    const auto texture = DependencyManager::get<TextureCache>()->getTexture(url, type, hfmTexture.content, hfmTexture.maxNumPixels, hfmTexture.sourceChannel);
     _textures[channel] = Texture { hfmTexture.name, texture };
 
     auto map = std::make_shared<graphics::TextureMap>();
diff --git a/libraries/model-networking/src/model-networking/ModelCacheScriptingInterface.h b/libraries/model-networking/src/model-networking/ModelCacheScriptingInterface.h
index 5ac7ac1e50..16532fafc3 100644
--- a/libraries/model-networking/src/model-networking/ModelCacheScriptingInterface.h
+++ b/libraries/model-networking/src/model-networking/ModelCacheScriptingInterface.h
@@ -30,6 +30,7 @@ class ModelCacheScriptingInterface : public ScriptableResourceCache, public Depe
      *
      * @hifi-interface
      * @hifi-client-entity
+     * @hifi-avatar
      *
      * @property {number} numTotal - Total number of total resources. <em>Read-only.</em>
      * @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp
index d4cf7e6ce9..a268f4ad0a 100644
--- a/libraries/model-networking/src/model-networking/TextureCache.cpp
+++ b/libraries/model-networking/src/model-networking/TextureCache.cpp
@@ -192,6 +192,7 @@ public:
     image::TextureUsage::Type type;
     const QByteArray& content;
     int maxNumPixels;
+    image::ColorChannel sourceChannel;
 };
 
 namespace std {
@@ -206,19 +207,19 @@ namespace std {
     struct hash<TextureExtra> {
         size_t operator()(const TextureExtra& a) const {
             size_t result = 0;
-            hash_combine(result, (int)a.type, a.content, a.maxNumPixels);
+            hash_combine(result, (int)a.type, a.content, a.maxNumPixels, (int)a.sourceChannel);
             return result;
         }
     };
 }
 
-ScriptableResource* TextureCache::prefetch(const QUrl& url, int type, int maxNumPixels) {
+ScriptableResource* TextureCache::prefetch(const QUrl& url, int type, int maxNumPixels, image::ColorChannel sourceChannel) {
     auto byteArray = QByteArray();
-    TextureExtra extra = { (image::TextureUsage::Type)type, byteArray, maxNumPixels };
+    TextureExtra extra = { (image::TextureUsage::Type)type, byteArray, maxNumPixels, sourceChannel };
     return ResourceCache::prefetch(url, &extra, std::hash<TextureExtra>()(extra));
 }
 
-NetworkTexturePointer TextureCache::getTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels) {
+NetworkTexturePointer TextureCache::getTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels, image::ColorChannel sourceChannel) {
     if (url.scheme() == RESOURCE_SCHEME) {
         return getResourceTexture(url);
     }
@@ -228,7 +229,7 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, image::TextureUs
         query.addQueryItem("skybox", "");
         modifiedUrl.setQuery(query.toString());
     }
-    TextureExtra extra = { type, content, maxNumPixels };
+    TextureExtra extra = { type, content, maxNumPixels, sourceChannel };
     return ResourceCache::getResource(modifiedUrl, QUrl(), &extra, std::hash<TextureExtra>()(extra)).staticCast<NetworkTexture>();
 }
 
@@ -335,6 +336,7 @@ int networkTexturePointerMetaTypeId = qRegisterMetaType<QWeakPointer<NetworkText
 
 NetworkTexture::NetworkTexture(const QUrl& url, bool resourceTexture) :
     Resource(url),
+    Texture(),
     _maxNumPixels(100)
 {
     if (resourceTexture) {
@@ -345,7 +347,9 @@ NetworkTexture::NetworkTexture(const QUrl& url, bool resourceTexture) :
 
 NetworkTexture::NetworkTexture(const NetworkTexture& other) :
     Resource(other),
+    Texture(other),
     _type(other._type),
+    _sourceChannel(other._sourceChannel),
     _currentlyLoadingResourceType(other._currentlyLoadingResourceType),
     _originalWidth(other._originalWidth),
     _originalHeight(other._originalHeight),
@@ -353,6 +357,11 @@ NetworkTexture::NetworkTexture(const NetworkTexture& other) :
     _height(other._height),
     _maxNumPixels(other._maxNumPixels)
 {
+    if (_width == 0 || _height == 0 ||
+        other._currentlyLoadingResourceType == ResourceType::META ||
+        (other._currentlyLoadingResourceType == ResourceType::KTX && other._ktxResourceState != KTXResourceState::WAITING_FOR_MIP_REQUEST)) {
+        _startedLoading = false;
+    }
 }
 
 static bool isLocalUrl(const QUrl& url) {
@@ -364,8 +373,14 @@ void NetworkTexture::setExtra(void* extra) {
     const TextureExtra* textureExtra = static_cast<const TextureExtra*>(extra);
     _type = textureExtra ? textureExtra->type : image::TextureUsage::DEFAULT_TEXTURE;
     _maxNumPixels = textureExtra ? textureExtra->maxNumPixels : ABSOLUTE_MAX_TEXTURE_NUM_PIXELS;
+    _sourceChannel = textureExtra ? textureExtra->sourceChannel : image::ColorChannel::NONE;
 
-    _textureSource = std::make_shared<gpu::TextureSource>(_url, (int)_type);
+    if (_textureSource) {
+        _textureSource->setUrl(_url);
+        _textureSource->setType((int)_type);
+    } else {
+        _textureSource = std::make_shared<gpu::TextureSource>(_url, (int)_type);
+    }
     _lowestRequestedMipLevel = 0;
 
     auto fileNameLowercase = _url.fileName().toLower();
@@ -425,7 +440,8 @@ gpu::TexturePointer NetworkTexture::getFallbackTexture() const {
 class ImageReader : public QRunnable {
 public:
     ImageReader(const QWeakPointer<Resource>& resource, const QUrl& url,
-                const QByteArray& data, size_t extraHash, int maxNumPixels);
+                const QByteArray& data, size_t extraHash, int maxNumPixels,
+                image::ColorChannel sourceChannel);
     void run() override final;
     void read();
 
@@ -437,6 +453,7 @@ private:
     QByteArray _content;
     size_t _extraHash;
     int _maxNumPixels;
+    image::ColorChannel _sourceChannel;
 };
 
 NetworkTexture::~NetworkTexture() {
@@ -523,7 +540,6 @@ void NetworkTexture::makeRequest() {
     } else {
         qWarning(networking) << "NetworkTexture::makeRequest() called while not in a valid state: " << _ktxResourceState;
     }
-
 }
 
 void NetworkTexture::handleLocalRequestCompleted() {
@@ -1069,7 +1085,7 @@ void NetworkTexture::loadTextureContent(const QByteArray& content) {
         return;
     }
 
-    QThreadPool::globalInstance()->start(new ImageReader(_self, _url, content, _extraHash, _maxNumPixels));
+    QThreadPool::globalInstance()->start(new ImageReader(_self, _url, content, _extraHash, _maxNumPixels, _sourceChannel));
 }
 
 void NetworkTexture::refresh() {
@@ -1094,12 +1110,13 @@ void NetworkTexture::refresh() {
     Resource::refresh();
 }
 
-ImageReader::ImageReader(const QWeakPointer<Resource>& resource, const QUrl& url, const QByteArray& data, size_t extraHash, int maxNumPixels) :
+ImageReader::ImageReader(const QWeakPointer<Resource>& resource, const QUrl& url, const QByteArray& data, size_t extraHash, int maxNumPixels, image::ColorChannel sourceChannel) :
     _resource(resource),
     _url(url),
     _content(data),
     _extraHash(extraHash),
-    _maxNumPixels(maxNumPixels)
+    _maxNumPixels(maxNumPixels),
+    _sourceChannel(sourceChannel)
 {
     DependencyManager::get<StatTracker>()->incrementStat("PendingProcessing");
     listSupportedImageFormats();
@@ -1207,7 +1224,7 @@ void ImageReader::read() {
         constexpr bool shouldCompress = false;
 #endif
         auto target = getBackendTarget();
-        texture = image::processImage(std::move(buffer), _url.toString().toStdString(), _maxNumPixels, networkTexture->getTextureType(), shouldCompress, target);
+        texture = image::processImage(std::move(buffer), _url.toString().toStdString(), _sourceChannel, _maxNumPixels, networkTexture->getTextureType(), shouldCompress, target);
 
         if (!texture) {
             QMetaObject::invokeMethod(resource.data(), "setImage",
diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h
index cdedc64ea5..acca916acc 100644
--- a/libraries/model-networking/src/model-networking/TextureCache.h
+++ b/libraries/model-networking/src/model-networking/TextureCache.h
@@ -22,6 +22,7 @@
 #include <DependencyManager.h>
 #include <ResourceCache.h>
 #include <graphics/TextureMap.h>
+#include <image/ColorChannel.h>
 #include <image/Image.h>
 #include <ktx/KTX.h>
 #include <TextureMeta.h>
@@ -96,6 +97,7 @@ private:
     friend class ImageReader;
 
     image::TextureUsage::Type _type;
+    image::ColorChannel _sourceChannel;
 
     enum class ResourceType {
         META,
@@ -178,7 +180,8 @@ public:
 
     /// Loads a texture from the specified URL.
     NetworkTexturePointer getTexture(const QUrl& url, image::TextureUsage::Type type = image::TextureUsage::DEFAULT_TEXTURE,
-        const QByteArray& content = QByteArray(), int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS);
+        const QByteArray& content = QByteArray(), int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS,
+        image::ColorChannel sourceChannel = image::ColorChannel::NONE);
 
     gpu::TexturePointer getTextureByHash(const std::string& hash);
     gpu::TexturePointer cacheTextureByHash(const std::string& hash, const gpu::TexturePointer& texture);
@@ -201,7 +204,7 @@ signals:
 protected:
     
     // Overload ResourceCache::prefetch to allow specifying texture type for loads
-    Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, int type, int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS);
+    Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, int type, int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, image::ColorChannel sourceChannel = image::ColorChannel::NONE);
 
     virtual QSharedPointer<Resource> createResource(const QUrl& url) override;
     QSharedPointer<Resource> createResourceCopy(const QSharedPointer<Resource>& resource) override;
diff --git a/libraries/model-networking/src/model-networking/TextureCacheScriptingInterface.h b/libraries/model-networking/src/model-networking/TextureCacheScriptingInterface.h
index 4120840759..1cc4f4f948 100644
--- a/libraries/model-networking/src/model-networking/TextureCacheScriptingInterface.h
+++ b/libraries/model-networking/src/model-networking/TextureCacheScriptingInterface.h
@@ -30,6 +30,7 @@ class TextureCacheScriptingInterface : public ScriptableResourceCache, public De
      *
      * @hifi-interface
      * @hifi-client-entity
+     * @hifi-avatar
      *
      * @property {number} numTotal - Total number of total resources. <em>Read-only.</em>
      * @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h
index 450b71023c..3b95923634 100644
--- a/libraries/networking/src/AddressManager.h
+++ b/libraries/networking/src/AddressManager.h
@@ -43,6 +43,7 @@ const QString GET_PLACE = "/api/v1/places/%1";
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  * @hifi-assignment-client
  *
  * @property {Uuid} domainID - A UUID uniquely identifying the domain you're visiting. Is {@link Uuid|Uuid.NULL} if you're not
diff --git a/libraries/networking/src/MessagesClient.h b/libraries/networking/src/MessagesClient.h
index f2ccfe33f4..255487f0bb 100644
--- a/libraries/networking/src/MessagesClient.h
+++ b/libraries/networking/src/MessagesClient.h
@@ -40,6 +40,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  * @hifi-server-entity
  * @hifi-assignment-client
  */
diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp
index cb7b8c7c82..7345081380 100644
--- a/libraries/networking/src/ResourceCache.cpp
+++ b/libraries/networking/src/ResourceCache.cpp
@@ -362,7 +362,6 @@ QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl&
             resource->moveToThread(qApp->thread());
             connect(resource.data(), &Resource::updateSize, this, &ResourceCache::updateTotalSize);
             resourcesWithExtraHash.insert(extraHash, resource);
-            removeUnusedResource(resource);
             resource->ensureLoading();
         }
     }
@@ -404,7 +403,7 @@ void ResourceCache::addUnusedResource(const QSharedPointer<Resource>& resource)
     // If it doesn't fit or its size is unknown, remove it from the cache.
     if (resource->getBytes() == 0 || resource->getBytes() > _unusedResourcesMaxSize) {
         resource->setCache(nullptr);
-        removeResource(resource->getURL(), resource->getBytes());
+        removeResource(resource->getURL(), resource->getExtraHash(), resource->getBytes());
         resetTotalResourceCounter();
         return;
     }
@@ -443,7 +442,7 @@ void ResourceCache::reserveUnusedResource(qint64 resourceSize) {
         auto size = it.value()->getBytes();
 
         locker.unlock();
-        removeResource(it.value()->getURL(), size);
+        removeResource(it.value()->getURL(), it.value()->getExtraHash(), size);
         locker.relock();
 
         _unusedResourcesSize -= size;
@@ -489,9 +488,13 @@ void ResourceCache::resetResourceCounters() {
     emit dirty();
 }
 
-void ResourceCache::removeResource(const QUrl& url, qint64 size) {
+void ResourceCache::removeResource(const QUrl& url, size_t extraHash, qint64 size) {
     QWriteLocker locker(&_resourcesLock);
-    _resources.remove(url);
+    auto& resources = _resources[url];
+    resources.remove(extraHash);
+    if (resources.size() == 0) {
+        _resources.remove(url);
+    }
     _totalResourcesSize -= size;
 }
 
@@ -664,7 +667,7 @@ void Resource::allReferencesCleared() {
     } else {
         if (_cache) {
             // remove from the cache
-            _cache->removeResource(getURL(), getBytes());
+            _cache->removeResource(getURL(), getExtraHash(), getBytes());
             _cache->resetTotalResourceCounter();
         }
 
diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h
index 740bdadc48..f3967d8ffc 100644
--- a/libraries/networking/src/ResourceCache.h
+++ b/libraries/networking/src/ResourceCache.h
@@ -95,6 +95,7 @@ class ScriptableResource : public QObject {
      *
      * @hifi-interface
      * @hifi-client-entity
+     * @hifi-avatar
      * @hifi-server-entity
      * @hifi-assignment-client
      *
@@ -271,7 +272,7 @@ private:
     friend class ScriptableResourceCache;
 
     void reserveUnusedResource(qint64 resourceSize);
-    void removeResource(const QUrl& url, qint64 size = 0);
+    void removeResource(const QUrl& url, size_t extraHash, qint64 size = 0);
 
     void resetTotalResourceCounter();
     void resetUnusedResourceCounter();
@@ -418,6 +419,7 @@ public:
 
     virtual void setExtra(void* extra) {};
     void setExtraHash(size_t extraHash) { _extraHash = extraHash; }
+    size_t getExtraHash() const { return _extraHash; }
 
 signals:
     /// Fired when the resource begins downloading.
diff --git a/libraries/networking/src/ResourceScriptingInterface.h b/libraries/networking/src/ResourceScriptingInterface.h
index cc3f12f990..5f81537e99 100644
--- a/libraries/networking/src/ResourceScriptingInterface.h
+++ b/libraries/networking/src/ResourceScriptingInterface.h
@@ -22,6 +22,7 @@
  * 
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  * @hifi-server-entity
  * @hifi-assignment-client
  */
diff --git a/libraries/plugins/src/plugins/SteamClientPlugin.h b/libraries/plugins/src/plugins/SteamClientPlugin.h
index fc1b85c572..2124d16b5e 100644
--- a/libraries/plugins/src/plugins/SteamClientPlugin.h
+++ b/libraries/plugins/src/plugins/SteamClientPlugin.h
@@ -45,6 +45,7 @@ public:
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  *
  * @property {boolean} running - <em>Read-only.</em>
  */
diff --git a/libraries/pointers/src/Pick.h b/libraries/pointers/src/Pick.h
index 857a72caa8..1a9d3fd32c 100644
--- a/libraries/pointers/src/Pick.h
+++ b/libraries/pointers/src/Pick.h
@@ -68,6 +68,7 @@ public:
      *
      * @hifi-interface
      * @hifi-client-entity
+     * @hifi-avatar
      *
      * @property {number} Ray Ray picks intersect a ray with the nearest object in front of them, along a given direction.
      * @property {number} Stylus Stylus picks provide "tapping" functionality on/into flat surfaces.
diff --git a/libraries/procedural/CMakeLists.txt b/libraries/procedural/CMakeLists.txt
index 6d6610a323..f3c3be687a 100644
--- a/libraries/procedural/CMakeLists.txt
+++ b/libraries/procedural/CMakeLists.txt
@@ -1,4 +1,3 @@
 set(TARGET_NAME procedural)
 setup_hifi_library()
 link_hifi_libraries(shared gpu shaders networking graphics model-networking ktx image)
-
diff --git a/libraries/script-engine/src/AssetScriptingInterface.h b/libraries/script-engine/src/AssetScriptingInterface.h
index 0e05a563b2..07d681ca88 100644
--- a/libraries/script-engine/src/AssetScriptingInterface.h
+++ b/libraries/script-engine/src/AssetScriptingInterface.h
@@ -30,6 +30,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  * @hifi-server-entity
  * @hifi-assignment-client
  */
diff --git a/libraries/script-engine/src/FileScriptingInterface.h b/libraries/script-engine/src/FileScriptingInterface.h
index 7b833399e0..859f343ec5 100644
--- a/libraries/script-engine/src/FileScriptingInterface.h
+++ b/libraries/script-engine/src/FileScriptingInterface.h
@@ -21,6 +21,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  * @hifi-server-entity
  * @hifi-assignment-client
  */
diff --git a/libraries/script-engine/src/Mat4.h b/libraries/script-engine/src/Mat4.h
index 288a101234..7ad77b9b24 100644
--- a/libraries/script-engine/src/Mat4.h
+++ b/libraries/script-engine/src/Mat4.h
@@ -26,6 +26,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  * @hifi-server-entity
  * @hifi-assignment-client
  */
diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h
index 76b7ac45e3..d24db786d0 100644
--- a/libraries/script-engine/src/Quat.h
+++ b/libraries/script-engine/src/Quat.h
@@ -40,6 +40,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  * @hifi-server-entity
  * @hifi-assignment-client
  *
diff --git a/libraries/script-engine/src/RecordingScriptingInterface.h b/libraries/script-engine/src/RecordingScriptingInterface.h
index c4d576351f..6a058feea5 100644
--- a/libraries/script-engine/src/RecordingScriptingInterface.h
+++ b/libraries/script-engine/src/RecordingScriptingInterface.h
@@ -28,6 +28,7 @@ class QScriptValue;
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  * @hifi-assignment-client
  */
 class RecordingScriptingInterface : public QObject, public Dependency {
diff --git a/libraries/script-engine/src/SceneScriptingInterface.h b/libraries/script-engine/src/SceneScriptingInterface.h
index da42cf2df3..a78fa44641 100644
--- a/libraries/script-engine/src/SceneScriptingInterface.h
+++ b/libraries/script-engine/src/SceneScriptingInterface.h
@@ -115,6 +115,7 @@ namespace SceneScripting {
      *
      * @hifi-interface
      * @hifi-client-entity
+     * @hifi-avatar
      *
      * @property {string} backgroundMode
      * @property {Scene.Stage.KeyLight} keyLight
@@ -178,6 +179,7 @@ namespace SceneScripting {
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  *
  * @property {boolean} shouldRenderAvatars
  * @property {boolean} shouldRenderEntities
diff --git a/libraries/script-engine/src/ScriptAudioInjector.h b/libraries/script-engine/src/ScriptAudioInjector.h
index 2c88d618e1..c7fb2f8a9a 100644
--- a/libraries/script-engine/src/ScriptAudioInjector.h
+++ b/libraries/script-engine/src/ScriptAudioInjector.h
@@ -23,6 +23,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  * @hifi-server-entity
  * @hifi-assignment-client
  *
diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp
index fa7a8e1114..5d33a6a061 100644
--- a/libraries/script-engine/src/ScriptEngine.cpp
+++ b/libraries/script-engine/src/ScriptEngine.cpp
@@ -588,6 +588,7 @@ static void scriptableResourceFromScriptValue(const QScriptValue& value, Scripta
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  * @hifi-server-entity
  * @hifi-assignment-client
  *
diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h
index 8753010089..48fb4f0b83 100644
--- a/libraries/script-engine/src/ScriptEngine.h
+++ b/libraries/script-engine/src/ScriptEngine.h
@@ -104,6 +104,7 @@ public:
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  * @hifi-server-entity
  * @hifi-assignment-client
  *
diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h
index 4d5964e462..4db150fce5 100644
--- a/libraries/script-engine/src/ScriptEngines.h
+++ b/libraries/script-engine/src/ScriptEngines.h
@@ -32,6 +32,7 @@ class ScriptEngine;
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  *
  * @property {string} debugScriptUrl
  * @property {string} defaultScriptsPath
diff --git a/libraries/script-engine/src/ScriptUUID.h b/libraries/script-engine/src/ScriptUUID.h
index 45e6ec0ad1..7387cefeff 100644
--- a/libraries/script-engine/src/ScriptUUID.h
+++ b/libraries/script-engine/src/ScriptUUID.h
@@ -27,6 +27,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  * @hifi-server-entity
  * @hifi-assignment-client
  *
diff --git a/libraries/script-engine/src/ScriptsModel.h b/libraries/script-engine/src/ScriptsModel.h
index 2466347baa..adfb15affc 100644
--- a/libraries/script-engine/src/ScriptsModel.h
+++ b/libraries/script-engine/src/ScriptsModel.h
@@ -71,6 +71,7 @@ public:
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  */
 class ScriptsModel : public QAbstractItemModel {
     Q_OBJECT
diff --git a/libraries/script-engine/src/ScriptsModelFilter.h b/libraries/script-engine/src/ScriptsModelFilter.h
index 05a76334bb..81ae4c1226 100644
--- a/libraries/script-engine/src/ScriptsModelFilter.h
+++ b/libraries/script-engine/src/ScriptsModelFilter.h
@@ -23,6 +23,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  */
 class ScriptsModelFilter : public QSortFilterProxyModel {
     Q_OBJECT
diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h
index e80d38239f..57de205066 100644
--- a/libraries/script-engine/src/UsersScriptingInterface.h
+++ b/libraries/script-engine/src/UsersScriptingInterface.h
@@ -21,6 +21,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  * @hifi-assignment-client
  *
  * @property {boolean} canKick - <code>true</code> if the domain server allows the node or avatar to kick (ban) avatars,
diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h
index fe903c07e2..7887938004 100644
--- a/libraries/script-engine/src/Vec3.h
+++ b/libraries/script-engine/src/Vec3.h
@@ -31,6 +31,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  * @hifi-server-entity
  * @hifi-assignment-client
  *
diff --git a/libraries/shared/src/DebugDraw.h b/libraries/shared/src/DebugDraw.h
index 81acbf554c..785e549c03 100644
--- a/libraries/shared/src/DebugDraw.h
+++ b/libraries/shared/src/DebugDraw.h
@@ -28,6 +28,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  * @hifi-server-entity
  * @hifi-assignment-client
  */
diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h
index 2247f4cc6a..0096cb6c90 100644
--- a/libraries/shared/src/PathUtils.h
+++ b/libraries/shared/src/PathUtils.h
@@ -25,6 +25,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  *
  * @deprecated The Paths API is deprecated. Use {@link Script.resolvePath} and {@link Script.resourcesPath} instead.
  * @readonly
diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h
index 9d5bca6b9f..729816cf82 100644
--- a/libraries/shared/src/RegisteredMetaTypes.h
+++ b/libraries/shared/src/RegisteredMetaTypes.h
@@ -645,6 +645,7 @@ using MeshPointer = std::shared_ptr<graphics::Mesh>;
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  * @hifi-server-entity
  * @hifi-assignment-client
  *
diff --git a/libraries/shared/src/shared/Camera.h b/libraries/shared/src/shared/Camera.h
index d14489b92c..31e6228bb9 100644
--- a/libraries/shared/src/shared/Camera.h
+++ b/libraries/shared/src/shared/Camera.h
@@ -43,6 +43,7 @@ class Camera : public QObject {
      *
      * @hifi-interface
      * @hifi-client-entity
+     * @hifi-avatar
      *
      * @property {Vec3} position - The position of the camera. You can set this value only when the camera is in independent 
      *     mode.
diff --git a/libraries/task/src/task/Config.h b/libraries/task/src/task/Config.h
index da9b95a274..486b28b6b9 100644
--- a/libraries/task/src/task/Config.h
+++ b/libraries/task/src/task/Config.h
@@ -196,6 +196,7 @@ public:
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  *
  * @property {number} cpuRunTime - <em>Read-only.</em>
  * @property {boolean} enabled
diff --git a/libraries/ui/src/InteractiveWindow.h b/libraries/ui/src/InteractiveWindow.h
index a25d559557..22a3df7530 100644
--- a/libraries/ui/src/InteractiveWindow.h
+++ b/libraries/ui/src/InteractiveWindow.h
@@ -44,7 +44,8 @@ using namespace InteractiveWindowEnums;
  * @class InteractiveWindow
  *
  * @hifi-interface
- * @hifi-client-en
+ * @hifi-client-entity
+ * @hifi-avatar
  *
  * @property {string} title
  * @property {Vec2} position
diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp
index a719593df2..137cffde94 100644
--- a/libraries/ui/src/OffscreenUi.cpp
+++ b/libraries/ui/src/OffscreenUi.cpp
@@ -36,6 +36,7 @@
  * 
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  * @property {boolean} navigationFocused
  * @property {boolean} navigationFocusDisabled
  */
diff --git a/libraries/ui/src/QmlWebWindowClass.h b/libraries/ui/src/QmlWebWindowClass.h
index e3aea22e3d..770f8ec965 100644
--- a/libraries/ui/src/QmlWebWindowClass.h
+++ b/libraries/ui/src/QmlWebWindowClass.h
@@ -17,6 +17,7 @@
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  *
  * @property {string} url - <em>Read-only.</em>
  * @property {Vec2} position
diff --git a/libraries/ui/src/QmlWindowClass.h b/libraries/ui/src/QmlWindowClass.h
index 18ee1fedd5..8aad9a8b36 100644
--- a/libraries/ui/src/QmlWindowClass.h
+++ b/libraries/ui/src/QmlWindowClass.h
@@ -24,7 +24,8 @@ class QScriptContext;
  * @param {OverlayWindow.Properties} [properties=null]
  *
  * @hifi-interface
- * @hifi-client-en
+ * @hifi-client-entity
+ * @hifi-avatar
  *
  * @property {Vec2} position
  * @property {Vec2} size
diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h
index 9821ad1263..1a6df83d93 100644
--- a/libraries/ui/src/ui/TabletScriptingInterface.h
+++ b/libraries/ui/src/ui/TabletScriptingInterface.h
@@ -43,12 +43,14 @@ class OffscreenQmlSurface;
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  */
 /**jsdoc
  * @namespace tabletInterface
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  *
  * @deprecated This API is deprecated and will be removed. Use {@link Tablet} instead.
  */
@@ -208,6 +210,7 @@ Q_DECLARE_METATYPE(TabletButtonsProxyModel*);
   *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  *
  * @property {string} name - Name of this tablet. <em>Read-only.</em>
  * @property {boolean} toolbarMode - Used to transition this tablet into and out of toolbar mode.
@@ -456,6 +459,7 @@ Q_DECLARE_METATYPE(TabletProxy*);
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  *
  * @property {Uuid} uuid - Uniquely identifies this button. <em>Read-only.</em>
  * @property {TabletButtonProxy.ButtonProperties} properties
diff --git a/libraries/ui/src/ui/ToolbarScriptingInterface.h b/libraries/ui/src/ui/ToolbarScriptingInterface.h
index 777eeba9dd..409ea28fdc 100644
--- a/libraries/ui/src/ui/ToolbarScriptingInterface.h
+++ b/libraries/ui/src/ui/ToolbarScriptingInterface.h
@@ -24,6 +24,7 @@ class QQuickItem;
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  */
 class ToolbarButtonProxy : public QmlWrapper {
     Q_OBJECT
@@ -83,6 +84,7 @@ Q_DECLARE_METATYPE(ToolbarButtonProxy*);
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  */
 class ToolbarProxy : public QmlWrapper {
     Q_OBJECT
@@ -136,6 +138,7 @@ Q_DECLARE_METATYPE(ToolbarProxy*);
  *
  * @hifi-interface
  * @hifi-client-entity
+ * @hifi-avatar
  */
 class ToolbarScriptingInterface : public QObject, public Dependency {
     Q_OBJECT
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index 4d7a594d92..b9ae635a4f 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -19,8 +19,8 @@ function(check_test name)
 endfunction()
 
 if (BUILD_TOOLS)
-    # Allow different tools for production builds
-    if (RELEASE_TYPE STREQUAL "PRODUCTION")
+    # Allow different tools for stable builds
+    if (STABLE_BUILD)
         set(ALL_TOOLS 
             udt-test 
             vhacd-util
diff --git a/tools/jsdoc/plugins/hifi.js b/tools/jsdoc/plugins/hifi.js
index 76f33e2c73..a525093965 100644
--- a/tools/jsdoc/plugins/hifi.js
+++ b/tools/jsdoc/plugins/hifi.js
@@ -107,6 +107,9 @@ exports.handlers = {
             if (e.doclet.hifiClientEntity) {
                 rows.push("Client Entity Scripts");
             }
+            if (e.doclet.hifiAvatar) {
+                rows.push("Avatar Scripts");
+            }
             if (e.doclet.hifiServerEntity) {
                 rows.push("Server Entity Scripts");
             }
@@ -140,6 +143,14 @@ exports.defineTags = function (dictionary) {
         }
     });
 
+    // @hifi-avatar-script
+    dictionary.defineTag("hifi-avatar", {
+        onTagged: function (doclet, tag) {
+            doclet.hifiAvatar = true;
+        }
+    });
+
+
     // @hifi-client-entity
     dictionary.defineTag("hifi-client-entity", {
         onTagged: function (doclet, tag) {
diff --git a/tools/nitpick/README.md b/tools/nitpick/README.md
index c5e3a5e21d..25f9001409 100644
--- a/tools/nitpick/README.md
+++ b/tools/nitpick/README.md
@@ -18,7 +18,7 @@ Nitpick has 5 functions, separated into separate tabs:
 ### Windows
 1.  (First time) download and install Python 3 from https://hifi-qa.s3.amazonaws.com/nitpick/Windows/python-3.7.0-amd64.exe (also located at https://www.python.org/downloads/)
     1. Click the "add python to path" checkbox on the python installer  
-    1. After installation - create an environment variable called PYTHON_PATH and set it to the folder containing the Python executable.
+    1. After installation - add the path to python.exe to the Windows PATH environment variable.
 1.  (First time) download and install AWS CLI from https://hifi-qa.s3.amazonaws.com/nitpick/Windows/AWSCLI64PY3.msi (also available at https://aws.amazon.com/cli/
     1.  Open a new command prompt and run  
         `aws configure`
@@ -31,7 +31,7 @@ Nitpick has 5 functions, separated into separate tabs:
 1.  (First time) Download adb (Android Debug Bridge) from *https://dl.google.com/android/repository/platform-tools-latest-windows.zip*
     1.  Copy the downloaded file to (for example) **C:\adb** and extract in place.  
         Verify you see *adb.exe* in **C:\adb\platform-tools\\**.
-    1.  Create an environment variable named ADB_PATH and set its value to the installation location (e.g. **C:\adb**)
+    1.  After installation - add the path to adb.exe to the Windows PATH environment variable (note that it is in *adb\platform-tools*).
 ### Mac
 1.  (first time) Install brew
     In a terminal:  
diff --git a/tools/nitpick/src/AWSInterface.cpp b/tools/nitpick/src/AWSInterface.cpp
index 59be26c383..63d5af9272 100644
--- a/tools/nitpick/src/AWSInterface.cpp
+++ b/tools/nitpick/src/AWSInterface.cpp
@@ -27,13 +27,30 @@ void AWSInterface::createWebPageFromResults(const QString& testResults,
                                             const QString& workingDirectory,
                                             QCheckBox* updateAWSCheckBox,
                                             QLineEdit* urlLineEdit) {
-    _testResults = testResults;
     _workingDirectory = workingDirectory;
 
+    // Verify filename is in correct format 
+    // For example `D:/tt/TestResults--2019-02-10_17-30-57(local)[DESKTOP-6BO62Q9].zip`
+    QStringList parts = testResults.split('/');
+    QString zipFilename = parts[parts.length() - 1];
+
+    QStringList zipFolderNameParts = zipFilename.split(QRegExp("[\\(\\)\\[\\]]"), QString::SkipEmptyParts);
+
+    if (!QRegularExpression("TestResults--\\d{4}(-\\d\\d){2}_\\d\\d(-\\d\\d){2}").match(zipFolderNameParts[0]).hasMatch() ||
+        !QRegularExpression("\\w").match(zipFolderNameParts[1]).hasMatch() ||                                                 // build (local, build number or PR number)
+        !QRegularExpression("\\w").match(zipFolderNameParts[2]).hasMatch()                                                  // machine name
+    ) {
+        QMessageBox::critical(0, "Filename is in wrong format", "'" + zipFilename + "' is not in nitpick format");
+        return;
+    }
+
+    _testResults = testResults;
+
     _urlLineEdit = urlLineEdit;
     _urlLineEdit->setEnabled(false);
 
-    extractTestFailuresFromZippedFolder();
+    QString zipFilenameWithoutExtension = zipFilename.split('.')[0];
+    extractTestFailuresFromZippedFolder(_workingDirectory + "/" + zipFilenameWithoutExtension);
     createHTMLFile();
 
     if (updateAWSCheckBox->isChecked()) {
@@ -44,14 +61,12 @@ void AWSInterface::createWebPageFromResults(const QString& testResults,
     }
 }
 
-void AWSInterface::extractTestFailuresFromZippedFolder() {
+void AWSInterface::extractTestFailuresFromZippedFolder(const QString& folderName) {
     // For a test results zip file called `D:/tt/TestResults--2018-10-02_16-54-11(9426)[DESKTOP-PMKNLSQ].zip`
     //   the folder will be called `TestResults--2018-10-02_16-54-11(9426)[DESKTOP-PMKNLSQ]`
     //   and, this folder will be in the working directory
-    QStringList parts = _testResults.split('/');
-    QString zipFolderName = _workingDirectory + "/" + parts[parts.length() - 1].split('.')[0];
-    if (QDir(zipFolderName).exists()) {
-        QDir dir = zipFolderName;
+    if (QDir(folderName).exists()) {
+        QDir dir = folderName;
         dir.removeRecursively();
     }
 
diff --git a/tools/nitpick/src/AWSInterface.h b/tools/nitpick/src/AWSInterface.h
index 63c48580f5..d95b8ecf2f 100644
--- a/tools/nitpick/src/AWSInterface.h
+++ b/tools/nitpick/src/AWSInterface.h
@@ -30,7 +30,7 @@ public:
                                   QCheckBox* updateAWSCheckBox,
                                   QLineEdit* urlLineEdit);
 
-    void extractTestFailuresFromZippedFolder();
+    void extractTestFailuresFromZippedFolder(const QString& folderName);
     void createHTMLFile();
 
     void startHTMLpage(QTextStream& stream);
diff --git a/tools/nitpick/src/AdbInterface.cpp b/tools/nitpick/src/AdbInterface.cpp
new file mode 100644
index 0000000000..82ef1446e3
--- /dev/null
+++ b/tools/nitpick/src/AdbInterface.cpp
@@ -0,0 +1,38 @@
+//
+//  AdbInterface.cpp
+//
+//  Created by Nissim Hadar on Feb 11, 2019.
+//  Copyright 2013 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
+//
+#include "AdbInterface.h"
+#include <PathUtils.h>
+
+#include <QFile>
+#include <QMessageBox>
+
+QString AdbInterface::getAdbCommand() {
+#ifdef Q_OS_WIN
+    if (_adbCommand.isNull()) {
+        QString adbPath = PathUtils::getPathToExecutable("adb.exe");
+        if (!adbPath.isNull()) {
+            _adbCommand = adbPath + _adbExe;
+        } else {
+            QMessageBox::critical(0, "python.exe not found",
+                "Please verify that pyton.exe is in the PATH");
+            exit(-1);
+        }
+    }
+#elif defined Q_OS_MAC
+    _adbCommand = "/usr/local/bin/adb";
+    if (!QFile::exists(_adbCommand)) {
+        QMessageBox::critical(0, "adb not found",
+            "adb not found at " + _adbCommand);
+        exit(-1);
+    }
+#endif
+
+    return _adbCommand;
+}
diff --git a/tools/nitpick/src/AdbInterface.h b/tools/nitpick/src/AdbInterface.h
new file mode 100644
index 0000000000..c1ce84c019
--- /dev/null
+++ b/tools/nitpick/src/AdbInterface.h
@@ -0,0 +1,30 @@
+//
+//  AdbInterface.h
+//
+//  Created by Nissim Hadar on Feb 11, 2019.
+//  Copyright 2013 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
+//
+#ifndef hifi_AdbInterface_h
+#define hifi_AdbInterface_h
+
+#include <QString>
+
+class AdbInterface {
+public:
+    QString getAdbCommand();
+
+private:
+#ifdef Q_OS_WIN
+    const QString _adbExe{ "adb.exe" };
+#else
+    // Both Mac and Linux use "python"
+    const QString _adbExe{ "adb" };
+#endif
+
+    QString _adbCommand;
+};
+
+#endif // hifi_AdbInterface_h
diff --git a/tools/nitpick/src/Nitpick.cpp b/tools/nitpick/src/Nitpick.cpp
index fa53730ce0..d5bc6f6e5a 100644
--- a/tools/nitpick/src/Nitpick.cpp
+++ b/tools/nitpick/src/Nitpick.cpp
@@ -40,7 +40,7 @@ Nitpick::Nitpick(QWidget* parent) : QMainWindow(parent) {
  
     _ui.plainTextEdit->setReadOnly(true);
 
-   setWindowTitle("Nitpick - v2.1.0");
+   setWindowTitle("Nitpick - v2.1.2");
 }
 
 Nitpick::~Nitpick() {
diff --git a/tools/nitpick/src/Nitpick.h b/tools/nitpick/src/Nitpick.h
index 29726be3bd..36ec7e534b 100644
--- a/tools/nitpick/src/Nitpick.h
+++ b/tools/nitpick/src/Nitpick.h
@@ -21,8 +21,6 @@
 #include "TestRunnerDesktop.h"
 #include "TestRunnerMobile.h"
 
-#include "AWSInterface.h"
-
 class Nitpick : public QMainWindow {
     Q_OBJECT
 
@@ -112,8 +110,6 @@ private:
     TestRunnerDesktop* _testRunnerDesktop{ nullptr };
     TestRunnerMobile* _testRunnerMobile{ nullptr };
 
-    AWSInterface _awsInterface;
-
     std::vector<Downloader*> _downloaders;
 
     // local storage for parameters - folder to store downloaded files in, and a list of their names
diff --git a/tools/nitpick/src/PathUtils.cpp b/tools/nitpick/src/PathUtils.cpp
new file mode 100644
index 0000000000..711570d568
--- /dev/null
+++ b/tools/nitpick/src/PathUtils.cpp
@@ -0,0 +1,31 @@
+//
+//  PathUtils.h
+//
+//  Created by Nissim Hadar on 11 Feb 2019.
+//  Copyright 2013 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
+//
+#include "PathUtils.h"
+
+#include <QFile>
+#include <QProcess>
+#include <QStringList>
+
+QString PathUtils::getPathToExecutable(const QString& executableName) {
+    QString path = QProcessEnvironment::systemEnvironment().value("PATH");
+
+    QStringList pathLocations = path.replace('\\', '/').split(';');
+
+    foreach (QString pathLocation, pathLocations) {
+        if (pathLocation[pathLocation.length() - 1] != '/') {
+            pathLocation += '/';
+        }
+        if (QFile::exists(pathLocation + executableName)) {
+            return pathLocation;
+        }
+    }
+
+    return QString();
+}
diff --git a/tools/nitpick/src/PathUtils.h b/tools/nitpick/src/PathUtils.h
new file mode 100644
index 0000000000..72f6839e3d
--- /dev/null
+++ b/tools/nitpick/src/PathUtils.h
@@ -0,0 +1,20 @@
+//
+//  PathUtils.h
+//
+//  Created by Nissim Hadar on 11 Feb 2019.
+//  Copyright 2013 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
+//
+#ifndef hifi_PathUtils_h
+#define hifi_PathUtils_h
+
+#include <QString>
+
+class PathUtils {
+public:
+    static QString getPathToExecutable(const QString& executableName);
+};
+
+#endif
\ No newline at end of file
diff --git a/tools/nitpick/src/PythonInterface.cpp b/tools/nitpick/src/PythonInterface.cpp
index 9e2fec005f..dcf4ecc682 100644
--- a/tools/nitpick/src/PythonInterface.cpp
+++ b/tools/nitpick/src/PythonInterface.cpp
@@ -8,36 +8,31 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 #include "PythonInterface.h"
+#include <PathUtils.h>
 
 #include <QFile>
 #include <QMessageBox>
-#include <QProcess>
 
-PythonInterface::PythonInterface() {
+QString PythonInterface::getPythonCommand() {
 #ifdef Q_OS_WIN
-    if (QProcessEnvironment::systemEnvironment().contains("PYTHON_PATH")) {
-        QString pythonPath = QProcessEnvironment::systemEnvironment().value("PYTHON_PATH");
-        if (!QFile::exists(pythonPath + "/" + _pythonExe)) {
-            QMessageBox::critical(0, _pythonExe, QString("Python executable not found in ") + pythonPath);
+    if (_pythonCommand.isNull()) {
+        QString pythonPath = PathUtils::getPathToExecutable("python.exe");
+        if (!pythonPath.isNull()) {
+            _pythonCommand = pythonPath + _pythonExe;
+        } else {
+            QMessageBox::critical(0, "python.exe not found",
+                "Please verify that pyton.exe is in the PATH");
             exit(-1);
         }
-        
-        _pythonCommand = pythonPath + "/" + _pythonExe;
-    } else {
-        QMessageBox::critical(0, "PYTHON_PATH not defined",
-                              "Please set PYTHON_PATH to directory containing the Python executable");
-        exit(-1);
     }
 #elif defined Q_OS_MAC
     _pythonCommand = "/usr/local/bin/python3";
     if (!QFile::exists(_pythonCommand)) {
         QMessageBox::critical(0, "python not found",
-                              "python3 not found at " + _pythonCommand);
+            "python3 not found at " + _pythonCommand);
         exit(-1);
     }
 #endif
-}
 
-QString PythonInterface::getPythonCommand() {
     return _pythonCommand;
 }
diff --git a/tools/nitpick/src/PythonInterface.h b/tools/nitpick/src/PythonInterface.h
index 947b359037..7972d55cce 100644
--- a/tools/nitpick/src/PythonInterface.h
+++ b/tools/nitpick/src/PythonInterface.h
@@ -14,8 +14,6 @@
 
 class PythonInterface {
 public:
-    PythonInterface();
-
     QString getPythonCommand();
 
 private:
diff --git a/tools/nitpick/src/Test.cpp b/tools/nitpick/src/Test.cpp
index 20d5ad89a6..f1e950db88 100644
--- a/tools/nitpick/src/Test.cpp
+++ b/tools/nitpick/src/Test.cpp
@@ -24,7 +24,7 @@ extern Nitpick* nitpick;
 
 #include <math.h>
 
-Test::Test(QProgressBar* progressBar, QCheckBox* checkBoxInteractiveMode) {
+Test::Test(QProgressBar* progressBar, QCheckBox* checkBoxInteractiveMode) : _awsInterface(NULL) {
     _progressBar = progressBar;
     _checkBoxInteractiveMode = checkBoxInteractiveMode;
 
@@ -835,11 +835,16 @@ void Test::createRecursiveScript(const QString& directory, bool interactiveMode)
         << endl;
     textStream << "Script.include(PATH_TO_THE_REPO_PATH_UTILS_FILE);" << endl << endl;
 
-    textStream << "if (typeof nitpick === 'undefined') nitpick = createNitpick(Script.resolvePath(\".\"));" << endl;
-    textStream << "if (typeof testsRootPath === 'undefined') testsRootPath = nitpick.getTestsRootPath();" << endl << endl;
-
-    textStream << "nitpick.enableRecursive();" << endl;
-    textStream << "nitpick.enableAuto();" << endl << endl;
+    // The 'depth' variable is used to signal when to start running the recursive scripts
+    textStream << "if (typeof depth === 'undefined') {" << endl; 
+    textStream << "   depth = 0;" << endl;
+    textStream << "   nitpick = createNitpick(Script.resolvePath(\".\"));" << endl;
+    textStream << "   testsRootPath = nitpick.getTestsRootPath();" << endl << endl;
+    textStream << "   nitpick.enableRecursive();" << endl;
+    textStream << "   nitpick.enableAuto();" << endl;
+    textStream << "} else {" << endl;
+    textStream << "   depth++" << endl;
+    textStream << "}" << endl << endl;
 
     // Now include the test scripts
     for (int i = 0; i < directories.length(); ++i) {
@@ -847,8 +852,9 @@ void Test::createRecursiveScript(const QString& directory, bool interactiveMode)
     }
 
     textStream << endl;
-    textStream << "if (typeof runningRecursive === 'undefined') {" << endl; 
-    textStream << "   runningRecursive = true;" << endl;
+    textStream << "if (depth > 0) {" << endl;
+    textStream << "   depth--;" << endl;
+    textStream << "} else {" << endl;
     textStream << "   nitpick.runRecursive();" << endl;
     textStream << "}" << endl << endl;
 
@@ -966,11 +972,15 @@ void Test::createTestRailTestCases() {
         return;
     }
 
+    if (!_testRailInterface) {
+        _testRailInterface = new TestRailInterface;
+    }
+
     if (_testRailCreateMode == PYTHON) {
-        _testRailInterface.createTestSuitePython(_testDirectory, outputDirectory, nitpick->getSelectedUser(),
+        _testRailInterface->createTestSuitePython(_testDirectory, outputDirectory, nitpick->getSelectedUser(),
                                               nitpick->getSelectedBranch());
     } else {
-        _testRailInterface.createTestSuiteXML(_testDirectory, outputDirectory, nitpick->getSelectedUser(),
+        _testRailInterface->createTestSuiteXML(_testDirectory, outputDirectory, nitpick->getSelectedUser(),
                                            nitpick->getSelectedBranch());
     }
 }
@@ -983,7 +993,12 @@ void Test::createTestRailRun() {
         return;
     }
 
-    _testRailInterface.createTestRailRun(outputDirectory);
+
+    if (!_testRailInterface) {
+        _testRailInterface = new TestRailInterface;
+    }
+
+    _testRailInterface->createTestRailRun(outputDirectory);
 }
 
 void Test::updateTestRailRunResult() {
@@ -999,7 +1014,12 @@ void Test::updateTestRailRunResult() {
         return;
     }
 
-    _testRailInterface.updateTestRailRunResults(testResults, tempDirectory);
+
+    if (!_testRailInterface) {
+        _testRailInterface = new TestRailInterface;
+    }
+
+    _testRailInterface->updateTestRailRunResults(testResults, tempDirectory);
 }
 
 QStringList Test::createListOfAll_imagesInDirectory(const QString& imageFormat, const QString& pathToImageDirectory) {
@@ -1077,7 +1097,7 @@ void Test::setTestRailCreateMode(TestRailCreateMode testRailCreateMode) {
 
 void Test::createWebPage(QCheckBox* updateAWSCheckBox, QLineEdit* urlLineEdit) {
     QString testResults = QFileDialog::getOpenFileName(nullptr, "Please select the zipped test results to update from", nullptr,
-                                                       "Zipped Test Results (*.zip)");
+                                                       "Zipped Test Results (TestResults--*.zip)");
     if (testResults.isNull()) {
         return;
     }
@@ -1088,5 +1108,9 @@ void Test::createWebPage(QCheckBox* updateAWSCheckBox, QLineEdit* urlLineEdit) {
         return;
     }
 
-    _awsInterface.createWebPageFromResults(testResults, workingDirectory, updateAWSCheckBox, urlLineEdit);
+    if (!_awsInterface) {
+        _awsInterface = new AWSInterface;
+    }
+
+    _awsInterface->createWebPageFromResults(testResults, workingDirectory, updateAWSCheckBox, urlLineEdit);
 }
\ No newline at end of file
diff --git a/tools/nitpick/src/Test.h b/tools/nitpick/src/Test.h
index 842e4bdb48..166c71688d 100644
--- a/tools/nitpick/src/Test.h
+++ b/tools/nitpick/src/Test.h
@@ -159,10 +159,10 @@ private:
 
     bool _exitWhenComplete{ false };
 
-    TestRailInterface _testRailInterface;
+    TestRailInterface* _testRailInterface;
     TestRailCreateMode _testRailCreateMode { PYTHON };
 
-    AWSInterface _awsInterface;
+    AWSInterface* _awsInterface;
 };
 
 #endif // hifi_test_h
\ No newline at end of file
diff --git a/tools/nitpick/src/TestRailInterface.cpp b/tools/nitpick/src/TestRailInterface.cpp
index 1d7aa0a32f..6ed13a72b6 100644
--- a/tools/nitpick/src/TestRailInterface.cpp
+++ b/tools/nitpick/src/TestRailInterface.cpp
@@ -20,7 +20,7 @@
 #include <QMessageBox>
 #include <QTextStream>
 
-TestRailInterface::TestRailInterface() {
+TestRailInterface::TestRailInterface() : _pythonInterface(NULL) {
     _testRailTestCasesSelectorWindow.setURL("https://highfidelity.testrail.net");
     _testRailTestCasesSelectorWindow.setUser("@highfidelity.io");
 
diff --git a/tools/nitpick/src/TestRunnerDesktop.cpp b/tools/nitpick/src/TestRunnerDesktop.cpp
index 50cb6f9a07..e45d895886 100644
--- a/tools/nitpick/src/TestRunnerDesktop.cpp
+++ b/tools/nitpick/src/TestRunnerDesktop.cpp
@@ -554,7 +554,7 @@ void TestRunnerDesktop::evaluateResults() {
     nitpick->startTestsEvaluation(false, true, _snapshotFolder, _branch, _user);
 }
 
-void TestRunnerDesktop::automaticTestRunEvaluationComplete(QString zippedFolder, int numberOfFailures) {
+void TestRunnerDesktop::automaticTestRunEvaluationComplete(const QString& zippedFolder, int numberOfFailures) {
     addBuildNumberToResults(zippedFolder);
     restoreHighFidelityAppDataFolder();
 
@@ -580,14 +580,19 @@ void TestRunnerDesktop::automaticTestRunEvaluationComplete(QString zippedFolder,
     _runNow->setEnabled(true);
 }
 
-void TestRunnerDesktop::addBuildNumberToResults(QString zippedFolderName) {
-    QString augmentedFilename;
+void TestRunnerDesktop::addBuildNumberToResults(const QString& zippedFolderName) {
+    QString augmentedFilename { zippedFolderName };
     if (!_runLatest->isChecked()) {
-        augmentedFilename = zippedFolderName.replace("local", getPRNumberFromURL(_url->text()));
+        augmentedFilename.replace("local", getPRNumberFromURL(_url->text()));
     } else {
-        augmentedFilename = zippedFolderName.replace("local", _buildInformation.build);
+        augmentedFilename.replace("local", _buildInformation.build);
+    }
+
+    if (!QFile::rename(zippedFolderName, augmentedFilename)) {
+        QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Could not rename '" + zippedFolderName + "' to '" + augmentedFilename);
+        exit(-1);
+    
     }
-    QFile::rename(zippedFolderName, augmentedFilename);
 }
 
 void TestRunnerDesktop::restoreHighFidelityAppDataFolder() {
diff --git a/tools/nitpick/src/TestRunnerDesktop.h b/tools/nitpick/src/TestRunnerDesktop.h
index a8f828b9d4..140a81f465 100644
--- a/tools/nitpick/src/TestRunnerDesktop.h
+++ b/tools/nitpick/src/TestRunnerDesktop.h
@@ -61,8 +61,8 @@ public:
     void runInterfaceWithTestScript();
 
     void evaluateResults();
-    void automaticTestRunEvaluationComplete(QString zippedFolderName, int numberOfFailures);
-    void addBuildNumberToResults(QString zippedFolderName);
+    void automaticTestRunEvaluationComplete(const QString& zippedFolderName, int numberOfFailures);
+    void addBuildNumberToResults(const QString& zippedFolderName);
 
     void copyFolder(const QString& source, const QString& destination);
 
diff --git a/tools/nitpick/src/TestRunnerMobile.cpp b/tools/nitpick/src/TestRunnerMobile.cpp
index e1c82854f4..ab276f3337 100644
--- a/tools/nitpick/src/TestRunnerMobile.cpp
+++ b/tools/nitpick/src/TestRunnerMobile.cpp
@@ -30,7 +30,7 @@ TestRunnerMobile::TestRunnerMobile(
     QLabel* statusLabel,
 
     QObject* parent
-) : QObject(parent) 
+) : QObject(parent), _adbInterface(NULL)
 {
     _workingFolderLabel = workingFolderLabel;
     _connectDeviceButton = connectDeviceButton;
@@ -47,30 +47,6 @@ TestRunnerMobile::TestRunnerMobile(
     folderLineEdit->setText("/sdcard/DCIM/TEST");
 
     modelNames["SM_G955U1"] = "Samsung S8+ unlocked";
-
-    // Find ADB (Android Debugging Bridge)
-#ifdef Q_OS_WIN
-    if (QProcessEnvironment::systemEnvironment().contains("ADB_PATH")) {
-        QString adbExePath = QProcessEnvironment::systemEnvironment().value("ADB_PATH") + "/platform-tools";
-        if (!QFile::exists(adbExePath + "/" + _adbExe)) {
-            QMessageBox::critical(0, _adbExe, QString("ADB executable not found in ") + adbExePath);
-            exit(-1);
-        }
-
-        _adbCommand = adbExePath + "/" + _adbExe;
-    } else {
-        QMessageBox::critical(0, "ADB_PATH not defined",
-            "Please set ADB_PATH to directory containing the `adb` executable");
-        exit(-1);
-    }
-#elif defined Q_OS_MAC
-    _adbCommand = "/usr/local/bin/adb";
-    if (!QFile::exists(_adbCommand)) {
-        QMessageBox::critical(0, "adb not found",
-            "python3 not found at " + _adbCommand);
-        exit(-1);
-    }
-#endif
 }
 
 TestRunnerMobile::~TestRunnerMobile() {
@@ -84,8 +60,12 @@ void TestRunnerMobile::setWorkingFolderAndEnableControls() {
 
 void TestRunnerMobile::connectDevice() {
 #if defined Q_OS_WIN || defined Q_OS_MAC
+    if (!_adbInterface) {
+        _adbInterface = new AdbInterface();
+    }
+    
     QString devicesFullFilename{ _workingFolder + "/devices.txt" };
-    QString command = _adbCommand + " devices -l > " + devicesFullFilename;
+    QString command = _adbInterface->getAdbCommand() + " devices -l > " + devicesFullFilename;
     system(command.toStdString().c_str());
 
     if (!QFile::exists(devicesFullFilename)) {
@@ -100,12 +80,13 @@ void TestRunnerMobile::connectDevice() {
     QString line2 = devicesFile.readLine();
 
     const QString DEVICE{ "device" };
-    if (line2.contains(DEVICE)) {
-        // Make sure only 1 device
+    if (line2.contains("unauthorized")) {
+        QMessageBox::critical(0, "Unauthorized device detected", "Please allow USB debugging on device");
+    } else if (line2.contains(DEVICE)) {
+            // Make sure only 1 device
         QString line3 = devicesFile.readLine();
         if (line3.contains(DEVICE)) {
             QMessageBox::critical(0, "Too many devices detected", "Tests will run only if a single device is attached");
-
         } else {
             // Line looks like this: 988a1b47335239434b     device product:dream2qlteue model:SM_G955U1 device:dream2qlteue transport_id:2
             QStringList tokens = line2.split(QRegExp("[\r\n\t ]+"));
@@ -169,8 +150,12 @@ void TestRunnerMobile::downloadComplete() {
 
 void TestRunnerMobile::installAPK() {
 #if defined Q_OS_WIN || defined Q_OS_MAC
+    if (!_adbInterface) {
+        _adbInterface = new AdbInterface();
+    }
+
     _statusLabel->setText("Installing");
-    QString command = _adbCommand + " install -r -d " + _workingFolder + "/" + _installerFilename + " >" + _workingFolder  + "/installOutput.txt";
+    QString command = _adbInterface->getAdbCommand() + " install -r -d " + _workingFolder + "/" + _installerFilename + " >" + _workingFolder  + "/installOutput.txt";
     system(command.toStdString().c_str());
     _statusLabel->setText("Installation complete");
     _runInterfacePushbutton->setEnabled(true);
@@ -179,8 +164,12 @@ void TestRunnerMobile::installAPK() {
 
 void TestRunnerMobile::runInterface() {
 #if defined Q_OS_WIN || defined Q_OS_MAC
+    if (!_adbInterface) {
+        _adbInterface = new AdbInterface();
+    }
+
     _statusLabel->setText("Starting Interface");
-    QString command = _adbCommand + " shell monkey -p io.highfidelity.hifiinterface -v 1";
+    QString command = _adbInterface->getAdbCommand() + " shell monkey -p io.highfidelity.hifiinterface -v 1";
     system(command.toStdString().c_str());
     _statusLabel->setText("Interface started");
 #endif
@@ -188,8 +177,12 @@ void TestRunnerMobile::runInterface() {
 
 void TestRunnerMobile::pullFolder() {
 #if defined Q_OS_WIN || defined Q_OS_MAC
+    if (!_adbInterface) {
+        _adbInterface = new AdbInterface();
+    }
+
     _statusLabel->setText("Pulling folder");
-    QString command = _adbCommand + " pull " + _folderLineEdit->text() + " " + _workingFolder + _installerFilename;
+    QString command = _adbInterface->getAdbCommand() + " pull " + _folderLineEdit->text() + " " + _workingFolder + _installerFilename;
     system(command.toStdString().c_str());
     _statusLabel->setText("Pull complete");
 #endif
diff --git a/tools/nitpick/src/TestRunnerMobile.h b/tools/nitpick/src/TestRunnerMobile.h
index 247f864976..52c2ba096d 100644
--- a/tools/nitpick/src/TestRunnerMobile.h
+++ b/tools/nitpick/src/TestRunnerMobile.h
@@ -17,6 +17,7 @@
 #include <QPushButton>
 
 #include "TestRunner.h"
+#include "AdbInterface.h"
 
 class TestRunnerMobile : public QObject, public TestRunner {
     Q_OBJECT
@@ -70,5 +71,7 @@ private:
     QString _adbCommand;
 
     std::map<QString, QString> modelNames;
+
+    AdbInterface* _adbInterface;
 };
 #endif
diff --git a/tools/nitpick/ui/Nitpick.ui b/tools/nitpick/ui/Nitpick.ui
index 16aaa9594d..79bdfd158b 100644
--- a/tools/nitpick/ui/Nitpick.ui
+++ b/tools/nitpick/ui/Nitpick.ui
@@ -686,8 +686,8 @@
      <widget class="QLabel" name="workingFolderLabel_4">
       <property name="geometry">
        <rect>
-        <x>290</x>
-        <y>20</y>
+        <x>300</x>
+        <y>60</y>
         <width>41</width>
         <height>31</height>
        </rect>
@@ -699,8 +699,8 @@
      <widget class="QLabel" name="statusLabelOnMobile">
       <property name="geometry">
        <rect>
-        <x>340</x>
-        <y>20</y>
+        <x>350</x>
+        <y>60</y>
         <width>271</width>
         <height>31</height>
        </rect>
diff --git a/tools/skeleton-dump/CMakeLists.txt b/tools/skeleton-dump/CMakeLists.txt
index baec1d163b..7d4248d171 100644
--- a/tools/skeleton-dump/CMakeLists.txt
+++ b/tools/skeleton-dump/CMakeLists.txt
@@ -2,3 +2,5 @@ set(TARGET_NAME skeleton-dump)
 setup_hifi_project(Core)
 setup_memory_debugger()
 link_hifi_libraries(shared fbx hfm graphics gpu gl animation)
+
+include_hifi_library_headers(image)
diff --git a/tools/vhacd-util/CMakeLists.txt b/tools/vhacd-util/CMakeLists.txt
index aa6642c610..90cfdf878a 100644
--- a/tools/vhacd-util/CMakeLists.txt
+++ b/tools/vhacd-util/CMakeLists.txt
@@ -2,6 +2,8 @@ set(TARGET_NAME vhacd-util)
 setup_hifi_project(Core)
 link_hifi_libraries(shared fbx hfm graphics gpu gl)
 
+include_hifi_library_headers(image)
+
 add_dependency_external_projects(vhacd)
 
 find_package(VHACD REQUIRED)