diff --git a/animation-server/src/main.cpp b/animation-server/src/main.cpp
index 125a14f6b6..2e13636eda 100644
--- a/animation-server/src/main.cpp
+++ b/animation-server/src/main.cpp
@@ -628,7 +628,7 @@ void* animateVoxels(void* args) {
         int packetsEnding = ::voxelEditPacketSender->packetsToSendCount();
         
         if (firstTime) {
-            int packetsPerSecond = (packetsEnding - packetsStarting) * (ACTUAL_FPS);
+            int packetsPerSecond = std::max(ACTUAL_FPS, (packetsEnding - packetsStarting) * (ACTUAL_FPS));
 
             std::cout << "Setting PPS to " << packetsPerSecond << "\n";
 
diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp
index 8b92448e71..d4e0eede6c 100644
--- a/interface/src/Menu.cpp
+++ b/interface/src/Menu.cpp
@@ -250,6 +250,9 @@ Menu::Menu() :
 
     addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::FastVoxelPipeline, 0,
                                            false, appInstance->getVoxels(), SLOT(setUseFastVoxelPipeline(bool)));
+
+    addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DontRemoveOutOfView);
+    addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::HideOutOfView);
     addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelTextures);
     addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::AmbientOcclusion);
                                            
diff --git a/interface/src/Menu.h b/interface/src/Menu.h
index f54d3767a1..de648c1407 100644
--- a/interface/src/Menu.h
+++ b/interface/src/Menu.h
@@ -141,6 +141,7 @@ namespace MenuOption {
     const QString DisplayFrustum = "Display Frustum";
     const QString DontRenderVoxels = "Don't call _voxels.render()";
     const QString DontCallOpenGLForVoxels = "Don't call glDrawElements()/glDrawRangeElementsEXT() for Voxels";
+    const QString DontRemoveOutOfView = "Don't Remove Out of View Voxels";
     const QString EchoAudio = "Echo Audio";
     const QString ExportVoxels = "Export Voxels";
     const QString HeadMouse = "Head Mouse";
@@ -161,6 +162,7 @@ namespace MenuOption {
     const QString GlowMode = "Cycle Glow Mode";
     const QString GoToDomain = "Go To Domain...";
     const QString GoToLocation = "Go To Location...";
+    const QString HideOutOfView = "Hide Out of View Voxels";
     const QString ImportVoxels = "Import Voxels";
     const QString ImportVoxelsClipboard = "Import Voxels to Clipboard";
     const QString IncreaseAvatarSize = "Increase Avatar Size";
diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp
index c8791000c6..0984838052 100644
--- a/interface/src/VoxelSystem.cpp
+++ b/interface/src/VoxelSystem.cpp
@@ -109,12 +109,15 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels)
 
 void VoxelSystem::voxelDeleted(VoxelNode* node) {
     if (node->isKnownBufferIndex() && (node->getVoxelSystem() == this)) {
+        forceRemoveNodeFromArraysAsPartialVBO(node);
+        
+        /***
         if (!_useFastVoxelPipeline || _inSetupNewVoxelsForDrawing || _writeRenderFullVBO) {
             freeBufferIndex(node->getBufferIndex());
         } else {
             forceRemoveNodeFromArraysAsPartialVBO(node);
-            freeBufferIndex(node->getBufferIndex());
         }
+        ***/
     }
 }
 
@@ -160,7 +163,9 @@ void VoxelSystem::voxelUpdated(VoxelNode* node) {
             }
         }
 
-        updateNodeInArraysAsPartialVBO(node);
+        const bool REUSE_INDEX = true;
+        const bool DONT_FORCE_REDRAW = false;
+        updateNodeInArrays(node, REUSE_INDEX, DONT_FORCE_REDRAW);
         _voxelsUpdated++;
 
         node->clearDirtyBit(); // clear the dirty bit, do this before we potentially delete things.
@@ -185,26 +190,44 @@ glBufferIndex VoxelSystem::getNextBufferIndex() {
     return output;
 }
 
