diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp
index f0d23cac62..ff876596b0 100644
--- a/assignment-client/src/AssignmentClient.cpp
+++ b/assignment-client/src/AssignmentClient.cpp
@@ -22,6 +22,7 @@
 #include <PacketHeaders.h>
 #include <SharedUtil.h>
 
+
 #include "AssignmentFactory.h"
 #include "AssignmentThread.h"
 
@@ -30,11 +31,12 @@
 const QString ASSIGNMENT_CLIENT_TARGET_NAME = "assignment-client";
 const long long ASSIGNMENT_REQUEST_INTERVAL_MSECS = 1 * 1000;
 
+SharedAssignmentPointer AssignmentClient::_currentAssignment;
+
 int hifiSockAddrMeta = qRegisterMetaType<HifiSockAddr>("HifiSockAddr");
 
 AssignmentClient::AssignmentClient(int &argc, char **argv) :
     QCoreApplication(argc, argv),
-    _currentAssignment(),
     _assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME)
 {
     DTLSClientSession::globalInit();
diff --git a/assignment-client/src/AssignmentClient.h b/assignment-client/src/AssignmentClient.h
index 89fe74f044..2df9f4ca40 100644
--- a/assignment-client/src/AssignmentClient.h
+++ b/assignment-client/src/AssignmentClient.h
@@ -20,15 +20,17 @@ class AssignmentClient : public QCoreApplication {
     Q_OBJECT
 public:
     AssignmentClient(int &argc, char **argv);
+    static const SharedAssignmentPointer& getCurrentAssignment() { return _currentAssignment; }
     ~AssignmentClient();
 private slots:
     void sendAssignmentRequest();
     void readPendingDatagrams();
     void assignmentCompleted();
     void handleAuthenticationRequest();
+
 private:
     Assignment _requestAssignment;
-    SharedAssignmentPointer _currentAssignment;
+    static SharedAssignmentPointer _currentAssignment;
     QString _assignmentServerHostname;
 };
 
diff --git a/assignment-client/src/octree/OctreeQueryNode.cpp b/assignment-client/src/octree/OctreeQueryNode.cpp
index 00526cc967..10d30ad1ae 100644
--- a/assignment-client/src/octree/OctreeQueryNode.cpp
+++ b/assignment-client/src/octree/OctreeQueryNode.cpp
@@ -47,41 +47,54 @@ OctreeQueryNode::OctreeQueryNode() :
 
 OctreeQueryNode::~OctreeQueryNode() {
     _isShuttingDown = true;
-    const bool extraDebugging = false;
-    if (extraDebugging) {
-        qDebug() << "OctreeQueryNode::~OctreeQueryNode()";
-    }
     if (_octreeSendThread) {
-        if (extraDebugging) {
-            qDebug() << "OctreeQueryNode::~OctreeQueryNode()... calling _octreeSendThread->terminate()";
-        }
-        _octreeSendThread->terminate();
-        if (extraDebugging) {
-            qDebug() << "OctreeQueryNode::~OctreeQueryNode()... calling delete _octreeSendThread";
-        }
-        delete _octreeSendThread;
+        forceNodeShutdown();
     }
-
+    
     delete[] _octreePacket;
     delete[] _lastOctreePacket;
-    if (extraDebugging) {
-        qDebug() << "OctreeQueryNode::~OctreeQueryNode()... DONE...";
-    }
 }
 
-
-void OctreeQueryNode::deleteLater() {
+void OctreeQueryNode::nodeKilled() {
     _isShuttingDown = true;
+    nodeBag.unhookNotifications(); // if our node is shutting down, then we no longer need octree element notifications
     if (_octreeSendThread) {
+        // just tell our thread we want to shutdown, this is asynchronous, and fast, we don't need or want it to block
+        // while the thread actually shuts down
         _octreeSendThread->setIsShuttingDown();
     }
-    OctreeQuery::deleteLater();
 }
 
+void OctreeQueryNode::forceNodeShutdown() {
+    _isShuttingDown = true;
+    nodeBag.unhookNotifications(); // if our node is shutting down, then we no longer need octree element notifications
+    if (_octreeSendThread) {
+        // we really need to force our thread to shutdown, this is synchronous, we will block while the thread actually 
+        // shuts down because we really need it to shutdown, and it's ok if we wait for it to complete
+        OctreeSendThread* sendThread = _octreeSendThread;
+        _octreeSendThread = NULL;
+        sendThread->setIsShuttingDown();
+        sendThread->terminate();
+        delete sendThread;
+    }
+}
 
-void OctreeQueryNode::initializeOctreeSendThread(OctreeServer* octreeServer, SharedNodePointer node) {
-    // Create octree sending thread...
-    _octreeSendThread = new OctreeSendThread(octreeServer, node);
+void OctreeQueryNode::sendThreadFinished() {
+    // We've been notified by our thread that it is shutting down. So we can clean up our reference to it, and
+    // delete the actual thread object. Cleaning up our thread will correctly unroll all refereces to shared
+    // pointers to our node as well as the octree server assignment
+    if (_octreeSendThread) {
+        OctreeSendThread* sendThread = _octreeSendThread;
+        _octreeSendThread = NULL;
+        delete sendThread;
+    }
+}
+
+void OctreeQueryNode::initializeOctreeSendThread(const SharedAssignmentPointer& myAssignment, const SharedNodePointer& node) {
+    _octreeSendThread = new OctreeSendThread(myAssignment, node);
+    
+    // we want to be notified when the thread finishes
+    connect(_octreeSendThread, &GenericThread::finished, this, &OctreeQueryNode::sendThreadFinished);
     _octreeSendThread->initialize(true);
 }
 
diff --git a/assignment-client/src/octree/OctreeQueryNode.h b/assignment-client/src/octree/OctreeQueryNode.h
index aa445db8a6..7b42208f16 100644
--- a/assignment-client/src/octree/OctreeQueryNode.h
+++ b/assignment-client/src/octree/OctreeQueryNode.h
@@ -13,25 +13,25 @@
 #define hifi_OctreeQueryNode_h
 
 #include <iostream>
-#include <NodeData.h>
-#include <OctreePacketData.h>
-#include <OctreeQuery.h>
+
 
 #include <CoverageMap.h>
+#include <NodeData.h>
 #include <OctreeConstants.h>
 #include <OctreeElementBag.h>
+#include <OctreePacketData.h>
+#include <OctreeQuery.h>
 #include <OctreeSceneStats.h>
+#include <ThreadedAssignment.h> // for SharedAssignmentPointer
 
 class OctreeSendThread;
-class OctreeServer;
 
 class OctreeQueryNode : public OctreeQuery {
     Q_OBJECT
 public:
     OctreeQueryNode();
     virtual ~OctreeQueryNode();
-    virtual void deleteLater();
-    
+
     void init(); // called after creation to set up some virtual items
     virtual PacketType getMyPacketType() const = 0;
 
@@ -86,7 +86,7 @@ public:
     
     OctreeSceneStats stats;
     
-    void initializeOctreeSendThread(OctreeServer* octreeServer, SharedNodePointer node);
+    void initializeOctreeSendThread(const SharedAssignmentPointer& myAssignment, const SharedNodePointer& node);
     bool isOctreeSendThreadInitalized() { return _octreeSendThread; }
     
     void dumpOutOfView();
@@ -96,8 +96,13 @@ public:
     unsigned int getlastOctreePacketLength() const { return _lastOctreePacketLength; }
     int getDuplicatePacketCount() const { return _duplicatePacketCount; }
     
+    void nodeKilled();
+    void forceNodeShutdown();
     bool isShuttingDown() const { return _isShuttingDown; }
     
+private slots:
+    void sendThreadFinished();
+    
 private:
     OctreeQueryNode(const OctreeQueryNode &);
     OctreeQueryNode& operator= (const OctreeQueryNode&);
diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp
index 0a53160b8f..d8a9f3d1ea 100644
--- a/assignment-client/src/octree/OctreeSendThread.cpp
+++ b/assignment-client/src/octree/OctreeSendThread.cpp
@@ -23,12 +23,13 @@
 quint64 startSceneSleepTime = 0;
 quint64 endSceneSleepTime = 0;
 
-OctreeSendThread::OctreeSendThread(OctreeServer* myServer, SharedNodePointer node) :
-    _myServer(myServer),
+OctreeSendThread::OctreeSendThread(const SharedAssignmentPointer& myAssignment, const SharedNodePointer& node) :
+    _myAssignment(myAssignment),
+    _myServer(static_cast<OctreeServer*>(myAssignment.data())),
+    _node(node),
     _nodeUUID(node->getUUID()),
     _packetData(),
     _nodeMissingCount(0),
-    _processLock(),
     _isShuttingDown(false)
 {
     QString safeServerName("Octree");
@@ -41,22 +42,24 @@ OctreeSendThread::OctreeSendThread(OctreeServer* myServer, SharedNodePointer nod
     OctreeServer::clientConnected();
 }
 
-OctreeSendThread::~OctreeSendThread() { 
+OctreeSendThread::~OctreeSendThread() {
     QString safeServerName("Octree");
     if (_myServer) {
         safeServerName = _myServer->getMyServerName();
     }
+    
     qDebug() << qPrintable(safeServerName)  << "server [" << _myServer << "]: client disconnected "
                                             "- ending sending thread [" << this << "]";
+
     OctreeServer::clientDisconnected();
+    OctreeServer::stopTrackingThread(this);
+
+    _node.clear();
+    _myAssignment.clear();
 }
 
 void OctreeSendThread::setIsShuttingDown() {
     _isShuttingDown = true;
-    OctreeServer::stopTrackingThread(this);
-    
-    // this will cause us to wait till the process loop is complete, we do this after we change _isShuttingDown
-    QMutexLocker locker(&_processLock); 
 }
 
 
@@ -65,47 +68,29 @@ bool OctreeSendThread::process() {
         return false; // exit early if we're shutting down
     }
 
+    // check that our server and assignment is still valid
+    if (!_myServer || !_myAssignment) {
+        return false; // exit early if it's not, it means the server is shutting down
+    }
+
     OctreeServer::didProcess(this);
 
-    float lockWaitElapsedUsec = OctreeServer::SKIP_TIME;
-    quint64 lockWaitStart = usecTimestampNow();
-    _processLock.lock();
-    quint64 lockWaitEnd = usecTimestampNow();
-    lockWaitElapsedUsec = (float)(lockWaitEnd - lockWaitStart);
-    OctreeServer::trackProcessWaitTime(lockWaitElapsedUsec);
-    
     quint64  start = usecTimestampNow();
 
     // don't do any send processing until the initial load of the octree is complete...
     if (_myServer->isInitialLoadComplete()) {
-        SharedNodePointer node = NodeList::getInstance()->nodeWithUUID(_nodeUUID, false);
-        if (node) {
+        if (_node) {
             _nodeMissingCount = 0;
-            OctreeQueryNode* nodeData = static_cast<OctreeQueryNode*>(node->getLinkedData());
+            OctreeQueryNode* nodeData = static_cast<OctreeQueryNode*>(_node->getLinkedData());
 
             // Sometimes the node data has not yet been linked, in which case we can't really do anything
             if (nodeData && !nodeData->isShuttingDown()) {
                 bool viewFrustumChanged = nodeData->updateCurrentViewFrustum();
-                packetDistributor(node, nodeData, viewFrustumChanged);
-            }
-        } else {
-            _nodeMissingCount++;
-            const int MANY_FAILED_LOCKS = 1;
-            if (_nodeMissingCount >= MANY_FAILED_LOCKS) {
-
-                QString safeServerName("Octree");
-                if (_myServer) {
-                    safeServerName = _myServer->getMyServerName();
-                }
-                
-                qDebug() << qPrintable(safeServerName)  << "server: sending thread [" << this << "]"
-                        << "failed to get nodeWithUUID() " << _nodeUUID <<". Failed:" << _nodeMissingCount << "times";
+                packetDistributor(nodeData, viewFrustumChanged);
             }
         }
     }
 
-    _processLock.unlock();
-    
     if (_isShuttingDown) {
         return false; // exit early if we're shutting down
     }
@@ -135,8 +120,7 @@ quint64 OctreeSendThread::_totalBytes = 0;
 quint64 OctreeSendThread::_totalWastedBytes = 0;
 quint64 OctreeSendThread::_totalPackets = 0;
 
-int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, 
-                        OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent) {
+int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent) {
 
     OctreeServer::didHandlePacketSend(this);
                  
@@ -196,12 +180,12 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node,
 
             // actually send it
             OctreeServer::didCallWriteDatagram(this);
-            NodeList::getInstance()->writeDatagram((char*) statsMessage, statsMessageLength, SharedNodePointer(node));
+            NodeList::getInstance()->writeDatagram((char*) statsMessage, statsMessageLength, _node);
             packetSent = true;
         } else {
             // not enough room in the packet, send two packets
             OctreeServer::didCallWriteDatagram(this);
-            NodeList::getInstance()->writeDatagram((char*) statsMessage, statsMessageLength, SharedNodePointer(node));
+            NodeList::getInstance()->writeDatagram((char*) statsMessage, statsMessageLength, _node);
 
             // since a stats message is only included on end of scene, don't consider any of these bytes "wasted", since
             // there was nothing else to send.
@@ -220,8 +204,7 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node,
             packetsSent++;
 
             OctreeServer::didCallWriteDatagram(this);
-            NodeList::getInstance()->writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(),
-                                                   SharedNodePointer(node));
+            NodeList::getInstance()->writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(), _node);
 
             packetSent = true;
 
@@ -241,8 +224,7 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node,
         if (nodeData->isPacketWaiting() && !nodeData->isShuttingDown()) {
             // just send the voxel packet
             OctreeServer::didCallWriteDatagram(this);
-            NodeList::getInstance()->writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(),
-                                                   SharedNodePointer(node));
+            NodeList::getInstance()->writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(), _node);
             packetSent = true;
 
             int thisWastedBytes = MAX_PACKET_SIZE - nodeData->getPacketLength();
@@ -269,7 +251,8 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node,
 }
 
 /// Version of voxel distributor that sends the deepest LOD level at once
-int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQueryNode* nodeData, bool viewFrustumChanged) {
+int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrustumChanged) {
+        
     OctreeServer::didPacketDistributor(this);
 
     // if shutting down, exit early
@@ -299,7 +282,7 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue
     // then let's just send that waiting packet.
     if (!nodeData->getCurrentPacketFormatMatches()) {
         if (nodeData->isPacketWaiting()) {
-            packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
+            packetsSentThisInterval += handlePacketSend(nodeData, trueBytesSent, truePacketsSent);
         } else {
             nodeData->resetOctreePacket();
         }
@@ -340,7 +323,7 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue
         //unsigned long encodeTime = nodeData->stats.getTotalEncodeTime();
         //unsigned long elapsedTime = nodeData->stats.getElapsedTime();
 
-        int packetsJustSent = handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
+        int packetsJustSent = handlePacketSend(nodeData, trueBytesSent, truePacketsSent);
         packetsSentThisInterval += packetsJustSent;
 
         // If we're starting a full scene, then definitely we want to empty the nodeBag
@@ -491,7 +474,7 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue
 
 
                     if (writtenSize > nodeData->getAvailable()) {
-                        packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
+                        packetsSentThisInterval += handlePacketSend(nodeData, trueBytesSent, truePacketsSent);
                     }
 
                     nodeData->writeToPacket(_packetData.getFinalizedData(), _packetData.getFinalizedSize());
@@ -513,7 +496,7 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue
                 int targetSize = MAX_OCTREE_PACKET_DATA_SIZE;
                 if (sendNow) {
                     quint64 packetSendingStart = usecTimestampNow();
-                    packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
+                    packetsSentThisInterval += handlePacketSend(nodeData, trueBytesSent, truePacketsSent);
                     quint64 packetSendingEnd = usecTimestampNow();
                     packetSendingElapsedUsec = (float)(packetSendingEnd - packetSendingStart);
 
@@ -546,8 +529,8 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue
         // Here's where we can/should allow the server to send other data...
         // send the environment packet
         // TODO: should we turn this into a while loop to better handle sending multiple special packets
-        if (_myServer->hasSpecialPacketToSend(node) && !nodeData->isShuttingDown()) {
-            trueBytesSent += _myServer->sendSpecialPacket(node);
+        if (_myServer->hasSpecialPacketToSend(_node) && !nodeData->isShuttingDown()) {
+            trueBytesSent += _myServer->sendSpecialPacket(_node);
             truePacketsSent++;
             packetsSentThisInterval++;
         }
diff --git a/assignment-client/src/octree/OctreeSendThread.h b/assignment-client/src/octree/OctreeSendThread.h
index 95205af32f..d8eed27802 100644
--- a/assignment-client/src/octree/OctreeSendThread.h
+++ b/assignment-client/src/octree/OctreeSendThread.h
@@ -17,15 +17,16 @@
 #include <GenericThread.h>
 #include <NetworkPacket.h>
 #include <OctreeElementBag.h>
-#include "OctreeQueryNode.h"
-#include "OctreeServer.h"
 
+#include "OctreeQueryNode.h"
+
+class OctreeServer;
 
 /// Threaded processor for sending voxel packets to a single client
 class OctreeSendThread : public GenericThread {
     Q_OBJECT
 public:
-    OctreeSendThread(OctreeServer* myServer, SharedNodePointer node);
+    OctreeSendThread(const SharedAssignmentPointer& myAssignment, const SharedNodePointer& node);
     virtual ~OctreeSendThread();
     
     void setIsShuttingDown();
@@ -42,16 +43,17 @@ protected:
     virtual bool process();
 
 private:
+    SharedAssignmentPointer _myAssignment;
     OctreeServer* _myServer;
+    SharedNodePointer _node;
     QUuid _nodeUUID;
 
-    int handlePacketSend(const SharedNodePointer& node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent);
-    int packetDistributor(const SharedNodePointer& node, OctreeQueryNode* nodeData, bool viewFrustumChanged);
+    int handlePacketSend(OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent);
+    int packetDistributor(OctreeQueryNode* nodeData, bool viewFrustumChanged);
 
     OctreePacketData _packetData;
     
     int _nodeMissingCount;
-    QMutex _processLock; // don't allow us to have our nodeData, or our thread to be deleted while we're processing
     bool _isShuttingDown;
 };
 
diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp
index 2e8a354c6a..bd04dd85d7 100644
--- a/assignment-client/src/octree/OctreeServer.cpp
+++ b/assignment-client/src/octree/OctreeServer.cpp
@@ -19,6 +19,8 @@
 #include <Logging.h>
 #include <UUID.h>
 
+#include "../AssignmentClient.h"
+
 #include "OctreeServer.h"
 #include "OctreeServerConsts.h"
 
@@ -206,7 +208,7 @@ void OctreeServer::trackProcessWaitTime(float time) {
 }
 
 void OctreeServer::attachQueryNodeToNode(Node* newNode) {
-    if (!newNode->getLinkedData()) {
+    if (!newNode->getLinkedData() && _instance) {
         OctreeQueryNode* newQueryNodeData = _instance->createOctreeQueryNode();
         newQueryNodeData->init();
         newNode->setLinkedData(newQueryNodeData);
@@ -234,7 +236,13 @@ OctreeServer::OctreeServer(const QByteArray& packet) :
     _started(time(0)),
     _startedUSecs(usecTimestampNow())
 {
+    if (_instance) {
+        qDebug() << "Octree Server starting... while old instance still running _instance=["<<_instance<<"] this=[" << this << "]";
+    }
+
+    qDebug() << "Octree Server starting... setting _instance to=[" << this << "]";
     _instance = this;
+
     _averageLoopTime.updateAverage(0);
     qDebug() << "Octree server starting... [" << this << "]";
 }
@@ -265,6 +273,16 @@ OctreeServer::~OctreeServer() {
 
     delete _jurisdiction;
     _jurisdiction = NULL;
+    
+    // cleanup our tree here...
+    qDebug() << qPrintable(_safeServerName) << "server START cleaning up octree... [" << this << "]";
+    delete _tree;
+    _tree = NULL;
+    qDebug() << qPrintable(_safeServerName) << "server DONE cleaning up octree... [" << this << "]";
+    
+    if (_instance == this) {
+        _instance = NULL; // we are gone
+    }
     qDebug() << qPrintable(_safeServerName) << "server DONE shutting down... [" << this << "]";
 }
 
@@ -812,33 +830,22 @@ void OctreeServer::readPendingDatagrams() {
     while (readAvailableDatagram(receivedPacket, senderSockAddr)) {
         if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
             PacketType packetType = packetTypeForPacket(receivedPacket);
-            
             SharedNodePointer matchingNode = nodeList->sendingNodeForPacket(receivedPacket);
-            
             if (packetType == getMyQueryMessageType()) {
-                bool debug = false;
-                if (debug) {
-                    if (matchingNode) {
-                        qDebug() << "Got PacketTypeVoxelQuery at" << usecTimestampNow() << "node:" << *matchingNode;
-                    } else {
-                        qDebug() << "Got PacketTypeVoxelQuery at" << usecTimestampNow() << "node: ??????";
-                    }
-                }
-                
+            
                 // If we got a PacketType_VOXEL_QUERY, then we're talking to an NodeType_t_AVATAR, and we
                 // need to make sure we have it in our nodeList.
                 if (matchingNode) {
-                    if (debug) {
-                        qDebug() << "calling updateNodeWithDataFromPacket()... node:" << *matchingNode;
-                    }
                     nodeList->updateNodeWithDataFromPacket(matchingNode, receivedPacket);
-                    
                     OctreeQueryNode* nodeData = (OctreeQueryNode*) matchingNode->getLinkedData();
                     if (nodeData && !nodeData->isOctreeSendThreadInitalized()) {
-                        if (debug) {
-                            qDebug() << "calling initializeOctreeSendThread()... node:" << *matchingNode;
-                        }
-                        nodeData->initializeOctreeSendThread(this, matchingNode);
+                    
+                        // NOTE: this is an important aspect of the proper ref counting. The send threads/node data need to 
+                        // know that the OctreeServer/Assignment will not get deleted on it while it's still active. The 
+                        // solution is to get the shared pointer for the current assignment. We need to make sure this is the 
+                        // same SharedAssignmentPointer that was ref counted by the assignment client.                    
+                        SharedAssignmentPointer sharedAssignment = AssignmentClient::getCurrentAssignment();
+                        nodeData->initializeOctreeSendThread(sharedAssignment, matchingNode);
                     }
                 }
             } else if (packetType == PacketTypeJurisdictionRequest) {
@@ -1042,23 +1049,46 @@ void OctreeServer::nodeAdded(SharedNodePointer node) {
 }
 
 void OctreeServer::nodeKilled(SharedNodePointer node) {
+    quint64 start  = usecTimestampNow();
+
     qDebug() << qPrintable(_safeServerName) << "server killed node:" << *node;
     OctreeQueryNode* nodeData = static_cast<OctreeQueryNode*>(node->getLinkedData());
     if (nodeData) {
-        qDebug() << qPrintable(_safeServerName) << "server resetting Linked Data for node:" << *node;
-        node->setLinkedData(NULL); // set this first in case another thread comes through and tryes to acces this
-        qDebug() << qPrintable(_safeServerName) << "server deleting Linked Data for node:" << *node;
-        nodeData->deleteLater();
+        nodeData->nodeKilled(); // tell our node data and sending threads that we'd like to shut down
     } else {
         qDebug() << qPrintable(_safeServerName) << "server node missing linked data node:" << *node;
     }
+
+    quint64 end  = usecTimestampNow();
+    quint64 usecsElapsed = (end - start);
+    if (usecsElapsed > 1000) {
+        qDebug() << qPrintable(_safeServerName) << "server nodeKilled() took: " << usecsElapsed << " usecs for node:" << *node;
+    }
 }
 
+void OctreeServer::forceNodeShutdown(SharedNodePointer node) {
+    quint64 start  = usecTimestampNow();
+
+    qDebug() << qPrintable(_safeServerName) << "server killed node:" << *node;
+    OctreeQueryNode* nodeData = static_cast<OctreeQueryNode*>(node->getLinkedData());
+    if (nodeData) {
+        nodeData->forceNodeShutdown(); // tell our node data and sending threads that we'd like to shut down
+    } else {
+        qDebug() << qPrintable(_safeServerName) << "server node missing linked data node:" << *node;
+    }
+
+    quint64 end  = usecTimestampNow();
+    quint64 usecsElapsed = (end - start);
+    qDebug() << qPrintable(_safeServerName) << "server forceNodeShutdown() took: "  
+                << usecsElapsed << " usecs for node:" << *node;
+}
+
+
 void OctreeServer::aboutToFinish() {
     qDebug() << qPrintable(_safeServerName) << "server STARTING about to finish...";
     foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
         qDebug() << qPrintable(_safeServerName) << "server about to finish while node still connected node:" << *node;
-        nodeKilled(node);
+        forceNodeShutdown(node);
     }
     qDebug() << qPrintable(_safeServerName) << "server ENDING about to finish...";
 }
diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h
index d02764bc59..d7139b5c3d 100644
--- a/assignment-client/src/octree/OctreeServer.h
+++ b/assignment-client/src/octree/OctreeServer.h
@@ -117,6 +117,7 @@ public:
     bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url);
 
     virtual void aboutToFinish();
+    void forceNodeShutdown(SharedNodePointer node);
     
 public slots:
     /// runs the voxel server assignment
diff --git a/examples/audioReflectorTools.js b/examples/audioReflectorTools.js
index 76869de578..f299407e54 100644
--- a/examples/audioReflectorTools.js
+++ b/examples/audioReflectorTools.js
@@ -7,6 +7,8 @@
 //
 //  Tools for manipulating the attributes of the AudioReflector behavior
 //
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
 
diff --git a/examples/clap.js b/examples/clap.js
index a0efcfab7b..9da36ba094 100644
--- a/examples/clap.js
+++ b/examples/clap.js
@@ -50,7 +50,7 @@ function maybePlaySound(deltaTime) {
 		const CLAP_DISTANCE = 0.2; 
 
     	if (!clapping[palm] && (distanceBetween < CLAP_DISTANCE) && (speed > CLAP_SPEED)) {
-    		var options = new AudioInjectionOptions();

+    		var options = new AudioInjectionOptions();
 			options.position = palm1Position;
 			options.volume = speed / 2.0;
 			if (options.volume > 1.0) options.volume = 1.0;
diff --git a/examples/drumStick.js b/examples/drumStick.js
index e9ac54f814..188661b000 100644
--- a/examples/drumStick.js
+++ b/examples/drumStick.js
@@ -61,7 +61,7 @@ function checkSticks(deltaTime) {
 			//   Waiting for change in velocity direction or slowing to trigger drum sound
 			if ((palmVelocity.y > 0.0) || (speed < STOP_SPEED)) {
 				state[palm] = 0;
-				var options = new AudioInjectionOptions();

+				var options = new AudioInjectionOptions();
 				options.position = Controller.getSpatialControlPosition(palm * 2 + 1);
 				if (strokeSpeed[palm] > 1.0) { strokeSpeed[palm] = 1.0; }
 				options.volume = strokeSpeed[palm];
diff --git a/examples/playSound.js b/examples/playSound.js
index fb589bc9e3..317581ba36 100644
--- a/examples/playSound.js
+++ b/examples/playSound.js
@@ -9,12 +9,12 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 
 //  First, load the clap sound from a URL 
-var clap = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/bushtit_1.raw");	
+var clap = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/bushtit_1.raw");
 
 function maybePlaySound(deltaTime) {
 	if (Math.random() < 0.01) {
 		//  Set the location and other info for the sound to play
-		var options = new AudioInjectionOptions();

+		var options = new AudioInjectionOptions();
 		var palmPosition = Controller.getSpatialControlPosition(0);
 		options.position = palmPosition;
 		options.volume = 0.5;
diff --git a/examples/spaceInvadersExample.js b/examples/spaceInvadersExample.js
index 5b25fa1236..5d62102d71 100644
--- a/examples/spaceInvadersExample.js
+++ b/examples/spaceInvadersExample.js
@@ -217,7 +217,7 @@ function update(deltaTime) {
 
         if (invaderStepOfCycle % stepsPerSound == 0) {
             // play the move sound
-            var options = new AudioInjectionOptions();

+            var options = new AudioInjectionOptions();
             if (soundInMyHead) {
                 options.position = { x: MyAvatar.position.x + 0.0, 
                                      y: MyAvatar.position.y + 0.1, 
@@ -329,7 +329,7 @@ function fireMissile() {
                             lifetime: 5
                         });
 
-        var options = new AudioInjectionOptions();

+        var options = new AudioInjectionOptions();
         if (soundInMyHead) {
             options.position = { x: MyAvatar.position.x + 0.0, 
                                  y: MyAvatar.position.y + 0.1, 
@@ -379,7 +379,7 @@ function deleteIfInvader(possibleInvaderParticle) {
                     Particles.deleteParticle(myMissile);
 
                     // play the hit sound
-                    var options = new AudioInjectionOptions();

+                    var options = new AudioInjectionOptions();
                     if (soundInMyHead) {
                         options.position = { x: MyAvatar.position.x + 0.0, 
                                              y: MyAvatar.position.y + 0.1, 
@@ -417,4 +417,3 @@ initializeInvaders();
 
 // shut down the game after 1 minute
 var gameTimer = Script.setTimeout(endGame, itemLifetimes * 1000);
-
diff --git a/examples/testingVoxelViewerRestart.js b/examples/testingVoxelViewerRestart.js
new file mode 100644
index 0000000000..c4ab67e8ba
--- /dev/null
+++ b/examples/testingVoxelViewerRestart.js
@@ -0,0 +1,94 @@
+//
+//  testingVoxelSeeingRestart.js
+//  hifi
+//
+//  Created by Brad Hefta-Gaub on 2/26/14
+//  Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+var count = 0;
+var yawDirection = -1;
+var yaw = 45;
+var yawMax = 70;
+var yawMin = 20;
+var vantagePoint = {x: 5000, y: 500, z: 5000};
+
+var isLocal = false;
+
+// set up our VoxelViewer with a position and orientation
+var orientation = Quat.fromPitchYawRollDegrees(0, yaw, 0);
+
+function getRandomInt(min, max) {
+    return Math.floor(Math.random() * (max - min + 1)) + min;
+}
+
+function init() {
+    if (isLocal) {
+        MyAvatar.position = vantagePoint;
+        MyAvatar.orientation = orientation;
+    } else {
+        VoxelViewer.setPosition(vantagePoint);
+        VoxelViewer.setOrientation(orientation);
+        VoxelViewer.queryOctree();
+        Agent.isAvatar = true;
+    }
+}
+
+function keepLooking(deltaTime) {
+    //print("count =" + count);
+
+    if (count == 0) {
+        init();
+    }
+    count++;
+    if (count % getRandomInt(5, 15) == 0) {
+        yaw += yawDirection;
+        orientation = Quat.fromPitchYawRollDegrees(0, yaw, 0);
+        if (yaw > yawMax || yaw < yawMin) {
+            yawDirection = yawDirection * -1;
+        }
+
+        //if (count % 10000 == 0) {
+        //    print("calling VoxelViewer.queryOctree()... count=" + count + " yaw=" + yaw);
+        //}
+
+        if (isLocal) {
+            MyAvatar.orientation = orientation;
+        } else {
+            VoxelViewer.setOrientation(orientation);
+            VoxelViewer.queryOctree();
+            
+            //if (count % 10000 == 0) {
+            //    print("VoxelViewer.getOctreeElementsCount()=" + VoxelViewer.getOctreeElementsCount());
+            //}
+        }
+    }
+    
+    // approximately every second, consider stopping
+    if (count % 60 == 0) {
+        print("considering stop.... elementCount:" + VoxelViewer.getOctreeElementsCount());
+        var stopProbability = 0.05; // 5% chance of stopping
+        if (Math.random() < stopProbability) {
+            print("stopping.... elementCount:" + VoxelViewer.getOctreeElementsCount());
+            Script.stop();
+        } 
+    }
+}
+
+function scriptEnding() {
+    print("SCRIPT ENDNG!!!\n");
+}
+
+// register the call back so it fires before each data send
+Script.update.connect(keepLooking);
+
+// register our scriptEnding callback
+Script.scriptEnding.connect(scriptEnding);
+
+
+// test for local...
+Menu.isOptionChecked("Voxels");
+isLocal = true; // will only get here on local client
diff --git a/examples/toyball.js b/examples/toyball.js
index de68e97357..d312c1bc94 100644
--- a/examples/toyball.js
+++ b/examples/toyball.js
@@ -111,7 +111,7 @@ function checkControllerSide(whichSide) {
                                 velocity : { x: 0, y: 0, z: 0}, inHand: true };
             Particles.editParticle(closestParticle, properties);
             
-    		var options = new AudioInjectionOptions();

+    		var options = new AudioInjectionOptions();
 			options.position = ballPosition;
 			options.volume = 1.0;
 			Audio.playSound(catchSound, options);
@@ -152,7 +152,7 @@ function checkControllerSide(whichSide) {
         }
 
         // Play a new ball sound
-        var options = new AudioInjectionOptions();

+        var options = new AudioInjectionOptions();
         options.position = ballPosition;
         options.volume = 1.0;
         Audio.playSound(catchSound, options);
@@ -201,7 +201,7 @@ function checkControllerSide(whichSide) {
                 rightHandParticle = false;
             }
 
-    		var options = new AudioInjectionOptions();

+    		var options = new AudioInjectionOptions();
 			options.position = ballPosition;
 			options.volume = 1.0;
 			Audio.playSound(throwSound, options);
diff --git a/examples/voxelDrumming.js b/examples/voxelDrumming.js
index 7f3495dea7..1b320ed755 100644
--- a/examples/voxelDrumming.js
+++ b/examples/voxelDrumming.js
@@ -64,7 +64,7 @@ collisionBubble[1] = Overlays.addOverlay("sphere",
                                 visible: false
                             });
 
-var audioOptions = new AudioInjectionOptions();

+var audioOptions = new AudioInjectionOptions();
 audioOptions.position = { x: MyAvatar.position.x, y: MyAvatar.position.y + 1, z: MyAvatar.position.z }; 
 audioOptions.volume = 1;
 
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 55933ce069..bfbc88a666 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -2207,7 +2207,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
             int packetLength = endOfQueryPacket - queryPacket;
 
             // make sure we still have an active socket
-            nodeList->writeDatagram(reinterpret_cast<const char*>(queryPacket), packetLength, node);
+            nodeList->writeUnverifiedDatagram(reinterpret_cast<const char*>(queryPacket), packetLength, node);
 
             // Feed number of bytes to corresponding channel of the bandwidth meter
             _bandwidthMeter.outputStream(BandwidthMeter::VOXELS).updateValue(packetLength);
@@ -2644,6 +2644,8 @@ void Application::displayOverlay() {
                          audioMeterY,
                          Menu::getInstance()->isOptionChecked(MenuOption::Mirror));
 
+    _audio.renderScope(_glWidget->width(), _glWidget->height());
+
     glBegin(GL_QUADS);
     if (isClipping) {
         glColor3f(1, 0, 0);
diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp
index ec59b10427..ace7ae5b62 100644
--- a/interface/src/Audio.cpp
+++ b/interface/src/Audio.cpp
@@ -92,7 +92,14 @@ Audio::Audio(int16_t initialJitterBufferSamples, QObject* parent) :
     _processSpatialAudio(false),
     _spatialAudioStart(0),
     _spatialAudioFinish(0),
-    _spatialAudioRingBuffer(NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL, true) // random access mode
+    _spatialAudioRingBuffer(NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL, true), // random access mode
+    _scopeEnabled(false),
+    _scopeEnabledPause(false),
+    _scopeInputOffset(0),
+    _scopeOutputOffset(0),
+    _scopeInput(SAMPLES_PER_SCOPE_WIDTH * sizeof(int16_t), 0),
+    _scopeOutputLeft(SAMPLES_PER_SCOPE_WIDTH * sizeof(int16_t), 0),
+    _scopeOutputRight(SAMPLES_PER_SCOPE_WIDTH * sizeof(int16_t), 0)
 {
     // clear the array of locally injected samples
     memset(_localProceduralSamples, 0, NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL);
@@ -575,6 +582,14 @@ void Audio::handleAudioInput() {
             processProceduralAudio(monoAudioSamples, NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
         }
 
+        if (_scopeEnabled && !_scopeEnabledPause) {
+            unsigned int numMonoAudioChannels = 1;
+            unsigned int monoAudioChannel = 0;
+            addBufferToScope(_scopeInput, _scopeInputOffset, monoAudioSamples, monoAudioChannel, numMonoAudioChannels); 
+            _scopeInputOffset += NETWORK_SAMPLES_PER_FRAME;
+            _scopeInputOffset %= SAMPLES_PER_SCOPE_WIDTH;
+        }
+
         NodeList* nodeList = NodeList::getInstance();
         SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer);
         
@@ -810,6 +825,30 @@ void Audio::processReceivedAudio(const QByteArray& audioByteArray) {
             if (_outputDevice) {
                 _outputDevice->write(outputBuffer);
             }
+
+            if (_scopeEnabled && !_scopeEnabledPause) {
+                unsigned int numAudioChannels = _desiredOutputFormat.channelCount();
+                int16_t* samples = ringBufferSamples;
+                for (int numSamples = numNetworkOutputSamples / numAudioChannels; numSamples > 0; numSamples -= NETWORK_SAMPLES_PER_FRAME) {
+
+                    unsigned int audioChannel = 0;
+                    addBufferToScope(
+                        _scopeOutputLeft, 
+                        _scopeOutputOffset, 
+                        samples, audioChannel, numAudioChannels); 
+
+                    audioChannel = 1;
+                    addBufferToScope(
+                        _scopeOutputRight, 
+                        _scopeOutputOffset, 
+                        samples, audioChannel, numAudioChannels); 
+                
+                    _scopeOutputOffset += NETWORK_SAMPLES_PER_FRAME;
+                    _scopeOutputOffset %= SAMPLES_PER_SCOPE_WIDTH;
+                    samples += NETWORK_SAMPLES_PER_FRAME * numAudioChannels;
+                }
+            }
+
             delete[] ringBufferSamples;
         }
     }
@@ -1016,6 +1055,140 @@ void Audio::renderToolBox(int x, int y, bool boxed) {
     glDisable(GL_TEXTURE_2D);
 }
 
+void Audio::toggleScopePause() {
+    _scopeEnabledPause = !_scopeEnabledPause;
+}
+
+void Audio::toggleScope() {
+    _scopeEnabled = !_scopeEnabled;
+    if (_scopeEnabled) {
+        static const int width = SAMPLES_PER_SCOPE_WIDTH;
+        _scopeInputOffset = 0;
+        _scopeOutputOffset = 0;
+        memset(_scopeInput.data(), 0, width * sizeof(int16_t));
+        memset(_scopeOutputLeft.data(), 0, width * sizeof(int16_t));
+        memset(_scopeOutputRight.data(), 0, width * sizeof(int16_t));
+    }
+}
+
+void Audio::addBufferToScope(
+    QByteArray& byteArray, unsigned int frameOffset, const int16_t* source, unsigned int sourceChannel, unsigned int sourceNumberOfChannels) {
+
+    // Constant multiplier to map sample value to vertical size of scope
+    float multiplier = (float)MULTIPLIER_SCOPE_HEIGHT / logf(2.0f);
+
+    // Temporary variable receives sample value
+    float sample;
+
+    // Temporary variable receives mapping of sample value
+    int16_t value;
+
+    // Short int pointer to mapped samples in byte array
+    int16_t* destination = (int16_t*) byteArray.data();
+
+    for (int i = 0; i < NETWORK_SAMPLES_PER_FRAME; i++) {
+
+        sample = (float)source[i * sourceNumberOfChannels + sourceChannel];
+		
+        if (sample > 0) {
+            value = (int16_t)(multiplier * logf(sample));
+        } else if (sample < 0) {
+            value = (int16_t)(-multiplier * logf(-sample));
+        } else {
+            value = 0;
+        }
+
+        destination[i + frameOffset] = value;
+    }
+}
+
+void Audio::renderScope(int width, int height) {
+
+    if (!_scopeEnabled)
+        return;
+
+    static const float backgroundColor[4] = { 0.2f, 0.2f, 0.2f, 0.6f };
+    static const float gridColor[4] = { 0.3f, 0.3f, 0.3f, 0.6f };
+    static const float inputColor[4] = { 0.3f, .7f, 0.3f, 0.6f };
+    static const float outputLeftColor[4] = { 0.7f, .3f, 0.3f, 0.6f };
+    static const float outputRightColor[4] = { 0.3f, .3f, 0.7f, 0.6f };
+    static const int gridRows = 2;
+    static const int gridCols = 5;
+
+    int x = (width - SAMPLES_PER_SCOPE_WIDTH) / 2;
+    int y = (height - SAMPLES_PER_SCOPE_HEIGHT) / 2;
+    int w = SAMPLES_PER_SCOPE_WIDTH;
+    int h = SAMPLES_PER_SCOPE_HEIGHT;
+
+    renderBackground(backgroundColor, x, y, w, h);
+    renderGrid(gridColor, x, y, w, h, gridRows, gridCols);
+    renderLineStrip(inputColor, x, y, w, _scopeInputOffset, _scopeInput);
+    renderLineStrip(outputLeftColor, x, y, w, _scopeOutputOffset, _scopeOutputLeft);
+    renderLineStrip(outputRightColor, x, y, w, _scopeOutputOffset, _scopeOutputRight);
+}
+
+void Audio::renderBackground(const float* color, int x, int y, int width, int height) {
+
+    glColor4fv(color);
+    glBegin(GL_QUADS);
+
+    glVertex2i(x, y);
+    glVertex2i(x + width, y);
+    glVertex2i(x + width, y + height);
+    glVertex2i(x , y + height);
+
+    glEnd();
+    glColor4f(1, 1, 1, 1); 
+}
+
+void Audio::renderGrid(const float* color, int x, int y, int width, int height, int rows, int cols) {
+
+    glColor4fv(color);
+    glBegin(GL_LINES);
+
+    int dx = width / cols;
+    int dy = height / rows;
+    int tx = x;
+    int ty = y;
+
+    // Draw horizontal grid lines
+    for (int i = rows + 1; --i >= 0; ) {
+        glVertex2i(x, ty);
+        glVertex2i(x + width, ty);
+        ty += dy;
+    }
+    // Draw vertical grid lines
+    for (int i = cols + 1; --i >= 0; ) {
+        glVertex2i(tx, y);
+        glVertex2i(tx, y + height);
+        tx += dx;
+    }
+    glEnd();
+    glColor4f(1, 1, 1, 1); 
+}
+
+void Audio::renderLineStrip(const float* color, int x, int y, int n, int offset, const QByteArray& byteArray) {
+
+    glColor4fv(color);
+    glBegin(GL_LINE_STRIP);
+
+    int16_t sample;
+    int16_t* samples = ((int16_t*) byteArray.data()) + offset;
+    y += SAMPLES_PER_SCOPE_HEIGHT / 2;
+    for (int i = n - offset; --i >= 0; ) {
+        sample = *samples++;
+        glVertex2i(x++, y - sample);
+    }
+    samples = (int16_t*) byteArray.data();
+    for (int i = offset; --i >= 0; ) {
+        sample = *samples++;
+        glVertex2i(x++, y - sample);
+    }
+    glEnd();
+    glColor4f(1, 1, 1, 1); 
+}
+
+
 bool Audio::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo) {
     bool supportedFormat = false;
     
diff --git a/interface/src/Audio.h b/interface/src/Audio.h
index 7ad1ddd926..e1b8a7dddc 100644
--- a/interface/src/Audio.h
+++ b/interface/src/Audio.h
@@ -25,6 +25,7 @@
 #include <QtCore/QVector>
 #include <QtMultimedia/QAudioFormat>
 #include <QVector>
+#include <QByteArray>
 
 #include <AbstractAudioInterface.h>
 #include <AudioRingBuffer.h>
@@ -64,6 +65,7 @@ public:
     bool mousePressEvent(int x, int y);
     
     void renderToolBox(int x, int y, bool boxed);
+    void renderScope(int width, int height);
     
     int getNetworkSampleRate() { return SAMPLE_RATE; }
     int getNetworkBufferLengthSamplesPerChannel() { return NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; }
@@ -80,6 +82,8 @@ public slots:
     void toggleMute();
     void toggleAudioNoiseReduction();
     void toggleToneInjection();
+    void toggleScope();
+    void toggleScopePause();
     void toggleAudioSpatialProcessing();
     
     virtual void handleAudioByteArray(const QByteArray& audioByteArray);
@@ -193,6 +197,29 @@ private:
     int calculateNumberOfFrameSamples(int numBytes);
     float calculateDeviceToNetworkInputRatio(int numBytes);
 
+    // Audio scope methods for data acquisition
+    void addBufferToScope(
+        QByteArray& byteArray, unsigned int frameOffset, const int16_t* source, unsigned int sourceChannel, unsigned int sourceNumberOfChannels);
+
+    // Audio scope methods for rendering
+    void renderBackground(const float* color, int x, int y, int width, int height);
+    void renderGrid(const float* color, int x, int y, int width, int height, int rows, int cols);
+    void renderLineStrip(const float* color, int x, int y, int n, int offset, const QByteArray& byteArray);
+
+    // Audio scope data
+    static const unsigned int NETWORK_SAMPLES_PER_FRAME = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL;
+    static const unsigned int FRAMES_PER_SCOPE = 5;
+    static const unsigned int SAMPLES_PER_SCOPE_WIDTH = FRAMES_PER_SCOPE * NETWORK_SAMPLES_PER_FRAME;
+    static const unsigned int MULTIPLIER_SCOPE_HEIGHT = 20;
+    static const unsigned int SAMPLES_PER_SCOPE_HEIGHT = 2 * 15 * MULTIPLIER_SCOPE_HEIGHT;
+    bool _scopeEnabled;
+    bool _scopeEnabledPause;
+    int _scopeInputOffset;
+    int _scopeOutputOffset;
+    QByteArray _scopeInput;
+    QByteArray _scopeOutputLeft;
+    QByteArray _scopeOutputRight;
+
 };
 
 
diff --git a/interface/src/AudioReflector.h b/interface/src/AudioReflector.h
index 582345e064..1bfb52ea09 100644
--- a/interface/src/AudioReflector.h
+++ b/interface/src/AudioReflector.h
@@ -5,6 +5,9 @@
 //  Created by Brad Hefta-Gaub on 4/2/2014
 //  Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
 //
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
 
 #ifndef interface_AudioReflector_h
 #define interface_AudioReflector_h
diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp
index b5b0f65d82..84b17fddca 100644
--- a/interface/src/Menu.cpp
+++ b/interface/src/Menu.cpp
@@ -260,6 +260,9 @@ Menu::Menu() :
     addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Bandwidth, 0, true);
     addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0, this, SLOT(bandwidthDetails()));
     addActionToQMenuAndActionHash(viewMenu, MenuOption::OctreeStats, 0, this, SLOT(octreeStatsDetails()));
+    addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::AudioScope, 0, false,
+                                           appInstance->getAudio(),
+                                           SLOT(toggleScope()));
 
     QMenu* developerMenu = addMenu("Developer");
 
@@ -386,6 +389,11 @@ Menu::Menu() :
                                            false,
                                            appInstance->getAudio(),
                                            SLOT(toggleToneInjection()));
+    addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScopePause,
+                                           Qt::CTRL | Qt::Key_P,
+                                           false,
+                                           appInstance->getAudio(),
+                                           SLOT(toggleScopePause()));
 
     QMenu* spatialAudioMenu = audioDebugMenu->addMenu("Spatial Audio");
 
diff --git a/interface/src/Menu.h b/interface/src/Menu.h
index a55570afaf..a1e6cded2a 100644
--- a/interface/src/Menu.h
+++ b/interface/src/Menu.h
@@ -259,8 +259,9 @@ namespace MenuOption {
     const QString AmbientOcclusion = "Ambient Occlusion";
     const QString Atmosphere = "Atmosphere";
     const QString AudioNoiseReduction = "Audio Noise Reduction";
+    const QString AudioScope = "Audio Scope";
+    const QString AudioScopePause = "Pause Audio Scope";
     const QString AudioToneInjection = "Inject Test Tone";
-
     const QString AudioSpatialProcessing = "Audio Spatial Processing";
     const QString AudioSpatialProcessingHeadOriented = "Head Oriented";
     const QString AudioSpatialProcessingIncludeOriginal = "Includes Network Original";
diff --git a/interface/src/voxels/PrimitiveRenderer.h b/interface/src/voxels/PrimitiveRenderer.h
index 8626e2e2a4..06dac119b0 100644
--- a/interface/src/voxels/PrimitiveRenderer.h
+++ b/interface/src/voxels/PrimitiveRenderer.h
@@ -106,7 +106,7 @@ private:
     /// Copy constructor prohibited.
     ///
     Primitive(
-        const Primitive& prim
+        const Primitive& copy
         );
 
     // SPI methods are defined here
@@ -153,14 +153,14 @@ public:
     /// Configuration dependency injection constructor.
     ///
     Cube(
-        float x,
-        float y,
-        float z,
-        float s,
-        unsigned char r,
-        unsigned char g,
-        unsigned char b,
-        unsigned char faces
+        float x,                                ///< Cube location on X-axis
+        float y,                                ///< Cube location on Y-axis
+        float z,                                ///< Cube location on Z-axis
+        float s,                                ///< Cube size
+        unsigned char r,                        ///< Cube red color component
+        unsigned char g,                        ///< Cube green color component
+        unsigned char b,                        ///< Cube blue color component
+        unsigned char faces                     ///< Bitmask of faces of cube excluded from construction
         );
 
     ~Cube();
@@ -172,36 +172,48 @@ private:
         const Cube& cube
         );
 
+    /// Cube initialization
+    ///
     void init(
-        float x,
-        float y,
-        float z,
-        float s,
-        unsigned char r,
-        unsigned char g,
-        unsigned char b,
-        unsigned char faceExclusions
+        float x,                                ///< Cube location on X-axis
+        float y,                                ///< Cube location on Y-axis
+        float z,                                ///< Cube location on Z-axis
+        float s,                                ///< Cube size
+        unsigned char r,                        ///< Cube red color component
+        unsigned char g,                        ///< Cube green color component
+        unsigned char b,                        ///< Cube blue color component
+        unsigned char faceExclusions            ///< Bitmask of faces of cube excluded from construction
         );
 
+    /// Cube termination
+    ///
     void terminate();
 
+    /// Initialize cube's vertex list 
+    ///
     void initializeVertices(
-        float x,
-        float y,
-        float z,
-        float s,
-        unsigned char r,
-        unsigned char g,
-        unsigned char b,
-        unsigned char faceExclusions
+        float x,                                ///< Cube location on X-axis
+        float y,                                ///< Cube location on Y-axis
+        float z,                                ///< Cube location on Z-axis
+        float s,                                ///< Cube size
+        unsigned char r,                        ///< Cube red color component
+        unsigned char g,                        ///< Cube green color component
+        unsigned char b,                        ///< Cube blue color component
+        unsigned char faceExclusions            ///< Bitmask of faces of cube excluded from construction
         );
 
+    /// Terminate cube's vertex list
+    ///
     void terminateVertices();
 
+    /// Initialize cube's triangle list
+    ///
     void initializeTris(
         unsigned char faceExclusions
         );
 
+    /// Terminate cube's triangle list
+    ///
     void terminateTris();
 
     // SPI virtual override methods go here
@@ -219,11 +231,11 @@ private:
 
     unsigned long _cpuMemoryUsage;              ///< Memory allocation of object
 
-    static const int _sNumFacesPerCube = 6;
-    static const int _sNumVerticesPerCube = 24;
-    static unsigned char _sFaceIndexToHalfSpaceMask[6];
-    static float _sVertexIndexToConstructionVector[24][3];
-    static float _sVertexIndexToNormalVector[6][3];
+    static const int _sNumFacesPerCube = 6;     ///< Number of faces per cube
+    static const int _sNumVerticesPerCube = 24; ///< Number of vertices per cube
+    static unsigned char _sFaceIndexToHalfSpaceMask[6];     ///< index to bitmask map
+    static float _sVertexIndexToConstructionVector[24][3];  ///< Vertex index to construction vector map
+    static float _sVertexIndexToNormalVector[6][3];         ///< Vertex index to normal vector map
 
 };
 
@@ -242,13 +254,13 @@ public:
     /// Add primitive to renderer database.
     ///
     int add(
-        Primitive* primitive                    ///< Pointer to primitive
+        Primitive* primitive                    ///< Primitive instance to be added
         );
 
     /// Remove primitive from renderer database.
     ///
     void remove(
-        int id                                    ///< Primitive id
+        int id                                  ///< Primitive id to be removed
         );
 
     /// Clear all primitives from renderer database
@@ -278,7 +290,7 @@ private:
     /// Copy constructor prohibited.
     ///
     Renderer(
-        const Renderer& primitive
+        const Renderer& copy
         );
 
     // SPI methods are defined here
@@ -286,10 +298,10 @@ private:
     /// Add primitive to renderer database.
     ///    Service implementer to provide private override for this method
     ///    in derived class
-    ///    @return primitive id
+    ///    @return Primitive id
     ///
     virtual int vAdd(
-        Primitive* primitive                    ///< Pointer to primitive
+        Primitive* primitive                    ///< Primitive instance to be added
         ) = 0;
 
     /// Remove primitive from renderer database.
@@ -297,7 +309,7 @@ private:
     ///    in derived class
     ///
     virtual void vRemove(
-        int id                                    ///< Primitive id
+        int id                                  ///< Primitive id
         ) = 0;
 
     /// Clear all primitives from renderer database
@@ -332,7 +344,7 @@ public:
     /// Configuration dependency injection constructor.
     ///
     PrimitiveRenderer(
-        int maxCount
+        int maxCount                            ///< Max count 
         );
 
     ~PrimitiveRenderer();
@@ -365,39 +377,39 @@ private:
     /// Construct the elements of the faces of the primitive.
     ///
     void constructElements(
-        Primitive* primitive
+        Primitive* primitive                    ///< Primitive instance
         );
 
     /// Deconstruct the elements of the faces of the primitive.
     ///
     void deconstructElements(
-        Primitive* primitive
+        Primitive* primitive                    ///< Primitive instance
         );
 
     /// Deconstruct the triangle element from the GL buffer.
     ///
     void deconstructTriElement(
-        int idx
+        int idx                                 ///< Triangle element index
         );
 
     /// Deconstruct the vertex element from the GL buffer.
     ///
     void deconstructVertexElement(
-        int idx
+        int idx                                 ///< Vertex element index
         );
 
     /// Transfer the vertex element to the GL buffer.
     ///
     void transferVertexElement(
-        int idx,
-        VertexElement *vertex
+        int idx,                                ///< Vertex element index 
+        VertexElement *vertex                   ///< Vertex element instance
         );
 
     /// Transfer the triangle element to the GL buffer.
     ///
     void transferTriElement(
-        int idx,
-        int tri[3]
+        int idx,                                ///< Triangle element index
+        int tri[3]                              ///< Triangle element data
         );
 
     /// Get available primitive index.
@@ -424,13 +436,13 @@ private:
     /// Add primitive to renderer database.
     ///
     int vAdd(
-        Primitive* primitive
+        Primitive* primitive                    ///< Primitive instance to be added
         );
 
     /// Remove primitive from renderer database.
     ///
     void vRemove(
-        int id
+        int id                                  ///< Primitive id to be removed
         );
 
     /// Clear all primitives from renderer database
@@ -451,7 +463,7 @@ private:
 
 private:
 
-    int _maxCount;
+    int _maxCount;                              ///< Maximum count of tris
 
     // GL related parameters
 
@@ -479,8 +491,8 @@ private:
 
     // Statistics parameters, not necessary for proper operation
 
-    unsigned long _gpuMemoryUsage;
-    unsigned long _cpuMemoryUsage;
+    unsigned long _gpuMemoryUsage;              ///< GPU memory used by this instance
+    unsigned long _cpuMemoryUsage;              ///< CPU memory used by this instance
 
 
     static const int _sIndicesPerTri = 3;
diff --git a/interface/src/voxels/VoxelSystem.cpp b/interface/src/voxels/VoxelSystem.cpp
index 8d59f33674..9a54a08619 100644
--- a/interface/src/voxels/VoxelSystem.cpp
+++ b/interface/src/voxels/VoxelSystem.cpp
@@ -938,6 +938,8 @@ void VoxelSystem::copyWrittenDataSegmentToReadArrays(glBufferIndex segmentStart,
 }
 
 void VoxelSystem::copyWrittenDataToReadArrays(bool fullVBOs) {
+    static unsigned int lockForReadAttempt = 0;
+    static unsigned int lockForWriteAttempt = 0;
     PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
                             "copyWrittenDataToReadArrays()");
 
@@ -946,7 +948,9 @@ void VoxelSystem::copyWrittenDataToReadArrays(bool fullVBOs) {
     // time around, the only side effect is the VBOs won't be updated this frame
     const int WAIT_FOR_LOCK_IN_MS = 5;
     if (_readArraysLock.tryLockForWrite(WAIT_FOR_LOCK_IN_MS)) {
+        lockForWriteAttempt = 0;
         if (_writeArraysLock.tryLockForRead(WAIT_FOR_LOCK_IN_MS)) {
+            lockForReadAttempt = 0;
             if (_voxelsDirty && _voxelsUpdated) {
                 if (fullVBOs) {
                     copyWrittenDataToReadArraysFullVBOs();
@@ -956,11 +960,19 @@ void VoxelSystem::copyWrittenDataToReadArrays(bool fullVBOs) {
             }
             _writeArraysLock.unlock();
         } else {
-            qDebug() << "couldn't get _writeArraysLock.LockForRead()...";
+            lockForReadAttempt++;
+            // only report error of first failure
+            if (lockForReadAttempt == 1) {
+                qDebug() << "couldn't get _writeArraysLock.LockForRead()...";
+            }
         }
         _readArraysLock.unlock();
     } else {
-        qDebug() << "couldn't get _readArraysLock.LockForWrite()...";
+        lockForWriteAttempt++;
+        // only report error of first failure
+        if (lockForWriteAttempt == 1) {
+            qDebug() << "couldn't get _readArraysLock.LockForWrite()...";
+        }
     }
 }
 
@@ -1683,11 +1695,12 @@ bool VoxelSystem::inspectForExteriorOcclusionsOperation(OctreeElement* element,
 
             //qDebug("Completely occupied voxel at %f %f %f size: %f", v.x, v.y, v.z, s);
 
-            // TODO: All of the exterior faces of this voxel element are
-            //        occluders, which means that this element is completely
-            //        occupied. Hence, the subtree from this node could be
-            //        pruned and replaced by a leaf voxel, if the visible 
-            //        properties of the children are the same
+            // All of the exterior faces of this voxel element are
+            // occluders, which means that this element is completely
+            // occupied. Hence, the subtree from this node could be
+            // pruned and replaced by a leaf voxel, if the visible 
+            // properties of the children are the same
+
         } else if (exteriorOcclusions != OctreeElement::HalfSpace::None) {
             //const glm::vec3& v = voxel->getCorner();
             //float s = voxel->getScale();
diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp
index db8a689001..ce78ec2d10 100644
--- a/libraries/networking/src/LimitedNodeList.cpp
+++ b/libraries/networking/src/LimitedNodeList.cpp
@@ -227,7 +227,30 @@ qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram, const SharedNo
             }
         }
         
-        writeDatagram(datagram, *destinationSockAddr, destinationNode->getConnectionSecret());
+        return writeDatagram(datagram, *destinationSockAddr, destinationNode->getConnectionSecret());
+    }
+    
+    // didn't have a destinationNode to send to, return 0
+    return 0;
+}
+
+qint64 LimitedNodeList::writeUnverifiedDatagram(const QByteArray& datagram, const SharedNodePointer& destinationNode,
+                               const HifiSockAddr& overridenSockAddr) {
+    if (destinationNode) {
+        // if we don't have an ovveriden address, assume they want to send to the node's active socket
+        const HifiSockAddr* destinationSockAddr = &overridenSockAddr;
+        if (overridenSockAddr.isNull()) {
+            if (destinationNode->getActiveSocket()) {
+                // use the node's active socket as the destination socket
+                destinationSockAddr = destinationNode->getActiveSocket();
+            } else {
+                // we don't have a socket to send to, return 0
+                return 0;
+            }
+        }
+        
+        // don't use the node secret!
+        return writeDatagram(datagram, *destinationSockAddr, QUuid());
     }
     
     // didn't have a destinationNode to send to, return 0
@@ -243,6 +266,11 @@ qint64 LimitedNodeList::writeDatagram(const char* data, qint64 size, const Share
     return writeDatagram(QByteArray(data, size), destinationNode, overridenSockAddr);
 }
 
+qint64 LimitedNodeList::writeUnverifiedDatagram(const char* data, qint64 size, const SharedNodePointer& destinationNode,
+                               const HifiSockAddr& overridenSockAddr) {
+    return writeUnverifiedDatagram(QByteArray(data, size), destinationNode, overridenSockAddr);
+}
+
 void LimitedNodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet) {
     // the node decided not to do anything with this packet
     // if it comes from a known source we should keep that node alive
diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h
index ea9cb42436..2a98dc536f 100644
--- a/libraries/networking/src/LimitedNodeList.h
+++ b/libraries/networking/src/LimitedNodeList.h
@@ -66,10 +66,17 @@ public:
     
     qint64 writeDatagram(const QByteArray& datagram, const SharedNodePointer& destinationNode,
                          const HifiSockAddr& overridenSockAddr = HifiSockAddr());
+
+    qint64 writeUnverifiedDatagram(const QByteArray& datagram, const SharedNodePointer& destinationNode,
+                               const HifiSockAddr& overridenSockAddr = HifiSockAddr());
+
     qint64 writeUnverifiedDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr);
     qint64 writeDatagram(const char* data, qint64 size, const SharedNodePointer& destinationNode,
                          const HifiSockAddr& overridenSockAddr = HifiSockAddr());
 
+    qint64 writeUnverifiedDatagram(const char* data, qint64 size, const SharedNodePointer& destinationNode,
+                         const HifiSockAddr& overridenSockAddr = HifiSockAddr());
+
     void(*linkedDataCreateCallback)(Node *);
 
     NodeHash getNodeHash();
diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h
index 0f52f90962..b7535e5064 100644
--- a/libraries/networking/src/PacketHeaders.h
+++ b/libraries/networking/src/PacketHeaders.h
@@ -69,7 +69,7 @@ const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>()
     << PacketTypeDomainServerRequireDTLS << PacketTypeDomainConnectRequest
     << PacketTypeDomainList << PacketTypeDomainListRequest
     << PacketTypeCreateAssignment << PacketTypeRequestAssignment << PacketTypeStunResponse
-    << PacketTypeNodeJsonStats;
+    << PacketTypeNodeJsonStats << PacketTypeVoxelQuery << PacketTypeParticleQuery;
 
 const int NUM_BYTES_MD5_HASH = 16;
 const int NUM_STATIC_HEADER_BYTES = sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID;
diff --git a/libraries/octree/src/OctreeElementBag.cpp b/libraries/octree/src/OctreeElementBag.cpp
index f929980a75..92a8fe5bff 100644
--- a/libraries/octree/src/OctreeElementBag.cpp
+++ b/libraries/octree/src/OctreeElementBag.cpp
@@ -16,13 +16,21 @@ OctreeElementBag::OctreeElementBag() :
     _bagElements()
 {
     OctreeElement::addDeleteHook(this);
+    _hooked = true;
 };
 
 OctreeElementBag::~OctreeElementBag() {
-    OctreeElement::removeDeleteHook(this);
+    unhookNotifications();
     deleteAll();
 }
 
+void OctreeElementBag::unhookNotifications() {
+    if (_hooked) {
+        OctreeElement::removeDeleteHook(this);
+        _hooked = false;
+    }
+}
+
 void OctreeElementBag::elementDeleted(OctreeElement* element) {
     remove(element); // note: remove can safely handle nodes that aren't in it, so we don't need to check contains()
 }
diff --git a/libraries/octree/src/OctreeElementBag.h b/libraries/octree/src/OctreeElementBag.h
index afc34bf1a6..8c18ece773 100644
--- a/libraries/octree/src/OctreeElementBag.h
+++ b/libraries/octree/src/OctreeElementBag.h
@@ -36,8 +36,11 @@ public:
     void deleteAll();
     virtual void elementDeleted(OctreeElement* element);
 
+    void unhookNotifications();
+
 private:
     QSet<OctreeElement*> _bagElements;
+    bool _hooked;
 };
 
 #endif // hifi_OctreeElementBag_h
diff --git a/libraries/octree/src/OctreeHeadlessViewer.cpp b/libraries/octree/src/OctreeHeadlessViewer.cpp
index b25cb4ff8a..5574b376cb 100644
--- a/libraries/octree/src/OctreeHeadlessViewer.cpp
+++ b/libraries/octree/src/OctreeHeadlessViewer.cpp
@@ -221,7 +221,7 @@ void OctreeHeadlessViewer::queryOctree() {
             int packetLength = endOfQueryPacket - queryPacket;
 
             // make sure we still have an active socket
-            nodeList->writeDatagram(reinterpret_cast<const char*>(queryPacket), packetLength, node);
+            nodeList->writeUnverifiedDatagram(reinterpret_cast<const char*>(queryPacket), packetLength, node);
         }
     }
 }
diff --git a/libraries/octree/src/ViewFrustum.cpp b/libraries/octree/src/ViewFrustum.cpp
index f2d19a66ea..c2e38b73c9 100644
--- a/libraries/octree/src/ViewFrustum.cpp
+++ b/libraries/octree/src/ViewFrustum.cpp
@@ -130,9 +130,11 @@ void ViewFrustum::calculate() {
 
     // Also calculate our projection matrix in case people want to project points...
     // Projection matrix : Field of View, ratio, display range : near to far
-    glm::mat4 projection = glm::perspective(_fieldOfView, _aspectRatio, _nearClip, _farClip);
-    glm::vec3 lookAt     = _position + _direction;
-    glm::mat4 view       = glm::lookAt(_position, lookAt, _up);
+    const float CLIP_NUDGE = 1.0f;
+    float farClip = (_farClip != _nearClip) ? _farClip : _nearClip + CLIP_NUDGE; // don't allow near and far to be equal
+    glm::mat4 projection = glm::perspective(_fieldOfView, _aspectRatio, _nearClip, farClip);
+    glm::vec3 lookAt = _position + _direction;
+    glm::mat4 view = glm::lookAt(_position, lookAt, _up);
 
     // Our ModelViewProjection : multiplication of our 3 matrices (note: model is identity, so we can drop it)
     _ourModelViewProjectionMatrix = projection * view; // Remember, matrix multiplication is the other way around