diff --git a/Base/Devices/imstkHDAPIDeviceClient.cpp b/Base/Devices/imstkHDAPIDeviceClient.cpp
index b62b437afe1a2d0c161a1919d0a7fd03287a88ac..4ebddbfb75ca79fc3981573918d4d74315a132cf 100644
--- a/Base/Devices/imstkHDAPIDeviceClient.cpp
+++ b/Base/Devices/imstkHDAPIDeviceClient.cpp
@@ -34,6 +34,8 @@ namespace imstk
 void
 HDAPIDeviceClient::init()
 {
+    m_buttons = std::map < size_t, bool > { { 0, false }, { 1, false }, { 2, false }, { 3, false }};
+
     // Open Device
     m_handle = hdInitDevice(this->getDeviceName().c_str());
 
diff --git a/Base/Devices/imstkHDAPIDeviceClient.h b/Base/Devices/imstkHDAPIDeviceClient.h
index d0483065fa659966ec9c0f2ff6ea678e15af7bb4..a6c1254df491259fe0307cd4c26702178bc5303a 100644
--- a/Base/Devices/imstkHDAPIDeviceClient.h
+++ b/Base/Devices/imstkHDAPIDeviceClient.h
@@ -32,7 +32,8 @@
 
 #include <memory>
 
-namespace imstk {
+namespace imstk
+{
 
 struct HD_state
 {
@@ -50,25 +51,39 @@ class HDAPIDeviceClient : public DeviceClient
 {
 public:
 
-    HDAPIDeviceClient(std::string name):
-        DeviceClient(name, "localhost")
-    {}
-
-    virtual ~HDAPIDeviceClient() {}
+    ///
+    /// \brief Constructor/Destructor
+    ///
+    HDAPIDeviceClient(std::string name): DeviceClient(name, "localhost"){}
+    virtual ~HDAPIDeviceClient(){}
 
 protected:
 
     friend class HDAPIDeviceServer;
+
+    ///
+    /// \brief Initialize the phantom omni device
+    ///
     void init();
+
+    ///
+    /// \brief Use callback to get tracking data from phantom omni
+    ///
     void run();
+
+    ///
+    /// \brief Closes the phantom omni device
+    ///
     void cleanUp();
 
 private:
-
+    ///
+    /// \brief Phantom omni device api callback
+    ///
     static HDCallbackCode HDCALLBACK hapticCallback(void* pData);
 
-    HHD m_handle; //!< device handle
-    HD_state m_state; //!< device reading state
+    HHD m_handle;     ///< device handle
+    HD_state m_state; ///< device reading state
 
 };
 }
diff --git a/Base/Scene/imstkScene.cpp b/Base/Scene/imstkScene.cpp
index 2983ebf0c503263f3d5dd93cb9673ff231174bb0..73360e8d4ab567f7dbd4a0edd2a4887770666cbc 100644
--- a/Base/Scene/imstkScene.cpp
+++ b/Base/Scene/imstkScene.cpp
@@ -21,6 +21,7 @@
 
 #include "imstkScene.h"
 #include "imstkCameraController.h"
+#include "imstkSceneObjectControllerBase.h"
 
 #include <g3log/g3log.hpp>
 
@@ -48,6 +49,12 @@ Scene::getSceneObjects() const
     return v;
 }
 
+const std::vector<std::shared_ptr<SceneObjectControllerBase>>
+Scene::getSceneObjectControllers() const
+{
+    return m_objectControllers;
+}
+
 std::shared_ptr<SceneObject>
 Scene::getSceneObject(std::string sceneObjectName) const
 {
@@ -62,7 +69,7 @@ Scene::getSceneObject(std::string sceneObjectName) const
 }
 
 void
-Scene::addSceneObject(std::shared_ptr<SceneObject>newSceneObject)
+Scene::addSceneObject(std::shared_ptr<SceneObject> newSceneObject)
 {
     std::string newSceneObjectName = newSceneObject->getName();
 
@@ -184,4 +191,9 @@ void Scene::addNonlinearSolver(std::shared_ptr<SolverBase> solver)
     m_solvers.push_back(solver);
 }
 
+void Scene::addObjectController(std::shared_ptr<SceneObjectControllerBase> controller)
+{
+    m_objectControllers.push_back(controller);
+}
+
 } // imstk
\ No newline at end of file
diff --git a/Base/Scene/imstkScene.h b/Base/Scene/imstkScene.h
index 4191cd3bd2760b8a3db5ad8ec4a77d4d4edc4fc6..1a753b91fc3e502400edff7d3017be7c1d6a635f 100644
--- a/Base/Scene/imstkScene.h
+++ b/Base/Scene/imstkScene.h
@@ -34,6 +34,8 @@
 namespace imstk
 {
 
+class SceneObjectControllerBase;
+
 ///
 /// \class Scene
 ///
@@ -67,6 +69,11 @@ public:
     ///
     const std::vector<std::shared_ptr<SceneObject>> getSceneObjects() const;
 
+    ///
+    /// \brief Get the scene object controllers
+    ///
+    const std::vector <std::shared_ptr<SceneObjectControllerBase>> getSceneObjectControllers() const;
+
     ///
     /// \brief Get a scene object of a specific name
     ///
@@ -124,6 +131,11 @@ public:
     ///
     void addNonlinearSolver(std::shared_ptr<SolverBase> solver);
 
+    ///
+    /// \brief Add objects controllers
+    ///
+    void addObjectController(std::shared_ptr<SceneObjectControllerBase> controller);
+
 protected:
 
     std::string m_name; ///> Name of the scene
@@ -131,7 +143,8 @@ protected:
     NamedMap<Light> m_lightsMap;
     std::shared_ptr<Camera> m_camera = std::make_shared<Camera>();
     std::shared_ptr<CollisionGraph> m_collisionGraph = std::make_shared<CollisionGraph>();
-    std::vector<std::shared_ptr<SolverBase>> m_solvers; ///> List of non-linear solvers
+    std::vector<std::shared_ptr<SolverBase>> m_solvers;     ///> List of non-linear solvers
+    std::vector<std::shared_ptr<SceneObjectControllerBase>> m_objectControllers; ///> List of controllers
 };
 
 } // imstk