-// Doesn't actually clean up the VBOs for the index, but does release responsibility of the index from the VoxelNode, 
-// and makes the index available for some other node to use
+// Release responsibility of the buffer/vbo index from the VoxelNode, and makes the index available for some other node to use
+// will also "clean up" the index data for the buffer/vbo slot, so that if it's in the middle of the draw range, the triangles
+// will be "invisible"
 void VoxelSystem::freeBufferIndex(glBufferIndex index) {
-    _freeIndexes.push_back(index);
+    assert(_voxelsInWriteArrays > 0);
+
+    // if the "freed" index was our max index, then just drop the _voxelsInWriteArrays down one...
+    if (index == (_voxelsInWriteArrays - 1)) {
+        _voxelsInWriteArrays--;
+    } else {
+        bool inList = false;
+        // make sure the index isn't already in the free list...
+        for (long i = 0; i < _freeIndexes.size(); i++) {
+            if (_freeIndexes[i] == index) {
+                printf("freeBufferIndex(glBufferIndex index)... index=%ld already in free list!\n", index);
+                inList = true;
+                break;
+            }
+        }
+        
+        if (!inList) {
+            // make the index available for next node that needs to be drawn
+            _freeIndexes.push_back(index);
+    
+            // make the VBO slot "invisible" in case this slot is not used
+            const glm::vec3 startVertex(FLT_MAX, FLT_MAX, FLT_MAX);
+            const float voxelScale = 0;
+            const nodeColor BLACK = {0, 0, 0, 0};
+            updateArraysDetails(index, startVertex, voxelScale, BLACK);
+        }
+    }
 }
 
 // This will run through the list of _freeIndexes and reset their VBO array values to be "invisible".
 void VoxelSystem::clearFreeBufferIndexes() {
     PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "###### clearFreeBufferIndexes()");
-
-    for (int i = 0; i < _freeIndexes.size(); i++) {
-        glBufferIndex nodeIndex = _freeIndexes[i];
-        glm::vec3 startVertex(FLT_MAX, FLT_MAX, FLT_MAX);
-        float voxelScale = 0;
-        _writeVoxelDirtyArray[nodeIndex] = true;
-        nodeColor color = {0, 0, 0, 0};
-        updateNodeInArrays(nodeIndex, startVertex, voxelScale, color);
-        _abandonedVBOSlots++;
-    }
     _freeIndexes.clear();
+    _abandonedVBOSlots = 0;
 }
 
 VoxelSystem::~VoxelSystem() {
@@ -589,8 +612,11 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
 
 
     if (!_useFastVoxelPipeline || _writeRenderFullVBO) {
+//printf("parseData about to call setupNewVoxelsForDrawing();\n");    
         setupNewVoxelsForDrawing();
     } else {
+printf("parseData about to call checkForCulling() and setupNewVoxelsForDrawingSingleNode(DONT_BAIL_EARLY)\n");    
+        checkForCulling();
         setupNewVoxelsForDrawingSingleNode(DONT_BAIL_EARLY);
     }
     
@@ -611,7 +637,7 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
     uint64_t sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000;
     
     // clear up the VBOs for any nodes that have been recently deleted.
-    clearFreeBufferIndexes();
+    //clearFreeBufferIndexes();
 
     bool iAmDebugging = false;  // if you're debugging set this to true, so you won't get skipped for slow debugging
     if (!iAmDebugging && sinceLastTime <= std::max((float) _setupNewVoxelsForDrawingLastElapsed, SIXTY_FPS_IN_MILLISECONDS)) {
@@ -631,6 +657,7 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
         PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), buffer);
         _callsToTreesToArrays++;
         if (_writeRenderFullVBO) {
+            printf("resetting _freeIndexes and _voxelsInWriteArrays\n");
             _voxelsInWriteArrays = 0; // reset our VBO
             _freeIndexes.clear(); // reset our free indexes
         }
