diff --git a/Examples/OpenVRController/CMakeLists.txt b/Examples/OpenVRController/CMakeLists.txt
index b22e1eee19c3b506f452a976f4960d1e649edaa9..5190298dac6aab05718309664d690b6d7b161c03 100644
--- a/Examples/OpenVRController/CMakeLists.txt
+++ b/Examples/OpenVRController/CMakeLists.txt
@@ -21,7 +21,10 @@ project(Example-OpenVRController)
 #-----------------------------------------------------------------------------
 # Create executable
 #-----------------------------------------------------------------------------
-imstk_add_executable(${PROJECT_NAME} OpenVRControllerExample.cpp)
+imstk_add_executable(${PROJECT_NAME}
+	OpenVRControllerExample.cpp
+	CameraOpenVRControl.h
+	CameraOpenVRControl.cpp)
 
 #-----------------------------------------------------------------------------
 # Add the target to Examples folder
diff --git a/Examples/OpenVRController/CameraOpenVRControl.cpp b/Examples/OpenVRController/CameraOpenVRControl.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fc2aa9663a1c53df7190a42f127427266f8559e9
--- /dev/null
+++ b/Examples/OpenVRController/CameraOpenVRControl.cpp
@@ -0,0 +1,60 @@
+/*=========================================================================
+
+   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 "CameraOpenVRControl.h"
+#include "imstkCamera.h"
+#include "imstkLogger.h"
+#include "imstkOpenVRDeviceClient.h"
+
+using namespace imstk;
+
+void
+CameraOpenVRControl::printControls()
+{
+    LOG(INFO) << "Mouse Scene Controls: Only usable in debug mode";
+    LOG(INFO) << "----------------------------------------------------------------------";
+    LOG(INFO) << " | Left Trackpad   - rotate view";
+    LOG(INFO) << " | Right Trakcpad  - translate view";
+    LOG(INFO) << "----------------------------------------------------------------------";
+}
+
+void
+CameraOpenVRControl::update(const double dt)
+{
+    // We may switch cameras on the controller
+    if (m_camera == nullptr)
+    {
+        return;
+    }
+
+    if (m_rotateDevice != nullptr)
+    {
+        const Vec2d& pos = m_rotateDevice->getTrackpadPosition();
+        const Mat4d& view = m_camera->getView();
+        m_camera->setView(view * mat4dRotation(Rotd(-pos[0] * m_rotateSpeedScale * dt, Vec3d(0.0, 1.0, 0.0))));
+    }
+    if (m_translateDevice != nullptr)
+    {
+        const Vec2d& pos = m_translateDevice->getTrackpadPosition();
+        const Mat4d& view = m_camera->getView();
+        m_camera->setView(view * mat4dTranslate(Vec3d(pos[0], 0.0, -pos[1]) * m_translateSpeedScale * dt));
+    }
+}
\ No newline at end of file
diff --git a/Examples/OpenVRController/CameraOpenVRControl.h b/Examples/OpenVRController/CameraOpenVRControl.h
new file mode 100644
index 0000000000000000000000000000000000000000..ed74ff89c548e56b2d09fb5940bc3324e6bba55f
--- /dev/null
+++ b/Examples/OpenVRController/CameraOpenVRControl.h
@@ -0,0 +1,96 @@
+/*=========================================================================
+
+   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.
+
+=========================================================================*/
+
+#pragma once
+
+#include "imstkDeviceControl.h"
+#include "imstkMath.h"
+
+namespace imstk
+{
+class Camera;
+class OpenVRDeviceClient;
+class SceneManager;
+}
+
+using namespace imstk;
+
+///
+/// \class CameraOpenVRControl
+///
+/// \brief Controls pose of a camera given two trackpads from OpenVRDeviceClient's
+/// 
+///
+class CameraOpenVRControl : public DeviceControl
+{
+public:
+    CameraOpenVRControl() = default;
+    virtual ~CameraOpenVRControl() override = default;
+
+public:
+    ///
+    /// \brief Get/Set how fast the camera translates
+    ///
+    void setTranslateSpeedScale(const double translateSpeedScale) { m_translateSpeedScale = translateSpeedScale; }
+    double getTranslateSpeedScale() const { return m_translateSpeedScale; }
+
+    ///
+    /// \brief Get/Set how fast the camera rotates
+    ///
+    void setRotateSpeedScale(const double rotateSpeedScale) { m_rotateSpeedScale = rotateSpeedScale; }
+    double getRotateSpeedScale() const { return m_rotateSpeedScale; }
+
+    ///
+    /// \brief Get/Set the device that can translate the camera
+    /// 
+    void setTranslateDevice(std::shared_ptr<OpenVRDeviceClient> translateDevice) { m_translateDevice  = translateDevice; }
+    std::shared_ptr<OpenVRDeviceClient> getTranslateDevice() const { return m_translateDevice; }
+
+    ///
+    /// \brief Get/Set the device that can rotate the camera
+    /// 
+    void setRotateDevice(std::shared_ptr<OpenVRDeviceClient> rotateDevice) { m_rotateDevice = rotateDevice; }
+    std::shared_ptr<OpenVRDeviceClient> getRotateDevice() const { return m_rotateDevice; }
+
+    ///
+    /// \brief Get/Set the camera to be controlled
+    /// 
+    void setCamera(std::shared_ptr<Camera> camera) { m_camera = camera; }
+    std::shared_ptr<Camera> getCamera() const { return m_camera; }
+
+public:
+    void printControls() override;
+
+    ///
+    /// \brief Updates control based on current device state
+    ///
+    void update(const double dt) override;
+
+protected:
+    std::shared_ptr<OpenVRDeviceClient> m_translateDevice = nullptr;
+    std::shared_ptr<OpenVRDeviceClient> m_rotateDevice = nullptr;
+
+    std::shared_ptr<Camera> m_camera = nullptr;
+
+    // User changeable values
+    double m_rotateSpeedScale = 1.0;
+    double m_translateSpeedScale = 1.0;
+};
\ No newline at end of file
diff --git a/Examples/OpenVRController/OpenVRControllerExample.cpp b/Examples/OpenVRController/OpenVRControllerExample.cpp
index e0b66c2a91f3f23f5a375bc37d8490b84c1f2eb2..cc612e79b88e34df225c6886da166e6fcd2f2ac4 100644
--- a/Examples/OpenVRController/OpenVRControllerExample.cpp
+++ b/Examples/OpenVRController/OpenVRControllerExample.cpp
@@ -35,6 +35,10 @@
 #include "imstkVisualModel.h"
 #include "imstkVisualObjectImporter.h"
 #include "imstkVTKOpenVRViewer.h"