diff --git a/Base/SceneElements/Controllers/imstkCameraController.cpp b/Base/SceneElements/Controllers/imstkCameraController.cpp
index 06272f2e50ae9be67935c86fa66343a504a78024..2162cf14c68c6a61bfa7da73d727d74e41fa1116 100644
--- a/Base/SceneElements/Controllers/imstkCameraController.cpp
+++ b/Base/SceneElements/Controllers/imstkCameraController.cpp
@@ -50,15 +50,18 @@ CameraController::initModule()
 void
 CameraController::runModule()
 {
-    Vec3d p;
-    Quatd r;
-
-    if (!this->computeTrackingData(p, r))
+    if (!m_trackingDataUptoDate)
     {
-        LOG(WARNING) << "CameraController::runModule warning: could not update tracking info.";
-        return;
+        if (!updateTrackingData())
+        {
+            LOG(WARNING) << "CameraController::runModule warning: could not update tracking info.";
+            return;
+        }
     }
 
+    Vec3d p = getPosition();
+    Quatd r = getRotation();
+
     // Set camera info
     m_camera.setPosition(p);
     m_camera.setFocalPoint((r*FORWARD_VECTOR)+p);
diff --git a/Base/SceneElements/Controllers/imstkCameraController.h b/Base/SceneElements/Controllers/imstkCameraController.h
index 2e2c94a28b3c6c4e6577a2c2bcfe80797b1c1980..b5958d4d320759e99a2384358c9096753a3f5087 100644
--- a/Base/SceneElements/Controllers/imstkCameraController.h
+++ b/Base/SceneElements/Controllers/imstkCameraController.h
@@ -23,7 +23,7 @@
 #define imstkCameraController_h
 
 #include "imstkModule.h"
-#include "imstkTrackingController.h"
+#include "imstkDeviceTracker.h"
 #include "imstkCamera.h"
 
 #include <memory>
@@ -36,7 +36,7 @@ namespace imstk
 ///
 /// \brief
 ///
-class CameraController : public Module, public TrackingController
+class CameraController : public Module, public DeviceTracker
 {
 public:
     ///
@@ -46,7 +46,7 @@ public:
                      std::shared_ptr<DeviceClient> deviceClient) :
         Module("Camera controller"),
         m_camera(camera),
-        TrackingController(deviceClient)
+        DeviceTracker(deviceClient)
     {}
 
     ///
diff --git a/Base/SceneElements/Controllers/imstkTrackingController.cpp b/Base/SceneElements/Controllers/imstkDeviceTracker.cpp
similarity index 52%
rename from Base/SceneElements/Controllers/imstkTrackingController.cpp
rename to Base/SceneElements/Controllers/imstkDeviceTracker.cpp
index 41422cf96aa6370fabb113c2caee7d2867374f14..ee4ad6100821dc2057d4107b889c73d6c33d4133 100644
--- a/Base/SceneElements/Controllers/imstkTrackingController.cpp
+++ b/Base/SceneElements/Controllers/imstkDeviceTracker.cpp
@@ -19,7 +19,7 @@
 
    =========================================================================*/
 
-#include "imstkTrackingController.h"
+#include "imstkDeviceTracker.h"
 
 #include <utility>
 
@@ -29,89 +29,91 @@ namespace imstk
 {
 
 bool
-TrackingController::computeTrackingData(Vec3d& p, Quatd& r)
+DeviceTracker::updateTrackingData()
 {
     if (m_deviceClient == nullptr)
     {
-        LOG(WARNING) << "TrackingController::getTrackingData warning: no controlling device set.";
+        LOG(WARNING) << "DeviceTracker::getTrackingData warning: no controlling device set.";
         return false;
     }
 
     // Retrieve device info
-    p = m_deviceClient->getPosition();
-    r = m_deviceClient->getOrientation();
+    m_currentPos = m_deviceClient->getPosition();
+    m_currentRot = m_deviceClient->getOrientation();
 
     // Apply inverse if needed
-    if(m_invertFlags & InvertFlag::transX) p[0] = -p[0];
-    if(m_invertFlags & InvertFlag::transY) p[1] = -p[1];
-    if(m_invertFlags & InvertFlag::transZ) p[2] = -p[2];
-    if(m_invertFlags & InvertFlag::rotX) r.x() = -r.x();
-    if(m_invertFlags & InvertFlag::rotY) r.y() = -r.y();
-    if(m_invertFlags & InvertFlag::rotZ) r.z() = -r.z();
+    if (m_invertFlags & InvertFlag::transX) m_currentPos[0] = -m_currentPos[0];
+    if (m_invertFlags & InvertFlag::transY) m_currentPos[1] = -m_currentPos[1];
+    if (m_invertFlags & InvertFlag::transZ) m_currentPos[2] = -m_currentPos[2];
+    if (m_invertFlags & InvertFlag::rotX) m_currentRot.x() = -m_currentRot.x();
+    if (m_invertFlags & InvertFlag::rotY) m_currentRot.y() = -m_currentRot.y();
+    if (m_invertFlags & InvertFlag::rotZ) m_currentRot.z() = -m_currentRot.z();
 
     // Apply Offsets
-    p = m_rotationOffset * p * m_scaling + m_translationOffset;
-    r *= m_rotationOffset;
+    m_currentPos = m_rotationOffset * m_currentPos * m_scaling + m_translationOffset;
+    m_currentRot *= m_rotationOffset;
+
+    m_trackingDataUptoDate = true;
 
     return true;
 }
 
 std::shared_ptr<DeviceClient>
-TrackingController::getDeviceClient() const
+DeviceTracker::getDeviceClient() const
 {
     return m_deviceClient;
 }
 
 void
-TrackingController::setDeviceClient(std::shared_ptr<DeviceClient> deviceClient)
+DeviceTracker::setDeviceClient(std::shared_ptr<DeviceClient> deviceClient)
 {
     m_deviceClient = deviceClient;
 }
 
 double
-TrackingController::getTranslationScaling() const
+DeviceTracker::getTranslationScaling() const
 {
     return m_scaling;
 }
 
 void
-TrackingController::setTranslationScaling(double scaling)
+DeviceTracker::setTranslationScaling(double scaling)
 {
     m_scaling = scaling;
 }
 
 const Vec3d&
-TrackingController::getTranslationOffset() const
+DeviceTracker::getTranslationOffset() const
 {
     return m_translationOffset;
 }
 
 void
-TrackingController::setTranslationOffset(const Vec3d& t)
+DeviceTracker::setTranslationOffset(const Vec3d& t)
 {
     m_translationOffset = t;
 }
 
 const Quatd&
-TrackingController::getRotationOffset()
+DeviceTracker::getRotationOffset()
 {
     return m_rotationOffset;
 }
 
 void
-TrackingController::setRotationOffset(const Quatd& r)
+DeviceTracker::setRotationOffset(const Quatd& r)
 {
     m_rotationOffset = r;
 }
 
 unsigned char
-TrackingController::getInversionFlags()
+DeviceTracker::getInversionFlags()
 {
     return m_invertFlags;
 }
 
 void
-TrackingController::setInversionFlags(unsigned char f)
+DeviceTracker::setInversionFlags(unsigned char f)
 {
     m_invertFlags = f;
 }
diff --git a/Base/SceneElements/Controllers/imstkTrackingController.h b/Base/SceneElements/Controllers/imstkDeviceTracker.h
similarity index 69%
rename from Base/SceneElements/Controllers/imstkTrackingController.h
rename to Base/SceneElements/Controllers/imstkDeviceTracker.h
index a68221a314f080cd61b518e0148d0e2e6bc3fa73..c6734321ec3f672e4e0b122aa28e93b32409255f 100644
--- a/Base/SceneElements/Controllers/imstkTrackingController.h
+++ b/Base/SceneElements/Controllers/imstkDeviceTracker.h
@@ -19,8 +19,8 @@
 
    =========================================================================*/
 
-#ifndef imstkTrackingController_h
-#define imstkTrackingController_h
+#ifndef imstkDeviceTracker_h
+#define imstkDeviceTracker_h
 
 #include <memory>
 
@@ -32,11 +32,11 @@ namespace imstk
 {
 
 ///
-/// \class TrackingController
+/// \class DeviceTracker
 ///
 /// \brief This class reports external device's position and orientation with a given offset
 ///
-class TrackingController
+class DeviceTracker
 {
 public:
 
@@ -45,20 +45,26 @@ public:
         transX = 0x01,
         transY = 0x02,
         transZ = 0x04,
-        rotX   = 0x08,
-        rotY   = 0x10,
-        rotZ   = 0x20
+        rotX = 0x08,
+        rotY = 0x10,
+        rotZ = 0x20
     };
 
+    ///
+    /// \brief Constructor
+    ///
+    DeviceTracker(std::shared_ptr<DeviceClient> deviceClient) :
+        m_deviceClient(deviceClient) {}
+
     ///
     /// \brief Destructor
     ///
-    ~TrackingController() = default;
+    ~DeviceTracker() = default;
 
     ///
     /// \brief Compute the world position and orientation
     ///
-    bool computeTrackingData(Vec3d& p, Quatd& r);
+    bool updateTrackingData();
 
     ///
     /// \brief Get/Set the device client
@@ -90,21 +96,40 @@ public:
     unsigned char getInversionFlags();
     void setInversionFlags(unsigned char f);
 
-protected:
     ///
-    /// \brief Constructor
+    /// \brief Sets the tracking data to be out of date or up to date
+    ///
+    void setTrackerToOutOfDate() { m_trackingDataUptoDate = false; }
+    void setTrackerToUpToDate() { m_trackingDataUptoDate = true; }
+
+    ///
+    /// \brief Returns true if the tracking data is already updated in current frame. Else, false.
     ///
-    TrackingController(std::shared_ptr<DeviceClient> deviceClient) :
-        m_deviceClient(deviceClient)
-    {}
+    bool isTrackerUpToDate() const { return m_trackingDataUptoDate; }
+
+    ///
+    /// \brief Get the latest position
+    ///
+    const Vec3d& getPosition(){ return m_currentPos; };
+
+    ///
+    /// \brief Get the latest rotation
+    ///
+    const Quatd& getRotation() { return m_currentRot; };
+
+protected:
 
     std::shared_ptr<DeviceClient> m_deviceClient; ///< Reports device tracking information
     double m_scaling;                             ///< Scaling factor for physical to virtual translations
     Vec3d m_translationOffset = WORLD_ORIGIN;     ///< Translation concatenated to the device translation
     Quatd m_rotationOffset = Quatd::Identity();   ///< Rotation concatenated to the device rotation
-    unsigned char m_invertFlags = 0x00;           ///< Invert flags to be masked with TrackingController::InvertFlag
+    unsigned char m_invertFlags = 0x00;           ///< Invert flags to be masked with DeviceTracker::InvertFlag
+
+    Vec3d m_currentPos;
+    Quatd m_currentRot;
+    bool m_trackingDataUptoDate = false;
 };
 
 } // imstk
 
-#endif // ifndef imstkTrackingController_h
+#endif // ifndef imstkDeviceTracker_h
diff --git a/Base/SceneElements/Controllers/imstkLaparoscopicToolController.cpp b/Base/SceneElements/Controllers/imstkLaparoscopicToolController.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..53d6e6d737ba4d615f154f9a7dcd45b9e71f49ea
--- /dev/null
+++ b/Base/SceneElements/Controllers/imstkLaparoscopicToolController.cpp
@@ -0,0 +1,111 @@
+/*=========================================================================
+
+   Library: iMSTK
+
+   Copyright (c) Kitware, Inc. & Center for Modeling, Simulation,
+   & Imaging in Medicine, Rensselaer Polytechnic Institute.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+   =========================================================================*/
+
+#include "imstkLaparoscopicToolController.h"
+
+#include "imstkCollidingObject.h"
+#include "imstkGeometry.h"
+
+#include <utility>
+
+#include <g3log/g3log.hpp>
+
+namespace imstk
+{
+
+void
+LaparoscopicToolController::initOffsets()
+{
+    m_trackingController->setTranslationOffset(m_shaft->getMasterGeometry()->getPosition());
+    m_trackingController->setRotationOffset(m_shaft->getMasterGeometry()->getOrientation());
+
+    m_trackingController->getDeviceClient()->setButtonsEnabled(true);
+}
+
+void
+LaparoscopicToolController::updateControlledObjects()
+{
+    if (!m_trackingController->isTrackerUpToDate())
+    {
+        if (!m_trackingController->updateTrackingData())
+        {
+            LOG(WARNING) << "LaparoscopicToolController::updateControlledObjects warning: could not update tracking info.";
+            return;
+        }
+    }
+
+    Vec3d p = m_trackingController->getPosition();
+    Quatd r = m_trackingController->getRotation();
+
+    // Update jaw angles
+    if (m_trackingController->getDeviceClient()->getButton(0))
+    {
+        m_jawAngle += m_change;
+        m_jawAngle = (m_jawAngle > m_maxJawAngle) ? m_maxJawAngle : m_jawAngle;
+    }
+
+    if (m_trackingController->getDeviceClient()->getButton(1))
+    {
+        m_jawAngle -= m_change;
+        m_jawAngle = (m_jawAngle < 0.0) ? 0.0 : m_jawAngle;
+    }
+
+    // Update orientation of parts
+    Quatd jawRotUpper;
+    jawRotUpper = r*Rotd(m_jawAngle, m_jawRotationAxis);
+    m_upperJaw->getMasterGeometry()->setOrientation(jawRotUpper);
+
+    Quatd jawRotLower;
+    jawRotLower = r*Rotd(-m_jawAngle, m_jawRotationAxis);
+    m_lowerJaw->getMasterGeometry()->setOrientation(jawRotLower);
+
+    m_shaft->getMasterGeometry()->setOrientation(r);
+
+    // Update positions of parts
+    m_shaft->getMasterGeometry()->setPosition(p);
+    m_upperJaw->getMasterGeometry()->setPosition(p);
+    m_lowerJaw->getMasterGeometry()->setPosition(p);
+}
+
+void
+LaparoscopicToolController::applyForces()
+{
+    Vec3d force(0, 0, 0);
+
+    if (auto collidingObject = dynamic_cast<CollidingObject*>(m_shaft.get()))
+    {
+        force += collidingObject->getForce();
+    }
+
+    if (auto collidingObject = dynamic_cast<CollidingObject*>(m_upperJaw.get()))
+    {
+        force += collidingObject->getForce();
+    }
+
+    if (auto collidingObject = dynamic_cast<CollidingObject*>(m_lowerJaw.get()))
+    {
+        force += collidingObject->getForce();
+    }
+
+    m_trackingController->getDeviceClient()->setForce(force);
+}
+
+} // imstk
diff --git a/Base/SceneElements/Controllers/imstkLaparoscopicToolController.h b/Base/SceneElements/Controllers/imstkLaparoscopicToolController.h
new file mode 100644
index 0000000000000000000000000000000000000000..ac9ad4ac565a13b8db7220cda19644105b03c8ea
--- /dev/null
+++ b/Base/SceneElements/Controllers/imstkLaparoscopicToolController.h
@@ -0,0 +1,134 @@
+/*=========================================================================
+
+   Library: iMSTK
+
+   Copyright (c) Kitware, Inc. & Center for Modeling, Simulation,
+   & Imaging in Medicine, Rensselaer Polytechnic Institute.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+   =========================================================================*/
+
+#ifndef imstkLaparoscopicToolController_h
+#define imstkLaparoscopicToolController_h
+
+#include "imstkSceneObjectControllerBase.h"
+#include "imstkDeviceTracker.h"
+#include "imstkSceneObject.h"
+#include "imstkMath.h"
+
+#include <memory>
+
+namespace imstk
+{
+
+///
+/// \class LaparoscopicTool
+///
+/// \brief Two-jawed laparoscopic tool controlled by external device
+/// The tool is composed of three scene objects: pivot, lower jaw and upper jaw
+/// The jaws open-close based on the buttons at present.
+/// This has to be replaced by potentiometer tracking in future.
+///
+class LaparoscopicToolController : public SceneObjectControllerBase
+{
+public:
+    ///
+    /// \brief Constructor
+    ///
+    LaparoscopicToolController(
+        std::shared_ptr<SceneObject> shaft,
+        std::shared_ptr<SceneObject> upperJaw,
+        std::shared_ptr<SceneObject> lowerJaw,
+        std::shared_ptr<DeviceTracker> trackingController) :
+        m_trackingController(trackingController),
+        m_shaft(shaft),
+        m_upperJaw(upperJaw),
+        m_lowerJaw(lowerJaw) {}
+
+    LaparoscopicToolController() = delete; //not allowed for now
+
+    ///
+    /// \brief Destructor
+    ///
+    ~LaparoscopicToolController() = default;
+
+    ///
+    /// \brief Initialize offset based on object geometry
+    ///
+    void initOffsets() override;
+
+    ///
+    /// \brief Update controlled laparoscopic tool using latest tracking information
+    ///
+    void updateControlledObjects() override;
+
+    ///
+    /// \brief Apply forces to the haptic device
+    ///
+    void applyForces() override;
+
+    ///
+    /// \brief Set the tracker to out-of-date
+    ///
+    inline void setTrackerToOutOfDate() override { m_trackingController->setTrackerToOutOfDate(); }
+
+    ///
+    /// \brief Set the maximum jaw angle
+    ///
+    inline void setMaxJawAngle(const double maxAngle) { m_maxJawAngle = maxAngle; }
+
+    ///
+    /// \brief Set the increment
+    ///
+    inline void setJawAngleChange(const double dAngle) { m_change = dAngle; }
+
+    ///
+    /// \brief Set the jaw rotation axis
+    ///
+    inline void setJawRotationAxis(const Vec3d& axis) { m_jawRotationAxis = axis; }
+
+    ///
+    /// \brief Get the current jaw angle
+    ///
+    inline double getJawAngle() const { return m_jawAngle; }
+
+    ///
+    /// \brief Get the max jaw angle
+    ///
+    inline double getMaxJawAngle() const { return m_maxJawAngle; }
+
+    ///
+    /// \brief Get/Set tracking controller
+    ///
+    inline std::shared_ptr<DeviceTracker> getTrackingController() const { return m_trackingController; }
+    inline void setTrackingController(std::shared_ptr<DeviceTracker> controller) { m_trackingController = controller; }
+
+protected:
+
+    std::shared_ptr<DeviceTracker> m_trackingController; ///< Device tracker
+
+    std::shared_ptr<SceneObject> m_shaft;    ///< Tool shaft
+    std::shared_ptr<SceneObject> m_upperJaw; ///< Tool upper jaw
+    std::shared_ptr<SceneObject> m_lowerJaw; ///< Tool lower jaw
+
+    double m_jawAngle = PI / 6.0;       ///< Angle of the jaws
+    double m_change = 6.0e-5;           ///< Amount of change in jaw angle per frame
+    double m_maxJawAngle = PI / 6.0;    ///< Maximum angle of the jaws
+
+    Vec3d m_jawRotationAxis = Vec3d(0, 1., 0);  ///< Angle of the jaws
+};
+
+} // imstk
+
+#endif // ifndef imstkLaparoscopicToolController_h
diff --git a/Base/SceneElements/Controllers/imstkSceneObjectController.cpp b/Base/SceneElements/Controllers/imstkSceneObjectController.cpp
index c4ed02636e1caa957d848e5b30953b9498d80ff5..2053c57c50ea7c7d12f3b0bdb253d64b8d3b1842 100644
--- a/Base/SceneElements/Controllers/imstkSceneObjectController.cpp
+++ b/Base/SceneElements/Controllers/imstkSceneObjectController.cpp
@@ -34,33 +34,33 @@ namespace imstk
 void
 SceneObjectController::initOffsets()
 {
-    m_translationOffset = m_sceneObject.getMasterGeometry()->getPosition();
-    m_rotationOffset = m_sceneObject.getMasterGeometry()->getOrientation();
+    m_trackingController->setTranslationOffset(m_sceneObject->getMasterGeometry()->getPosition());
+    m_trackingController->setRotationOffset(m_sceneObject->getMasterGeometry()->getOrientation());
 }
 
 void
-SceneObjectController::updateFromDevice()
+SceneObjectController::updateControlledObjects()
 {
-    Vec3d p;
-    Quatd r;
-
-    if (!this->computeTrackingData(p, r))
+    if (!m_trackingController->isTrackerUpToDate())
     {
-        LOG(WARNING) << "SceneObjectController::updateFromDevice warning: could not update tracking info.";
-        return;
+        if (!m_trackingController->updateTrackingData())
+        {
+            LOG(WARNING) << "SceneObjectController::updateControlledObjects warning: could not update tracking info.";
+            return;
+        }
     }
 
     // Update colliding geometry
-    m_sceneObject.getMasterGeometry()->setPosition(p);
-    m_sceneObject.getMasterGeometry()->setOrientation(r);
+    m_sceneObject->getMasterGeometry()->setPosition(m_trackingController->getPosition());
+    m_sceneObject->getMasterGeometry()->setOrientation(m_trackingController->getRotation());
 }
 
 void
 SceneObjectController::applyForces()
 {
-    if(auto collidingObject = dynamic_cast<CollidingObject*>(&m_sceneObject))
+    if(auto collidingObject = dynamic_cast<CollidingObject*>(m_sceneObject.get()))
     {
-        m_deviceClient->setForce(collidingObject->getForce());
+        m_trackingController->getDeviceClient()->setForce(collidingObject->getForce());
     }
 }
 
diff --git a/Base/SceneElements/Controllers/imstkSceneObjectController.h b/Base/SceneElements/Controllers/imstkSceneObjectController.h
index 2563c4292ba1e508fc03112be3eb69fd9a731247..2357d7e322622b8121352bb897276cf58513cedd 100644
--- a/Base/SceneElements/Controllers/imstkSceneObjectController.h
+++ b/Base/SceneElements/Controllers/imstkSceneObjectController.h
@@ -22,7 +22,8 @@
 #ifndef imstkSceneObjectController_h
 #define imstkSceneObjectController_h
 
-#include "imstkTrackingController.h"
+#include "imstkSceneObjectControllerBase.h"
+#include "imstkDeviceTracker.h"
 #include "imstkSceneObject.h"
 
 #include <memory>
@@ -33,19 +34,18 @@ namespace imstk
 ///
 /// \class SceneObjectController
 ///
-/// \brief
+/// \brief This class implements once tracking controller controlling one scnene object
 ///
-class SceneObjectController : public TrackingController
+class SceneObjectController : public SceneObjectControllerBase
 {
 public:
     ///
     /// \brief Constructor
     ///
-    SceneObjectController(SceneObject& sceneObject,
-                          std::shared_ptr<DeviceClient> deviceClient) :
-        TrackingController(deviceClient),
-        m_sceneObject(sceneObject)
-    {}
+    SceneObjectController(std::shared_ptr<SceneObject> sceneObject, std::shared_ptr<DeviceTracker> trackingController) :
+        m_trackingController(trackingController), m_sceneObject(sceneObject) {}
+
+    SceneObjectController() = delete;
 
     ///
     /// \brief Destructor
@@ -55,22 +55,38 @@ public:
     ///
     /// \brief Initialize offset based on object geometry
     ///
-    void initOffsets();
+    void initOffsets() override;
 
     ///
-    /// \brief Update geometries transformations
+    /// \brief Update controlled scene object using latest tracking information
     ///
-    void updateFromDevice();
+    void updateControlledObjects() override;
 
     ///
     /// \brief Apply forces to the haptic device
     ///
-    void applyForces();
+    void applyForces() override;
 
-protected:
+    ///
+    /// \brief Sets the tracker to out-of-date
+    ///
+    inline void setTrackerToOutOfDate() override { m_trackingController->setTrackerToOutOfDate(); }
+
+    ///
+    /// \brief Get/Set controlled scene object
+    ///
+    inline std::shared_ptr<SceneObject> getControlledSceneObject() const { return m_sceneObject; }
+    inline void setControlledSceneObject(std::shared_ptr<SceneObject> so) { m_sceneObject = so; }
 
-    SceneObject& m_sceneObject; ///< SceneObject controlled by the external device
+    ///
+    /// \brief Get/Set tracking controller
+    ///
+    inline std::shared_ptr<DeviceTracker> getTrackingController() const { return m_trackingController; }
+    inline void setTrackingController(std::shared_ptr<DeviceTracker> controller) { m_trackingController = controller; }
 
+protected:
+    std::shared_ptr<DeviceTracker> m_trackingController; ///< Device tracker
+    std::shared_ptr<SceneObject> m_sceneObject;          ///< SceneObject controlled by the Tracker
 };
 
 } // imstk