@@ -676,7 +703,7 @@ void VoxelSystem::setupNewVoxelsForDrawingSingleNode(bool allowBailEarly) {
     uint64_t sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000;
 
     // clear up the VBOs for any nodes that have been recently deleted.
-    clearFreeBufferIndexes();
+    //clearFreeBufferIndexes();
 
     bool iAmDebugging = false;  // if you're debugging set this to true, so you won't get skipped for slow debugging
     if (allowBailEarly && !iAmDebugging && 
@@ -684,7 +711,7 @@ void VoxelSystem::setupNewVoxelsForDrawingSingleNode(bool allowBailEarly) {
         return; // bail early, it hasn't been long enough since the last time we ran
     }
 
-    checkForCulling(); // check for out of view and deleted voxels...
+    //checkForCulling(); // check for out of view and deleted voxels...
 
     // lock on the buffer write lock so we can't modify the data when the GPU is reading it
     {
@@ -723,7 +750,12 @@ void VoxelSystem::checkForCulling() {
         // When we call removeOutOfView() voxels, we don't actually remove the voxels from the VBOs, but we do remove
         // them from tree, this makes our tree caclulations faster, but doesn't require us to fully rebuild the VBOs (which
         // can be expensive).
-        removeOutOfView();
+        if (Menu::getInstance()->isOptionChecked(MenuOption::HideOutOfView)) {
+            hideOutOfView();
+        }
+        if (!Menu::getInstance()->isOptionChecked(MenuOption::DontRemoveOutOfView)) {
+            removeOutOfView();
+        }
         
         // Once we call cleanupRemovedVoxels() we do need to rebuild our VBOs (if anything was actually removed). So,
         // we should consider putting this someplace else... as this might be able to occur less frequently, and save us on
@@ -747,6 +779,7 @@ void VoxelSystem::cleanupRemovedVoxels() {
         }
         _writeRenderFullVBO = true; // if we remove voxels, we must update our full VBOs
     }
+
     // we also might have VBO slots that have been abandoned, if too many of our VBO slots
     // are abandonded we want to rerender our full VBOs
     const float TOO_MANY_ABANDONED_RATIO = 0.5f;
@@ -830,6 +863,7 @@ void VoxelSystem::copyWrittenDataSegmentToReadArrays(glBufferIndex segmentStart,
 void VoxelSystem::copyWrittenDataToReadArrays(bool fullVBOs) {
     PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
                             "copyWrittenDataToReadArrays()");
+
     if (_voxelsDirty && _voxelsUpdated) {
         if (fullVBOs) {
             copyWrittenDataToReadArraysFullVBOs();
@@ -869,39 +903,21 @@ int VoxelSystem::newTreeToArrays(VoxelNode* node) {
         }
     }
     if (_writeRenderFullVBO) {
-        voxelsUpdated += updateNodeInArraysAsFullVBO(node);
+        const bool DONT_REUSE_INDEX = false;
+        const bool FORCE_REDRAW = true;
+//printf("newTreeToArrays() about to call updateNodeInArrays(node, DONT_REUSE_INDEX, FORCE_REDRAW);\n");
+        voxelsUpdated += updateNodeInArrays(node, DONT_REUSE_INDEX, FORCE_REDRAW);
     } else {
-        voxelsUpdated += updateNodeInArraysAsPartialVBO(node);
+        const bool REUSE_INDEX = true;
+        const bool DONT_FORCE_REDRAW = false;
+//printf("newTreeToArrays() about to call updateNodeInArrays(node, REUSE_INDEX, DONT_FORCE_REDRAW);\n");
+        voxelsUpdated += updateNodeInArrays(node, REUSE_INDEX, DONT_FORCE_REDRAW);
     }
     node->clearDirtyBit(); // clear the dirty bit, do this before we potentially delete things.
     
     return voxelsUpdated;
 }
 
-int VoxelSystem::updateNodeInArraysAsFullVBO(VoxelNode* node) {
-    // If we've run out of room, then just bail...
-    if (_voxelsInWriteArrays >= _maxVoxels) {
-        return 0;
-    }
-    
-    if (node->getShouldRender()) {
-        glm::vec3 startVertex = node->getCorner();
-        float voxelScale = node->getScale();
-        glBufferIndex nodeIndex = getNextBufferIndex();
-
-        // populate the array with points for the 8 vertices
-        // and RGB color for each added vertex
-        updateNodeInArrays(nodeIndex, startVertex, voxelScale, node->getColor());
-        node->setBufferIndex(nodeIndex);
-        node->setVoxelSystem(this);
-        return 1; // rendered
-    } else {
-        node->setBufferIndex(GLBUFFER_INDEX_UNKNOWN);
-    }
-    
-    return 0; // not-rendered
-}
-
 // called as response to voxelDeleted() in fast pipeline case. The node
 // is being deleted, but it's state is such that it thinks it should render
 // and therefore we can't use the normal render calculations. This method
@@ -914,29 +930,22 @@ int VoxelSystem::forceRemoveNodeFromArraysAsPartialVBO(VoxelNode* node) {
 
     // if the node is not in the VBOs then we have nothing to do!
     if (node->isKnownBufferIndex()) {
-
-        // if we shouldn't render then set out location to some infinitely distant location, 
-        // and our scale as infinitely small
-        glm::vec3 startVertex(FLT_MAX, FLT_MAX, FLT_MAX);
-        float voxelScale = 0;
-        _abandonedVBOSlots++;
-
         // If this node has not yet been written to the array, then add it to the end of the array.
         glBufferIndex nodeIndex = node->getBufferIndex();
-
-        _writeVoxelDirtyArray[nodeIndex] = true;
-
-        // populate the array with points for the 8 vertices
-        // and RGB color for each added vertex
-        const nodeColor BLACK = { 0,0,0};
-        updateNodeInArrays(nodeIndex, startVertex, voxelScale, BLACK);
-        
+        freeBufferIndex(nodeIndex); // NOTE: This is make the node invisible!
+        node->setBufferIndex(GLBUFFER_INDEX_UNKNOWN);
         return 1; // updated!
     }
     return 0; // not-updated
 }
 
-int VoxelSystem::updateNodeInArraysAsPartialVBO(VoxelNode* node) {
+int VoxelSystem::updateNodeInArrays(VoxelNode* node, bool reuseIndex, bool forceDraw) {
+
+/*
+printf("updateNodeInArrays(VoxelNode* node=[%f, %f, %f] %f, bool reuseIndex=%s, bool forceDraw=%s)\n",
+    node->getCorner().x,node->getCorner().y,node->getCorner().z, node->getScale(),
+    debug::valueOf(reuseIndex),debug::valueOf(forceDraw));
+*/  
     // If we've run out of room, then just bail...
     if (_voxelsInWriteArrays >= _maxVoxels) {
         return 0;
@@ -946,64 +955,76 @@ int VoxelSystem::updateNodeInArraysAsPartialVBO(VoxelNode* node) {
         return 0;
     }
     
-    // Now, if we've changed any attributes (our renderness, our color, etc) then update the Arrays...
-    if (node->isDirty()) {
-        glm::vec3 startVertex;
-        float voxelScale = 0;
+    // If we've changed any attributes (our renderness, our color, etc), or we've been told to force a redraw
+    // then update the Arrays...
+    if (forceDraw || node->isDirty()) {
+        /*
+        printf("updateNodeInArrays(VoxelNode* node=[%f, %f, %f] %f, bool reuseIndex=%s, bool forceDraw=%s)\n",
+            node->getCorner().x,node->getCorner().y,node->getCorner().z, node->getScale(),
+            debug::valueOf(reuseIndex),debug::valueOf(forceDraw));
+        */
+        //printf("....(isDirty() [isDirty()=%s] or forceRedraw=%s_...\n",debug::valueOf(node->isDirty()),debug::valueOf(forceDraw));
+            
         // If we're should render, use our legit location and scale, 
         if (node->getShouldRender()) {
-            startVertex = node->getCorner();
-            voxelScale = node->getScale();
-        } else {
-            // if we shouldn't render then set out location to some infinitely distant location, 
-            // and our scale as infinitely small
-            startVertex[0] = startVertex[1] = startVertex[2] = FLT_MAX;
-            voxelScale = 0;
-            _abandonedVBOSlots++;
-        }
+            //printf("....shouldRender()...\n");
+            glm::vec3 startVertex = node->getCorner();
+            float voxelScale = node->getScale();
 
-        // If this node has not yet been written to the array, then add it to the end of the array.
-        glBufferIndex nodeIndex;
-        if (node->isKnownBufferIndex()) {
-            nodeIndex = node->getBufferIndex();
+            glBufferIndex nodeIndex = GLBUFFER_INDEX_UNKNOWN;
+            if (reuseIndex && node->isKnownBufferIndex()) {
+                //printf("....shouldRender() and reuseIndex && node->isKnownBufferIndex() reusing...\n");
+                nodeIndex = node->getBufferIndex();
+            } else {
+                //printf("....shouldRender() and NOT(reuseIndex && node->isKnownBufferIndex()) getting new index...\n");
+                nodeIndex = getNextBufferIndex();
+                node->setBufferIndex(nodeIndex);
+                node->setVoxelSystem(this);
+            }
+            // populate the array with points for the 8 vertices and RGB color for each added vertex
+            updateArraysDetails(nodeIndex, startVertex, voxelScale, node->getColor());
+            return 1; // updated!
         } else {
-            nodeIndex = getNextBufferIndex();
-            node->setBufferIndex(nodeIndex);
-            node->setVoxelSystem(this);
+            //printf("....NOT shouldRender()...\n");
+            // If we shouldn't render, but we did have a known index, then we will need to release our index
+            if (reuseIndex && node->isKnownBufferIndex()) {
+                //printf("....NOT shouldRender() and reuseIndex && node->isKnownBufferIndex() freeBufferIndex!...\n");
+                freeBufferIndex(node->getBufferIndex()); // this will make the node invisible
+                return 1; // updated!
+            }
         }
-        _writeVoxelDirtyArray[nodeIndex] = true;
-
-        // populate the array with points for the 8 vertices
-        // and RGB color for each added vertex
-        updateNodeInArrays(nodeIndex, startVertex, voxelScale, node->getColor());
-        
-        return 1; // updated!
+    } else {
+        //printf("....NOT (dirty [isDirty()=%s] or forceRedraw=%s_...\n",debug::valueOf(node->isDirty()),debug::valueOf(forceDraw));
     }
     return 0; // not-updated
 }
 
-void VoxelSystem::updateNodeInArrays(glBufferIndex nodeIndex, const glm::vec3& startVertex,
+void VoxelSystem::updateArraysDetails(glBufferIndex nodeIndex, const glm::vec3& startVertex,
                                      float voxelScale, const nodeColor& color) {
+
+    if (_initialized) {
+        _writeVoxelDirtyArray[nodeIndex] = true;
                                      
-    if (_useVoxelShader) {
-        if (_writeVoxelShaderData) {
-            VoxelShaderVBOData* writeVerticesAt = &_writeVoxelShaderData[nodeIndex];
-            writeVerticesAt->x = startVertex.x * TREE_SCALE;
-            writeVerticesAt->y = startVertex.y * TREE_SCALE;
-            writeVerticesAt->z = startVertex.z * TREE_SCALE;
-            writeVerticesAt->s = voxelScale * TREE_SCALE;
-            writeVerticesAt->r = color[RED_INDEX];
-            writeVerticesAt->g = color[GREEN_INDEX];
-            writeVerticesAt->b = color[BLUE_INDEX];
-        }
-    } else {
-        if (_writeVerticesArray && _writeColorsArray) {
-            int vertexPointsPerVoxel = GLOBAL_NORMALS_VERTEX_POINTS_PER_VOXEL;
-            for (int j = 0; j < vertexPointsPerVoxel; j++ ) {
-                GLfloat* writeVerticesAt = _writeVerticesArray + (nodeIndex * vertexPointsPerVoxel);
-                GLubyte* writeColorsAt   = _writeColorsArray   + (nodeIndex * vertexPointsPerVoxel);
-                *(writeVerticesAt+j) = startVertex[j % 3] + (identityVerticesGlobalNormals[j] * voxelScale);
-                *(writeColorsAt  +j) = color[j % 3];
+        if (_useVoxelShader) {
+            if (_writeVoxelShaderData) {
+                VoxelShaderVBOData* writeVerticesAt = &_writeVoxelShaderData[nodeIndex];
+                writeVerticesAt->x = startVertex.x * TREE_SCALE;
+                writeVerticesAt->y = startVertex.y * TREE_SCALE;
+                writeVerticesAt->z = startVertex.z * TREE_SCALE;
+                writeVerticesAt->s = voxelScale * TREE_SCALE;
+                writeVerticesAt->r = color[RED_INDEX];
+                writeVerticesAt->g = color[GREEN_INDEX];
+                writeVerticesAt->b = color[BLUE_INDEX];
+            }
+        } else {
+            if (_writeVerticesArray && _writeColorsArray) {
+                int vertexPointsPerVoxel = GLOBAL_NORMALS_VERTEX_POINTS_PER_VOXEL;
+                for (int j = 0; j < vertexPointsPerVoxel; j++ ) {
+                    GLfloat* writeVerticesAt = _writeVerticesArray + (nodeIndex * vertexPointsPerVoxel);
+                    GLubyte* writeColorsAt   = _writeColorsArray   + (nodeIndex * vertexPointsPerVoxel);
+                    *(writeVerticesAt+j) = startVertex[j % 3] + (identityVerticesGlobalNormals[j] * voxelScale);
+                    *(writeColorsAt  +j) = color[j % 3];
+                }
             }
         }
     }
@@ -1517,7 +1538,7 @@ public:
     unsigned long   nodesIntersect;
     unsigned long   nodesOutside;
     
-    removeOutOfViewArgs(VoxelSystem* voxelSystem) :
+    removeOutOfViewArgs(VoxelSystem* voxelSystem, bool widenViewFrustum = true) :
         thisVoxelSystem(voxelSystem),
         thisViewFrustum(*voxelSystem->getViewFrustum()),
         dontRecurseBag(),
@@ -1528,10 +1549,12 @@ public:
         nodesOutside(0)
     {
         // Widen the FOV for trimming
-        float originalFOV = thisViewFrustum.getFieldOfView();
-        float wideFOV = originalFOV + VIEW_FRUSTUM_FOV_OVERSEND;
-        thisViewFrustum.setFieldOfView(wideFOV);
-        thisViewFrustum.calculate();
+        if (widenViewFrustum) {
+            float originalFOV = thisViewFrustum.getFieldOfView();
+            float wideFOV = originalFOV + VIEW_FRUSTUM_FOV_OVERSEND;
+            thisViewFrustum.setFieldOfView(wideFOV);
+            thisViewFrustum.calculate();
+        }
     }
 };
 
@@ -1587,6 +1610,80 @@ bool VoxelSystem::removeOutOfViewOperation(VoxelNode* node, void* extraData) {
     return true; // keep going!
 }
 
+// "hide" voxels in the VBOs that are still in the tree that but not in view. 
+// We don't remove them from the tree, we don't delete them, we do remove them
+// from the VBOs and mark them as such in the tree.
+bool VoxelSystem::hideOutOfViewOperation(VoxelNode* node, void* extraData) {
+    removeOutOfViewArgs* args = (removeOutOfViewArgs*)extraData;
+
+    // If our node was previously added to the don't recurse bag, then return false to
+    // stop the further recursion. This means that the whole node and it's children are
+    // known to be in view, so don't recurse them
+    if (args->dontRecurseBag.contains(node)) {
+        args->dontRecurseBag.remove(node);
+        return false; // stop recursion
+    }
+    
+    VoxelSystem* thisVoxelSystem = args->thisVoxelSystem;
+    args->nodesScanned++;
+    // Need to operate on our child nodes, so we can remove them
+    for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
+        VoxelNode* childNode = node->getChildAtIndex(i);
+        if (childNode) {
+            ViewFrustum::location inFrustum = childNode->inFrustum(args->thisViewFrustum);
+            switch (inFrustum) {
+                case ViewFrustum::OUTSIDE: {
+                    args->nodesOutside++;
+                    if (childNode->isKnownBufferIndex()) {
+                        args->nodesRemoved++;
+                        thisVoxelSystem->forceRemoveNodeFromArraysAsPartialVBO(childNode);
+                        thisVoxelSystem->_voxelsUpdated++;
+                        thisVoxelSystem->setupNewVoxelsForDrawingSingleNode();
+                    }
+                    // how to handle recursion???? we probably want to continue
+                } break;
+                case ViewFrustum::INSIDE: {
+
+                    // if the child node is fully INSIDE the view, then there's no need to recurse it
+                    // because we know all it's children will also be in the view, so we want to 
+                    // tell the caller to NOT recurse this child
+                    args->nodesInside++;
+                    
+                    if (childNode->getShouldRender() && !childNode->isKnownBufferIndex()) {
+                        /*
+                        printf("in view, isKnownIndex()=%s isDirty()=%s shouldRender()=%s\n",
+                            debug::valueOf(childNode->isKnownBufferIndex()),
+                            debug::valueOf(childNode->isDirty()),
+                            debug::valueOf(childNode->getShouldRender()));
+                        */  
+                        childNode->setDirtyBit(); // will this make it draw?
+                    }
+                    //args->dontRecurseBag.insert(childNode);
+                } break;
+                case ViewFrustum::INTERSECT: {
+                    // if the child node INTERSECTs the view, then we don't want to remove it because
+                    // it is at least partially in view. But we DO want to recurse the children because
+                    // some of them may not be in view... nothing specifically to do, just keep iterating
+                    // the children
+                    args->nodesIntersect++;
+
+                    if (childNode->getShouldRender() && !childNode->isKnownBufferIndex()) {
+                        /*
+                        printf("intersect view, isKnownIndex()=%s isDirty()=%s shouldRender()=%s\n",
+                            debug::valueOf(childNode->isKnownBufferIndex()),
+                            debug::valueOf(childNode->isDirty()),
+                            debug::valueOf(childNode->getShouldRender()));
+                        */  
+                        childNode->setDirtyBit(); // will this make it draw?
+                    }
+
+                } break;
+            }
+        }
+    }
+    return true; // keep going!
+}
+
 
 bool VoxelSystem::isViewChanging() {
     bool result = false; // assume the best
@@ -1632,6 +1729,29 @@ void VoxelSystem::removeOutOfView() {
     }
 }
 
+void VoxelSystem::hideOutOfView() {
+    PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "hideOutOfView()",true);
+    removeOutOfViewArgs args(this, true); // widen to match server!
+    _tree->recurseTreeWithOperation(hideOutOfViewOperation,(void*)&args);
+
+    if (args.nodesRemoved) {
+        _tree->setDirtyBit();
+        setupNewVoxelsForDrawingSingleNode(DONT_BAIL_EARLY);
+    }
+    
+    
+    bool showRemoveDebugDetails = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
+    if (showRemoveDebugDetails) {
+        qDebug("hideOutOfView() scanned=%ld removed=%ld inside=%ld intersect=%ld outside=%ld\n", 
+                args.nodesScanned, args.nodesRemoved, args.nodesInside, 
+                args.nodesIntersect, args.nodesOutside
+            );
+    }
+    
+    collectStatsForTreesAndVBOs();
+}
+
+
 bool VoxelSystem::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
                                       VoxelDetail& detail, float& distance, BoxFace& face) {
     pthread_mutex_lock(&_treeLock);                                  
@@ -1751,6 +1871,14 @@ bool VoxelSystem::collectStatsForTreesAndVBOsOperation(VoxelNode* node, void* ex
     if (node->isKnownBufferIndex()) {
         args->nodesInVBO++;
         unsigned long nodeIndex = node->getBufferIndex();
+        
+        const bool extraDebugging = false; // enable for extra debugging
+        if (extraDebugging) {
+            qDebug("node In VBO... [%f,%f,%f] %f ... index=%ld, isDirty=%s, shouldRender=%s \n", 
+                    node->getCorner().x, node->getCorner().y, node->getCorner().z, node->getScale(), 
+                    nodeIndex, debug::valueOf(node->isDirty()), debug::valueOf(node->getShouldRender()));
+        }
+
         if (args->hasIndexFound[nodeIndex]) {
             args->duplicateVBOIndex++;
             qDebug("duplicateVBO found... index=%ld, isDirty=%s, shouldRender=%s \n", nodeIndex, 
@@ -1786,6 +1914,9 @@ void VoxelSystem::collectStatsForTreesAndVBOs() {
 
     collectStatsForTreesAndVBOsArgs args;
     args.expectedMax = _voxelsInWriteArrays;
+    
+    qDebug("CALCULATING Local Voxel Tree Statistics >>>>>>>>>>>>\n");
+    
     _tree->recurseTreeWithOperation(collectStatsForTreesAndVBOsOperation,&args);
 
     qDebug("Local Voxel Tree Statistics:\n total nodes %ld \n leaves %ld \n dirty %ld \n colored %ld \n shouldRender %ld \n",
@@ -1810,6 +1941,10 @@ void VoxelSystem::collectStatsForTreesAndVBOs() {
     qDebug(" minInVBO=%ld \n maxInVBO=%ld \n _voxelsInWriteArrays=%ld \n _voxelsInReadArrays=%ld \n", 
             minInVBO, maxInVBO, _voxelsInWriteArrays, _voxelsInReadArrays);
 
+    qDebug(" _freeIndexes.size()=%ld \n", 
+            _freeIndexes.size());
+
+    qDebug("DONE WITH Local Voxel Tree Statistics >>>>>>>>>>>>\n");
 }
 
 
diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h
index 456a9bc8c6..963d6280bb 100644
--- a/interface/src/VoxelSystem.h
+++ b/interface/src/VoxelSystem.h
@@ -81,6 +81,7 @@ public:
     void killLocalVoxels();
 
     virtual void removeOutOfView();
+    virtual void hideOutOfView();
     bool hasViewChanged();
     bool isViewChanging();
     
@@ -151,7 +152,7 @@ protected:
     glm::vec3 computeVoxelVertex(const glm::vec3& startVertex, float voxelScale, int index) const;
 
     
-    virtual void updateNodeInArrays(glBufferIndex nodeIndex, const glm::vec3& startVertex,
+    virtual void updateArraysDetails(glBufferIndex nodeIndex, const glm::vec3& startVertex,
                                     float voxelScale, const nodeColor& color);
     virtual void copyWrittenDataSegmentToReadArrays(glBufferIndex segmentStart, glBufferIndex segmentEnd);
     virtual void updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd);
@@ -185,9 +186,10 @@ private:
     static bool killSourceVoxelsOperation(VoxelNode* node, void* extraData);
     static bool forceRedrawEntireTreeOperation(VoxelNode* node, void* extraData);
     static bool clearAllNodesBufferIndexOperation(VoxelNode* node, void* extraData);
+    static bool hideOutOfViewOperation(VoxelNode* node, void* extraData);
 
-    int updateNodeInArraysAsFullVBO(VoxelNode* node);
-    int updateNodeInArraysAsPartialVBO(VoxelNode* node);
+    int updateNodeInArrays(VoxelNode* node, bool reuseIndex, bool forceDraw);
+    
     int forceRemoveNodeFromArraysAsPartialVBO(VoxelNode* node);
 
     void copyWrittenDataToReadArraysFullVBOs();
diff --git a/interface/src/avatar/AvatarVoxelSystem.cpp b/interface/src/avatar/AvatarVoxelSystem.cpp
index 9c94d1a06b..db02e20f85 100644
--- a/interface/src/avatar/AvatarVoxelSystem.cpp
+++ b/interface/src/avatar/AvatarVoxelSystem.cpp
@@ -151,9 +151,9 @@ void AvatarVoxelSystem::setVoxelURL(const QUrl& url) {
     connect(_voxelReply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleVoxelReplyError()));
 }
 
-void AvatarVoxelSystem::updateNodeInArrays(glBufferIndex nodeIndex, const glm::vec3& startVertex,
+void AvatarVoxelSystem::updateArraysDetails(glBufferIndex nodeIndex, const glm::vec3& startVertex,
                                            float voxelScale, const nodeColor& color) {
-    VoxelSystem::updateNodeInArrays(nodeIndex, startVertex, voxelScale, color);
+    VoxelSystem::updateArraysDetails(nodeIndex, startVertex, voxelScale, color);
     
     GLubyte* writeBoneIndicesAt = _writeBoneIndicesArray + (nodeIndex * BONE_ELEMENTS_PER_VOXEL);
     GLfloat* writeBoneWeightsAt = _writeBoneWeightsArray + (nodeIndex * BONE_ELEMENTS_PER_VOXEL);
diff --git a/interface/src/avatar/AvatarVoxelSystem.h b/interface/src/avatar/AvatarVoxelSystem.h
index 43c76fb5c2..2560af11a5 100644
--- a/interface/src/avatar/AvatarVoxelSystem.h
+++ b/interface/src/avatar/AvatarVoxelSystem.h
@@ -42,7 +42,7 @@ public slots:
     
 protected:
     
-    virtual void updateNodeInArrays(glBufferIndex nodeIndex, const glm::vec3& startVertex,
+    virtual void updateArraysDetails(glBufferIndex nodeIndex, const glm::vec3& startVertex,
                                     float voxelScale, const nodeColor& color);
     virtual void copyWrittenDataSegmentToReadArrays(glBufferIndex segmentStart, glBufferIndex segmentEnd);
     virtual void updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd);
diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp
index a5ad61208c..f40d604790 100644
--- a/libraries/voxels/src/VoxelTree.cpp
+++ b/libraries/voxels/src/VoxelTree.cpp
@@ -221,6 +221,14 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode, unsigned char* nodeData,
                 nodeWasDirty = childNodeAt->isDirty();
                 childNodeAt->setColor(newColor);
                 childNodeAt->setSourceID(args.sourceID);
+                
+                // if we had a local version of the node already, it's possible that we have it in the VBO but
+                // with the same color data, so this won't count as a change. To address this we check the following
+                if (!childNodeAt->isDirty() && !childNodeAt->isKnownBufferIndex() && childNodeAt->getShouldRender()) {
+                    printf("got network packet with node that is in local tree, force dirty...\n");
+                    childNodeAt->setDirtyBit(); // force dirty!
+                }
+                
                 nodeIsDirty = childNodeAt->isDirty();
             }
             if (nodeIsDirty) {