+#include "CameraOpenVRControl.h"
+#include "imstkVTKViewer.h"
+
+#include "imstkTimer.h"
 
 using namespace imstk;
 
@@ -125,80 +129,111 @@ main()
 
     {
         // Add a module to run the viewer
-        imstkNew<VTKOpenVRViewer> viewer;
+        imstkNew<VTKViewer> viewer;
+        viewer->setExecutionType(Module::ExecutionType::SEQUENTIAL);
         viewer->setActiveScene(scene);
 
         // Add a module to run the scene
-        imstkNew<SceneManager> sceneManager;
-        sceneManager->setActiveScene(scene);
+        /*imstkNew<SceneManager> sceneManager;
+        sceneManager->setActiveScene(scene);*/
 
         imstkNew<SimulationManager> driver;
         driver->addModule(viewer);
-        driver->addModule(sceneManager);
-
-        // Add a VR controller for the scalpel handle
-        imstkNew<SceneObjectController> controller1(scalpelHandle, viewer->getVRDeviceClient(OPENVR_RIGHT_CONTROLLER));
-        scene->addController(controller1);
-        // Add a VR controller for the scalpel blade
-        imstkNew<SceneObjectController> controller2(scalpelBlade10, viewer->getVRDeviceClient(OPENVR_RIGHT_CONTROLLER));
-        scene->addController(controller2);
-
-        // This button event is emitted from the viewer's thread, thus it is queued to the scene so that we do not
-        // run it while the scene is updating
-        bool blade10InHand = true;
-        queueConnect<ButtonEvent>(viewer->getVRDeviceClient(OPENVR_RIGHT_CONTROLLER), &OpenVRDeviceClient::buttonStateChanged, sceneManager,
-            [&](ButtonEvent* e)
+        //driver->addModule(sceneManager);
+        //driver->setDesiredDt(0.01);
+
+        //// Add a VR controller for the scalpel handle
+        //imstkNew<SceneObjectController> controller1(scalpelHandle, viewer->getVRDeviceClient(OPENVR_RIGHT_CONTROLLER));
+        //scene->addController(controller1);
+        //// Add a VR controller for the scalpel blade
+        //imstkNew<SceneObjectController> controller2(scalpelBlade10, viewer->getVRDeviceClient(OPENVR_RIGHT_CONTROLLER));
+        //scene->addController(controller2);
+
+        //imstkNew<CameraOpenVRControl> camControl;
+        //camControl->setRotateDevice(viewer->getVRDeviceClient(OPENVR_RIGHT_CONTROLLER));
+        //camControl->setTranslateDevice(viewer->getVRDeviceClient(OPENVR_LEFT_CONTROLLER));
+        //camControl->setTranslateSpeedScale(1.0);
+        //camControl->setRotateSpeedScale(1.0);
+        //camControl->setCamera(scene->getActiveCamera());
+        //viewer->addControl(camControl); // Only needs to update every render
+
+        StopWatch timer;
+        timer.start();
+        int i = 0;
+        //connect<Event>(viewer, &Viewer::postUpdate, [&](Event*)
+        //    {
+        //        double ms = timer.getTimeElapsed();
+        //        //if (i++ % 100 == 0)
+        //        {
+        //            printf("%f\n", ms);
+        //        }
+        //        timer.start();
+        //    });
+        viewer->init();
+        while(true)
         {
-            // When any button pressed, swap blade
-            if (e->m_buttonState == BUTTON_PRESSED)
-            {
-                const Vec3d& posControl = viewer->getVRDeviceClient(OPENVR_RIGHT_CONTROLLER)->getPosition();
-                if (blade10InHand)
-                {
-                    // Swap to blade 15 only if it's close in space
-                    Vec3d min, max;
-                    scalpelBlade15->getVisualGeometry()->computeBoundingBox(min, max);
-                    const Vec3d posBlade = (min + max) * 0.5;
-                    const double dist    = (posControl - posBlade).norm();
-                    LOG(INFO) << "Dist: " << dist;
-                    if (dist < 2.0)
-                    {
-                        const Vec3d t = scalpelBlade15->getVisualGeometry()->getTranslation();
-                        const Mat3d r = scalpelBlade15->getVisualGeometry()->getRotation();
-
-                        // Set the new blade to move
-                        controller2->setControlledSceneObject(scalpelBlade15);
-                        blade10InHand = false;
-
-                        scalpelBlade10->getVisualGeometry()->setTranslation(t);
-                        scalpelBlade10->getVisualGeometry()->setRotation(r);
-                    }
-                }
-                else
-                {
-                    // Swap to blade 10 only if it's close in space
-                    Vec3d min, max;
-                    scalpelBlade10->getVisualGeometry()->computeBoundingBox(min, max);
-                    const Vec3d posBlade = (min + max) * 0.5;
-                    const double dist    = (posControl - posBlade).norm();
-                    LOG(INFO) << "Dist: " << dist;
-                    if (dist < 2.0)
-                    {
-                        const Vec3d t = scalpelBlade10->getVisualGeometry()->getTranslation();
-                        const Mat3d r = scalpelBlade10->getVisualGeometry()->getRotation();
-
-                        controller2->setControlledSceneObject(scalpelBlade10);
-                        blade10InHand = true;
-
-                        // Swap transforms of the blades
-                        scalpelBlade15->getVisualGeometry()->setTranslation(t);
-                        scalpelBlade15->getVisualGeometry()->setRotation(r);
-                    }
-                }
-            }
-        });
-
-        driver->start();
+            double ms = timer.getTimeElapsed();
+            printf("%f\n", ms);
+            timer.start();
+            viewer->update();
+        }
+
+        //// This button event is emitted from the viewer's thread, thus it is queued to the scene so that we do not
+        //// run it while the scene is updating
+        //bool blade10InHand = true;
+        //queueConnect<ButtonEvent>(viewer->getVRDeviceClient(OPENVR_RIGHT_CONTROLLER), &OpenVRDeviceClient::buttonStateChanged, sceneManager,
+        //    [&](ButtonEvent* e)
+        //{
+        //    // When any button pressed, swap blade
+        //    if (e->m_buttonState == BUTTON_PRESSED)
+        //    {
+        //        const Vec3d& posControl = viewer->getVRDeviceClient(OPENVR_RIGHT_CONTROLLER)->getPosition();
+        //        if (blade10InHand)
+        //        {
+        //            // Swap to blade 15 only if it's close in space
+        //            Vec3d min, max;
+        //            scalpelBlade15->getVisualGeometry()->computeBoundingBox(min, max);
+        //            const Vec3d posBlade = (min + max) * 0.5;
+        //            const double dist    = (posControl - posBlade).norm();
+        //            LOG(INFO) << "Dist: " << dist;
+        //            if (dist < 2.0)
+        //            {
+        //                const Vec3d t = scalpelBlade15->getVisualGeometry()->getTranslation();
+        //                const Mat3d r = scalpelBlade15->getVisualGeometry()->getRotation();
+
+        //                // Set the new blade to move
+        //                controller2->setControlledSceneObject(scalpelBlade15);
+        //                blade10InHand = false;
+
+        //                scalpelBlade10->getVisualGeometry()->setTranslation(t);
+        //                scalpelBlade10->getVisualGeometry()->setRotation(r);
+        //            }
+        //        }
+        //        else
+        //        {
+        //            // Swap to blade 10 only if it's close in space
+        //            Vec3d min, max;
+        //            scalpelBlade10->getVisualGeometry()->computeBoundingBox(min, max);
+        //            const Vec3d posBlade = (min + max) * 0.5;
+        //            const double dist    = (posControl - posBlade).norm();
+        //            LOG(INFO) << "Dist: " << dist;
+        //            if (dist < 2.0)
+        //            {
+        //                const Vec3d t = scalpelBlade10->getVisualGeometry()->getTranslation();
+        //                const Mat3d r = scalpelBlade10->getVisualGeometry()->getRotation();
+
+        //                controller2->setControlledSceneObject(scalpelBlade10);
+        //                blade10InHand = true;
+
+        //                // Swap transforms of the blades
+        //                scalpelBlade15->getVisualGeometry()->setTranslation(t);
+        //                scalpelBlade15->getVisualGeometry()->setRotation(r);
+        //            }
+        //        }
+        //    }
+        //});
+
+        //driver->start();
     }
 
     return 0;