diff --git a/Base/SceneElements/Controllers/imstkSceneObjectControllerBase.h b/Base/SceneElements/Controllers/imstkSceneObjectControllerBase.h
new file mode 100644
index 0000000000000000000000000000000000000000..4fc3d6740e9311c6bc0a53e9d976c5a270e14840
--- /dev/null
+++ b/Base/SceneElements/Controllers/imstkSceneObjectControllerBase.h
@@ -0,0 +1,65 @@
+/*=========================================================================
+
+   Library: iMSTK
+
+   Copyright (c) Kitware, Inc. & Center for Modeling, Simulation,
+   & Imaging in Medicine, Rensselaer Polytechnic Institute.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+   =========================================================================*/
+
+#ifndef imstkSceneObjectControllerBase_h
+#define imstkSceneObjectControllerBase_h
+
+namespace imstk
+{
+
+///
+/// \class SceneObjectControllerBase
+///
+/// \brief Base class for all the scene object controllers
+///
+class SceneObjectControllerBase
+{
+public:
+    ///
+    /// \brief Constructor/Destructor
+    ///
+    SceneObjectControllerBase() = default;
+    ~SceneObjectControllerBase() = default;
+
+    ///
+    /// \brief Initialize offset based on object geometry
+    ///
+    virtual void initOffsets() = 0;
+
+    ///
+    /// \brief Update controlled scene objects using latest tracking information
+    ///
+    virtual void updateControlledObjects() = 0;
+
+    ///
+    /// \brief Apply forces to the haptic device
+    ///
+    virtual void applyForces() = 0;
+
+    ///
+    /// \brief
+    ///
+    virtual void setTrackerToOutOfDate() = 0;
+};
+
+} // imstk
+
+#endif // ifndef imstkSceneObjectControllerBase_h
\ No newline at end of file
diff --git a/Base/SceneElements/Objects/imstkSceneObject.cpp b/Base/SceneElements/Objects/imstkSceneObject.cpp
index e25bea4d995f0cc9a4661c9fd742035034646b70..8ab778dc4a200747969113150973096ecf8d32ca 100644
--- a/Base/SceneElements/Objects/imstkSceneObject.cpp
+++ b/Base/SceneElements/Objects/imstkSceneObject.cpp
@@ -69,24 +69,4 @@ SceneObject::setName(const std::string& name)
     m_name = name;
 }
 
-std::shared_ptr<SceneObjectController>
-SceneObject::getController() const
-{
-    return m_controller;
-}
-
-std::shared_ptr<SceneObjectController>
-SceneObject::setupController(std::shared_ptr<DeviceClient> deviceClient)
-{
-    if(m_controller == nullptr)
-    {
-        m_controller = std::make_shared<SceneObjectController>(*this, deviceClient);
-    }
-    else
-    {
-        m_controller->setDeviceClient(deviceClient);
-    }
-    return m_controller;
-}
-
 } // imstk
diff --git a/Base/SceneElements/Objects/imstkSceneObject.h b/Base/SceneElements/Objects/imstkSceneObject.h
index 38045907bad200c5b6056d7420d8bc6075bab324..229b1356c4b16eac5897c233df5333f2799f00cd 100644
--- a/Base/SceneElements/Objects/imstkSceneObject.h
+++ b/Base/SceneElements/Objects/imstkSceneObject.h
@@ -29,7 +29,6 @@ namespace imstk
 {
 
 class Geometry;
-class SceneObjectController;
 class DeviceClient;
 
 ///
@@ -85,16 +84,6 @@ public:
     ///
     virtual std::shared_ptr<Geometry> getMasterGeometry() const;
 
-    ///
-    /// \brief Get the object controller
-    ///
-    std::shared_ptr<SceneObjectController> getController() const;
-
-    ///
-    /// \brief Setup a controller for the object for a given device client
-    ///
-    std::shared_ptr<SceneObjectController> setupController(std::shared_ptr<DeviceClient> deviceClient);
-
     ///
     /// \brief
     ///
@@ -109,7 +98,6 @@ protected:
     Type m_type; ///> Type of the scene object
     std::string m_name; ///> Custom name of the scene object
     std::shared_ptr<Geometry> m_visualGeometry; ///> Geometry for rendering
-    std::shared_ptr<SceneObjectController> m_controller; ///> Object controller
  };
 
 using VisualObject = SceneObject;
diff --git a/Base/SceneElements/Objects/imstkVirtualCouplingPBDObject.cpp b/Base/SceneElements/Objects/imstkVirtualCouplingPBDObject.cpp
index b1f2f8f2a7e1be1b10aaaef25c81ee7e647ea23b..eb57f013c7b9b2d0c25570880fe8c32e9586bcd4 100644
--- a/Base/SceneElements/Objects/imstkVirtualCouplingPBDObject.cpp
+++ b/Base/SceneElements/Objects/imstkVirtualCouplingPBDObject.cpp
@@ -40,45 +40,49 @@ VirtualCouplingPBDObject::initOffsets()
 void
 VirtualCouplingPBDObject::updateFromDevice()
 {
-    Vec3d p;
-    Quatd r;
-    if (!this->computeTrackingData(p, r))
+    if (!m_trackingDataUptoDate)
     {
-        LOG(WARNING) << "VirtualCouplingPBDObject::updateFromDevice warning: could not update tracking info.";
-        return;
+        if (!updateTrackingData())
+        {
+            LOG(WARNING) << "VirtualCouplingPBDObject::updateFromDevice warning: could not update tracking info.";
+            return;
+        }
     }
 
+    Vec3d p = getPosition();
+    Quatd r = getRotation();
+
     // Update colliding geometry
     m_visualGeometry->setPosition(p);
     m_visualGeometry->setOrientation(r);
 
     computeTransform(p, r, transform);
 
-   auto collidingMesh = std::dynamic_pointer_cast<Mesh>(m_collidingGeometry);
+    auto collidingMesh = std::dynamic_pointer_cast<Mesh>(m_collidingGeometry);
 
-   Vec4d vertexPos4;
-   vertexPos4.w() = 1;
-   Vec3d vertexPos3;
+    Vec4d vertexPos4;
+    vertexPos4.w() = 1;
+    Vec3d vertexPos3;
 
     for (int i = 0; i < collidingMesh->getNumVertices(); ++i)
-   {
-       vertexPos3 = collidingMesh->getVertexPosition(i);
-       vertexPos4.x() = vertexPos3.x();
-       vertexPos4.y() = vertexPos3.y();
-       vertexPos4.z() = vertexPos3.z();
-
-       vertexPos4.applyOnTheLeft(transform);
-       vertexPos3.x() = vertexPos4.x();
-       vertexPos3.y() = vertexPos4.y();
-       vertexPos3.z() = vertexPos4.z();
-
-       collidingMesh->setVerticePosition(i, vertexPos3);
+    {
+        vertexPos3 = collidingMesh->getVertexPosition(i);
+        vertexPos4.x() = vertexPos3.x();
+        vertexPos4.y() = vertexPos3.y();
+        vertexPos4.z() = vertexPos3.z();
+
+        vertexPos4.applyOnTheLeft(transform);
+        vertexPos3.x() = vertexPos4.x();
+        vertexPos3.y() = vertexPos4.y();
+        vertexPos3.z() = vertexPos4.z();
+
+        collidingMesh->setVerticePosition(i, vertexPos3);
     }
     applyCollidingToPhysics();
     updatePbdStates();
 }
 
-void 
+void
 VirtualCouplingPBDObject::computeTransform(Vec3d& pos, Quatd& quat, Eigen::Matrix4d& t){
     auto scaling = m_collidingGeometry->getScaling();
     auto angleAxis = Rotd(quat);
diff --git a/Base/SceneElements/Objects/imstkVirtualCouplingPBDObject.h b/Base/SceneElements/Objects/imstkVirtualCouplingPBDObject.h
index b959cdb51cf8f803bb72db832b59bd1852ad1f07..087cd90ff6fc4e57a4ed9180af0b94a9f8d050b5 100644
--- a/Base/SceneElements/Objects/imstkVirtualCouplingPBDObject.h
+++ b/Base/SceneElements/Objects/imstkVirtualCouplingPBDObject.h
@@ -22,7 +22,7 @@
 #ifndef imstkVirtualCouplingPBDObject_h
 #define imstkVirtualCouplingPBDObject_h
 
-#include "imstkTrackingController.h"
+#include "imstkDeviceTracker.h"
 #include "imstkPbdRigidObject.h"
 namespace imstk
 {
@@ -35,7 +35,7 @@ class GeometryMap;
 ///
 /// \brief
 ///
-class VirtualCouplingPBDObject : public TrackingController, public PbdRigidObject
+class VirtualCouplingPBDObject : public DeviceTracker, public PbdRigidObject
 {
 public:
     ///
@@ -43,7 +43,7 @@ public:
     ///
     VirtualCouplingPBDObject(std::string name,
                              std::shared_ptr<DeviceClient> deviceClient) :
-        TrackingController(deviceClient),
+        DeviceTracker(deviceClient),
         PbdRigidObject(name)
     {
     }
@@ -96,6 +96,11 @@ public:
 
     void applyCollidingToPhysics();
 
+    ///
+    /// \brief Set the virtual coupling tracker to out-of-date
+    ///
+    void setTrackerToOutOfDate() { DeviceTracker::setTrackerToOutOfDate(); };
+
 protected:
 
     bool m_forceModified;
diff --git a/Base/SimulationManager/imstkSceneManager.cpp b/Base/SimulationManager/imstkSceneManager.cpp
index 94a5ce8da9353da6714c8e8b0eb0470c5dbf7177..f8f7f182b2c5e6eb2505f7a0b6d7b82f88fad40e 100644
--- a/Base/SimulationManager/imstkSceneManager.cpp
+++ b/Base/SimulationManager/imstkSceneManager.cpp
@@ -49,13 +49,15 @@ SceneManager::initModule()
         this->startModuleInNewThread(camController);
     }
 
+    // Update objects controlled by the device controllers
+    for (auto controller : m_scene->getSceneObjectControllers())
+    {
+        controller->initOffsets();
+    }
+
     // Init virtual coupling objects offsets
     for (auto obj : m_scene->getSceneObjects())
     {
-        if (auto controller = obj->getController())
-        {
-            controller->initOffsets();
-        }
         if (auto virtualCouplingPBD = std::dynamic_pointer_cast<VirtualCouplingPBDObject>(obj))
         {
             virtualCouplingPBD->initOffsets();
@@ -66,27 +68,26 @@ SceneManager::initModule()
 void
 SceneManager::runModule()
 {
-    // Update virtualCoupling objects based on devices
+    // Reset Colliding Geometry so that the transform obtained from device can be applied
     for (auto obj : m_scene->getSceneObjects())
     {
-        if (auto controller = obj->getController())
+        if (auto collidingObj = std::dynamic_pointer_cast<CollidingObject>(obj))
         {
-            controller->updateFromDevice();
-            if (auto collidingObj = std::dynamic_pointer_cast<CollidingObject>(obj))
-            {
-                controller->applyForces();
-                collidingObj->setForce(Vec3d(0,0,0));
-            }
+            collidingObj->setForce(Vec3d(0,0,0));
         }
-        else if (auto virtualCouplingPBD = std::dynamic_pointer_cast<VirtualCouplingPBDObject>(obj))
+        if (auto virtualCouplingPBD = std::dynamic_pointer_cast<VirtualCouplingPBDObject>(obj))
         {
-            // reset Colliding Geometry so that the transform obtained from device can be applied
             virtualCouplingPBD->resetCollidingGeometry();
-            virtualCouplingPBD->updateFromDevice();
-            virtualCouplingPBD->applyForces();
         }
     }
 
+    // Update objects controlled by the device controllers
+    for (auto controller : m_scene->getSceneObjectControllers())
+    {
+        controller->updateControlledObjects();
+        controller->applyForces();
+    }
+
     // Compute collision data per interaction pair
     for (auto intPair : m_scene->getCollisionGraph()->getInteractionPairList())
     {
@@ -118,6 +119,21 @@ SceneManager::runModule()
         }
         intPair->resolveCollision();
     }
+
+    // Set the trackers of virtual coupling PBD objects to out-of-date
+    for (auto obj : m_scene->getSceneObjects())
+    {
+        if (auto virtualCouplingPBD = std::dynamic_pointer_cast<VirtualCouplingPBDObject>(obj))
+        {
+            virtualCouplingPBD->setTrackerToOutOfDate();
+        }
+    }
+
+    // Set the trackers of the scene object controllers to out-of-date
+    for (auto controller : m_scene->getSceneObjectControllers())
+    {
+        controller->setTrackerToOutOfDate();
+    }
 }
 
 void
diff --git a/Base/SimulationManager/imstkSimulationManager.cpp b/Base/SimulationManager/imstkSimulationManager.cpp
index a03cd9624882177bc787e4f686c07a1eea4491e4..e5829d37c88805dd21bfef3e821175e28d9b6504 100644
--- a/Base/SimulationManager/imstkSimulationManager.cpp
+++ b/Base/SimulationManager/imstkSimulationManager.cpp
@@ -170,6 +170,12 @@ SimulationManager::getViewer() const
     return m_viewer;
 }
 
+void
+SimulationManager::setCurrentScene(std::shared_ptr<Scene> scene, bool unloadCurrentScene)
+{
+    setCurrentScene(scene->getName(), unloadCurrentScene);
+}
+
 void
 SimulationManager::setCurrentScene(std::string newSceneName, bool unloadCurrentScene)
 {
diff --git a/Base/SimulationManager/imstkSimulationManager.h b/Base/SimulationManager/imstkSimulationManager.h
index 413a48c158d7167de5a3ec8b26eeca83cb70ca40..e8edc79f391cbc8b5141682e59e006d7ecb862bf 100644
--- a/Base/SimulationManager/imstkSimulationManager.h
+++ b/Base/SimulationManager/imstkSimulationManager.h
@@ -133,6 +133,7 @@ public:
     /// \brief
     ///
     void setCurrentScene(std::string newSceneName, bool unloadCurrentScene = false);
+    void setCurrentScene(std::shared_ptr<Scene> scene, bool unloadCurrentScene = false);
 
     ///
     /// \brief
diff --git a/Examples/Sandbox/main.cpp b/Examples/Sandbox/main.cpp
index 0bc71fbf42027b5eb3e5da32b13ed305e21bd941..df023ae1b8f4fc046821f7ba549bfe56c296c91f 100644
--- a/Examples/Sandbox/main.cpp
+++ b/Examples/Sandbox/main.cpp
@@ -53,6 +53,7 @@
 #include "imstkVRPNDeviceServer.h"
 #include "imstkCameraController.h"
 #include "imstkSceneObjectController.h"
+#include "imstkLaparoscopicToolController.h"
 
 // Collisions
 #include "imstkInteractionPair.h"
@@ -102,6 +103,7 @@ void testPbdCloth();
 void testPbdCollision();
 void testLineMesh();
 void testMshAndVegaIO();
+void testLapToolController();
 
 int main()
 {
@@ -117,35 +119,133 @@ int main()
         << "Starting Sandbox\n"
         << "****************\n";
 
+    /*------------------
+    Test rendering
+    ------------------*/
     //testMultiTextures();
+    //testVTKTexture();
+    //testMultiObjectWithTextures();
+    //testViewer();
+
+
+    /*------------------
+    Test CD and CR
+    ------------------*/
     //testMeshCCD();
     //testPenaltyRigidCollision();
-    //testTwoFalcons();
-    //testObjectController();
-    //testCameraController();
-    //testViewer();
-    //testReadMesh();
-    //testAnalyticalGeometry();
-    //testScenesManagement();
+
+
+    /*------------------
+    Test geometry, maps
+    ------------------*/
     //testIsometricMap();
     //testTetraTriangleMap();
     //testExtractSurfaceMesh();
     //testOneToOneNodalMap();
     //testSurfaceMeshOptimizer();
-    testDeformableBody();
-    //testVTKTexture();
-    //testMultiObjectWithTextures();
-    //testTwoOmnis();
-    //testVectorPlotters();
+    //testAnalyticalGeometry();
+
+
+    /*------------------
+    Test physics
+    ------------------*/
     //testPbdVolume();
     //testPbdCloth();
     //testPbdCollision();
+    //testDeformableBody();
+
+
+    /*------------------
+    Test mesh I/O
+    ------------------*/
     //testLineMesh();
     //testMshAndVegaIO();
+    //testReadMesh();
+
+
+    /*------------------
+    Test devices, controllers
+    ------------------*/
+    //testObjectController();
+    //testTwoFalcons();
+    //testCameraController();
+    //testTwoOmnis();
+    testLapToolController();
+
+
+    /*------------------
+    Test Misc.
+    ------------------*/
+    //testScenesManagement();
+    //testVectorPlotters();
+
 
     return 0;
 }
 
+void testLapToolController()
+{
+
+#ifdef iMSTK_USE_OPENHAPTICS
+    // SDK and Scene
+    auto sdk = std::make_shared<imstk::SimulationManager>();
+    auto scene = sdk->createNewScene("TestLapToolController");
+
+    // Device clients
+    auto client0 = std::make_shared<imstk::HDAPIDeviceClient>("PHANToM 1");
+
+    // Device Server
+    auto server = std::make_shared<imstk::HDAPIDeviceServer>();
+    server->addDeviceClient(client0);
+    sdk->addModule(server);
+
+    // Plane
+    auto planeGeom = std::make_shared<imstk::Plane>();
+    planeGeom->scale(100);
+    planeGeom->translate(Vec3d(0., -20., 0.));
+    auto planeObj = std::make_shared<imstk::VisualObject>("VisualPlane");
+    planeObj->setVisualGeometry(planeGeom);
+    scene->addSceneObject(planeObj);
+
+    auto createAndAddVisualSceneObject =
+        [](std::shared_ptr<imstk::Scene> scene,
+        const std::string& fileName,
+        const std::string& objectName) ->
+        std::shared_ptr<imstk::SceneObject>
+    {
+        auto mesh = imstk::MeshIO::read(fileName);
+        auto SurfaceMesh = std::dynamic_pointer_cast<imstk::SurfaceMesh>(mesh);
+
+        // Create object and add to scene
+        auto meshObject = std::make_shared<imstk::VisualObject>("meshObject");
+        meshObject->setVisualGeometry(SurfaceMesh);
+        meshObject->setName(objectName);
+        scene->addSceneObject(meshObject);
+        return meshObject;
+    };
+
+    // laparoscopic tool
+    auto pivot = createAndAddVisualSceneObject(scene, DATA_ROOT_PATH"/laptool/pivot.obj", "pivot");
+    auto upperJaw = createAndAddVisualSceneObject(scene, DATA_ROOT_PATH"/laptool/upper.obj", "upperJaw");
+    auto lowerJaw = createAndAddVisualSceneObject(scene, DATA_ROOT_PATH"/laptool/lower.obj", "lowerJaw");
+
+    auto trackingCtrl = std::make_shared<imstk::DeviceTracker>(client0);
+    trackingCtrl->setTranslationScaling(0.5);
+    auto lapToolController = std::make_shared<imstk::LaparoscopicToolController>(pivot, upperJaw, lowerJaw, trackingCtrl);
+    lapToolController->setJawRotationAxis(imstk::Vec3d(1.0, 0, 0));
+    scene->addObjectController(lapToolController);
+
+    // Set Camera
+    auto cam = scene->getCamera();
+    cam->setPosition(imstk::Vec3d(0, 30, 60));
+    cam->setFocalPoint(imstk::Vec3d(0, 0, 0));
+
+    // Run
+    sdk->setCurrentScene(scene);
+    sdk->startSimulation(true);
+#endif
+}
+
 void testMshAndVegaIO()
 {
     // SDK and Scene
@@ -201,7 +301,7 @@ void testMshAndVegaIO()
     scene->addSceneObject(objectB);
 
     // Run
-    sdk->setCurrentScene("SceneTestMesh");
+    sdk->setCurrentScene(scene);
     sdk->startSimulation(true);
 
 }
@@ -313,7 +413,7 @@ void testMultiObjectWithTextures()
         scene->addSceneObject(object1);
     }
     // Run
-    sdk->setCurrentScene("multiObjectWithTexturesTest");
+    sdk->setCurrentScene(scene);
     sdk->startSimulation(true);
 }
 
@@ -338,7 +438,7 @@ void testMultiTextures()
     scene->addSceneObject(object);
 
     // Run
-    sdk->setCurrentScene("multitexturestest");
+    sdk->setCurrentScene(scene);
     sdk->startSimulation(true);
 }
 
@@ -384,7 +484,7 @@ void testMeshCCD()
     });
 
     // Run
-    sdk->setCurrentScene("MeshCCDTest");
+    sdk->setCurrentScene(scene);
     sdk->startSimulation(true);
     t.join();
 }
@@ -424,10 +524,13 @@ void testPenaltyRigidCollision()
     auto sphere0Obj = std::make_shared<imstk::CollidingObject>("Sphere0");
     sphere0Obj->setVisualGeometry(sphere0Geom);
     sphere0Obj->setCollidingGeometry(sphere0Geom);
-    auto sphere0Controller = sphere0Obj->setupController(client0);
-    sphere0Controller->setTranslationScaling(40);
     scene->addSceneObject(sphere0Obj);
 
+    auto trackCtrl0 = std::make_shared<imstk::DeviceTracker>(client0);
+    trackCtrl0->setTranslationScaling(40);
+    auto sphere0Controller = std::make_shared<imstk::SceneObjectController>(sphere0Obj, trackCtrl0);
+    scene->addObjectController(sphere0Controller);
+
     // Sphere1
     auto sphere1Geom = std::make_shared<Sphere>();
     sphere1Geom->scale(0.5);
@@ -435,10 +538,13 @@ void testPenaltyRigidCollision()
     auto sphere1Obj = std::make_shared<imstk::CollidingObject>("Sphere1");
     sphere1Obj->setVisualGeometry(sphere1Geom);
     sphere1Obj->setCollidingGeometry(sphere1Geom);
-    auto sphere1Controller = sphere1Obj->setupController(client1);
-    sphere1Controller->setTranslationScaling(40);
     scene->addSceneObject(sphere1Obj);
 
+    auto trackCtrl1 = std::make_shared<imstk::DeviceTracker>(client1);
+    trackCtrl1->setTranslationScaling(40);
+    auto sphere1Controller = std::make_shared<imstk::SceneObjectController>(sphere1Obj, trackCtrl1);
+    scene->addObjectController(sphere1Controller);
+
     // Collisions
     auto colGraph = scene->getCollisionGraph();
     colGraph->addInteractionPair(planeObj, sphere0Obj,
@@ -455,7 +561,7 @@ void testPenaltyRigidCollision()
         CollisionHandling::Type::Penalty);
 
     // Run
-    sdk->setCurrentScene("InteractionPairTest");
+    sdk->setCurrentScene(scene);
     sdk->startSimulation(true);
 }
 
@@ -497,10 +603,13 @@ void testTwoFalcons()
     auto sphere0Obj = std::make_shared<imstk::CollidingObject>("Sphere0");
     sphere0Obj->setVisualGeometry(sphere0Geom);
     sphere0Obj->setCollidingGeometry(sphere0Geom);
-    auto sphere0Controller = sphere0Obj->setupController(falcon0);
-    sphere0Controller->setTranslationScaling(30);
     scene->addSceneObject(sphere0Obj);
 
+    auto trackCtrl0 = std::make_shared<imstk::DeviceTracker>(falcon0);
+    trackCtrl0->setTranslationScaling(30);
+    auto controller0 = std::make_shared<imstk::SceneObjectController>(sphere0Obj, trackCtrl0);
+    scene->addObjectController(controller0);
+
     // Sphere1
     auto sphere1Geom = std::make_shared<imstk::Sphere>();
     sphere1Geom->setPosition(imstk::Vec3d(-16, 4.5, 0));
@@ -508,10 +617,13 @@ void testTwoFalcons()
     auto sphere1Obj = std::make_shared<imstk::CollidingObject>("Sphere1");
     sphere1Obj->setVisualGeometry(sphere1Geom);
     sphere1Obj->setCollidingGeometry(sphere1Geom);
-    auto sphere1Controller = sphere1Obj->setupController(falcon1);
-    sphere1Controller->setTranslationScaling(30);
     scene->addSceneObject(sphere1Obj);
 
+    auto trackCtrl1 = std::make_shared<imstk::DeviceTracker>(falcon1);
+    trackCtrl1->setTranslationScaling(30);
+    auto controller1 = std::make_shared<imstk::SceneObjectController>(sphere1Obj, trackCtrl1);
+    scene->addObjectController(controller1);
+
     // Camera
     auto cam = scene->getCamera();
     cam->setPosition(imstk::Vec3d(0, 18, 20));
@@ -521,11 +633,12 @@ void testTwoFalcons()
                                      imstk::CameraController::InvertFlag::rotZ);
 
     // Run
-    sdk->setCurrentScene("FalconsTestScene");
+    sdk->setCurrentScene(scene);
     sdk->startSimulation(true);
 }
 
-void testTwoOmnis(){
+void testTwoOmnis()
+{
 #ifdef iMSTK_USE_OPENHAPTICS
     // SDK and Scene
     auto sdk = std::make_shared<imstk::SimulationManager>();
@@ -553,31 +666,39 @@ void testTwoOmnis(){
     auto sphere0Geom = std::make_shared<imstk::Sphere>();
     sphere0Geom->setPosition(imstk::Vec3d(2, 2.5, 0));
     sphere0Geom->scale(1);
+
     auto sphere0Obj = std::make_shared<imstk::CollidingObject>("Sphere0");
     sphere0Obj->setVisualGeometry(sphere0Geom);
     sphere0Obj->setCollidingGeometry(sphere0Geom);
-    auto sphere0Controller = sphere0Obj->setupController(client0);
-    sphere0Controller->setTranslationScaling(0.05);
     scene->addSceneObject(sphere0Obj);
 
+    auto trackCtrl0 = std::make_shared<imstk::DeviceTracker>(client0);
+    trackCtrl0->setTranslationScaling(0.05);
+    auto controller0 = std::make_shared<imstk::SceneObjectController>(sphere0Obj, trackCtrl0);
+    scene->addObjectController(controller0);
+
     // Sphere1
     auto sphere1Geom = std::make_shared<imstk::Sphere>();
     sphere1Geom->setPosition(imstk::Vec3d(-2, 2.5, 0));
     sphere1Geom->scale(1);
+
     auto sphere1Obj = std::make_shared<imstk::CollidingObject>("Sphere1");
     sphere1Obj->setVisualGeometry(sphere1Geom);
     sphere1Obj->setCollidingGeometry(sphere1Geom);
-    auto sphere1Controller = sphere1Obj->setupController(client1);
-    sphere1Controller->setTranslationScaling(0.05);
     scene->addSceneObject(sphere1Obj);
 
+    auto trackCtrl1 = std::make_shared<imstk::DeviceTracker>(client1);
+    trackCtrl1->setTranslationScaling(0.05);
+    auto controller1 = std::make_shared<imstk::SceneObjectController>(sphere1Obj, trackCtrl1);
+    scene->addObjectController(controller1);
+
     // Update Camera position
     auto cam = scene->getCamera();
     cam->setPosition(imstk::Vec3d(0, 0, 10));
     cam->setFocalPoint(sphere0Geom->getPosition());
 
     // Run
-    sdk->setCurrentScene("OmnisTestScene");
+    sdk->setCurrentScene(scene);
     sdk->startSimulation(false);
 #endif
 }
@@ -601,20 +722,24 @@ void testObjectController()
     auto geom = std::make_shared<imstk::Cube>();
     geom->setPosition(imstk::UP_VECTOR);
     geom->scale(2);
+
     auto object = std::make_shared<imstk::CollidingObject>("VirtualObject");
     object->setVisualGeometry(geom);
     object->setCollidingGeometry(geom);
-    auto controller = object->setupController(client);
-    controller->setTranslationScaling(0.1);
     scene->addSceneObject(object);
 
+    auto trackCtrl = std::make_shared<imstk::DeviceTracker>(client);
+    trackCtrl->setTranslationScaling(0.1);
+    auto controller = std::make_shared<imstk::SceneObjectController>(object, trackCtrl);
+    scene->addObjectController(controller);
+
     // Update Camera position
     auto cam = scene->getCamera();
     cam->setPosition(imstk::Vec3d(0, 0, 10));
     cam->setFocalPoint(geom->getPosition());
 
     // Run
-    sdk->setCurrentScene("SceneTestDevice");
+    sdk->setCurrentScene(scene);
     sdk->startSimulation(false);
 #endif
 }
@@ -653,7 +778,7 @@ void testCameraController()
                                      imstk::CameraController::InvertFlag::rotZ);
 
     // Run
-    sdk->setCurrentScene("SceneTestDevice");
+    sdk->setCurrentScene(scene);
     sdk->startSimulation(true);
 }
 
@@ -685,7 +810,7 @@ void testReadMesh()
     scene->addSceneObject(object);
 
     // Run
-    sdk->setCurrentScene("SceneTestMesh");
+    sdk->setCurrentScene(scene);
     sdk->startSimulation(true);
 }
 
@@ -743,7 +868,7 @@ void testViewer()
     cam1->setFocalPoint(imstk::Vec3d(1, 1, 0));
 
     // Run
-    sdk->setCurrentScene("SceneTest");
+    sdk->setCurrentScene(sceneTest);
     sdk->startSimulation(true);
 }
 
@@ -803,18 +928,18 @@ void testScenesManagement()
     // switch
     LOG(INFO) << "-- Test scene switch";
     int delay = 5;
-    sdk->setCurrentScene("scene1");
+    sdk->setCurrentScene(scene1);
     sdk->startSimulation();
     std::this_thread::sleep_for(std::chrono::seconds(delay));
-    sdk->setCurrentScene("scene2", false);
+    sdk->setCurrentScene(scene2, false);
     std::this_thread::sleep_for(std::chrono::seconds(delay));
-    sdk->setCurrentScene("scene1", true);
+    sdk->setCurrentScene(scene1, true);
     std::this_thread::sleep_for(std::chrono::seconds(delay));
     sdk->endSimulation();
 
     // pause/run
     LOG(INFO) << "-- Test simulation pause/run";
-    sdk->setCurrentScene("scene2");
+    sdk->setCurrentScene(scene2);
     sdk->startSimulation();
     std::this_thread::sleep_for(std::chrono::seconds(delay));
     sdk->pauseSimulation();
@@ -872,7 +997,7 @@ void testIsometricMap()
     LOG(INFO) << cubeGeom->getPosition();
 
     // Start simulation
-    sdk->setCurrentScene("geometryMapTest");
+    sdk->setCurrentScene(geometryMapTest);
     sdk->startSimulation(imstk::VTKRenderer::Mode::DEBUG);
 }
 
@@ -1191,7 +1316,7 @@ void testDeformableBody()
     scene->addNonlinearSolver(nlSolver);
 
     // Run the simulation
-    sdk->setCurrentScene("DeformableBodyTest");
+    sdk->setCurrentScene(scene);
     sdk->startSimulation(true);
 }
 
@@ -1280,7 +1405,7 @@ void testPbdVolume()
     planeObj->setCollidingGeometry(planeGeom);
     scene->addSceneObject(planeObj);
 
-    sdk->setCurrentScene("PositionBasedDynamicsTest");
+    sdk->setCurrentScene(scene);
     sdk->startSimulation(true);
 
 }
@@ -1370,7 +1495,7 @@ void testPbdCloth()
     scene->addLight(colorLight);
     scene->addSceneObject(deformableObj);
 
-    sdk->setCurrentScene("PositionBasedDynamicsTest");
+    sdk->setCurrentScene(scene);
     sdk->startSimulation(true);
 }
 
@@ -1684,7 +1809,7 @@ void testPbdCollision()
 
         colGraph->addInteractionPair(pair);
     }
-    sdk->setCurrentScene("PbdCollisionTest");
+    sdk->setCurrentScene(scene);
     sdk->startSimulation(true);
 }
 
@@ -2016,7 +2141,7 @@ void testLineMesh()
         scene->getCamera()->setFocalPoint(surfMesh.get()->getInitialVertexPosition(20));
     }
     // Run
-    sdk->setCurrentScene("TestLineMesh");
+    sdk->setCurrentScene(scene);
     sdk->startSimulation(true);
 #endif
 }