diff --git a/Base/Devices/imstkHDAPIDeviceClient.cpp b/Base/Devices/imstkHDAPIDeviceClient.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a10bddd70b4e532c2de6b215fc0989ca77707bc5
--- /dev/null
+++ b/Base/Devices/imstkHDAPIDeviceClient.cpp
@@ -0,0 +1,102 @@
+/*=========================================================================
+
+   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 "imstkHDAPIDeviceClient.h"
+
+#include <HDU/hduVector.h>
+#include <HDU/hduError.h>
+
+#include "g3log/g3log.hpp"
+
+namespace imstk {
+
+void
+HDAPIDeviceClient::initModule()
+{
+	// Open Device
+    m_handle = hdInitDevice(this->getName().c_str());
+
+	// If failed
+	HDErrorInfo error;
+	if (HD_DEVICE_ERROR(error = hdGetError()))
+	{
+		LOG(FATAL) << "Failed to initialize Phantom Omni " << this->getName();
+		m_handle = -1;
+        return;
+	}
+
+    // Calibration
+    if (hdCheckCalibration() != HD_CALIBRATION_OK)
+    {   
+        LOG(INFO) << "Move " << this->getName() << " in its dock to calibrate it.";
+        while (hdCheckCalibration() != HD_CALIBRATION_OK)
+        {
+        }
+    }
+    
+    // Success
+	LOG(INFO) << this->getName() << " successfully initialized.";
+    hdEnable(HD_FORCE_OUTPUT);
+    hdEnable(HD_FORCE_RAMPING);
+    hdStartScheduler();
+}
+
+void
+HDAPIDeviceClient::runModule()
+{
+    hdScheduleSynchronous(hapticCallback, this, HD_MAX_SCHEDULER_PRIORITY);
+}
+
+void
+HDAPIDeviceClient::cleanUpModule()
+{
+    hdStopScheduler();
+    hdDisableDevice(m_handle);
+}
+
+HDCallbackCode HDCALLBACK
+HDAPIDeviceClient::hapticCallback(void* pData)
+{
+    auto client = reinterpret_cast<HDAPIDeviceClient*>(pData);
+    auto handle = client->m_handle;
+    auto state = client->m_state;
+
+	hdBeginFrame(handle);
+
+    hdSetDoublev(HD_CURRENT_FORCE, client->m_force.data());
+    hdGetDoublev(HD_CURRENT_POSITION, state.pos);
+    hdGetDoublev(HD_CURRENT_VELOCITY, state.vel);
+    hdGetDoublev(HD_CURRENT_TRANSFORM, state.trans);
+    hdGetIntegerv(HD_CURRENT_BUTTONS, &state.buttons);
+
+    hdEndFrame(handle);
+
+    client->m_position << state.pos[0], state.pos[1], state.pos[2];
+    client->m_velocity << state.vel[0], state.vel[1], state.vel[2];
+    client->m_orientation = (Eigen::Affine3d(Eigen::Matrix4d(state.trans))).rotation();
+    client->m_buttons[0] = state.buttons & HD_DEVICE_BUTTON_1;
+    client->m_buttons[1] = state.buttons & HD_DEVICE_BUTTON_2;
+    client->m_buttons[2] = state.buttons & HD_DEVICE_BUTTON_3;
+    client->m_buttons[3] = state.buttons & HD_DEVICE_BUTTON_4;
+
+    return HD_CALLBACK_CONTINUE;
+}
+}
diff --git a/Base/Devices/imstkHDAPIDeviceClient.h b/Base/Devices/imstkHDAPIDeviceClient.h
new file mode 100644
index 0000000000000000000000000000000000000000..d6b4c0ba239623ed13db8340e113b17c1eda332b
--- /dev/null
+++ b/Base/Devices/imstkHDAPIDeviceClient.h
@@ -0,0 +1,73 @@
+/*=========================================================================
+
+   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 imstkHDAPIDeviceClient_h
+#define imstkHDAPIDeviceClient_h
+
+#include <map>
+
+#include "imstkDeviceClient.h"
+
+#include <HD/hd.h>
+
+#include <memory>
+
+namespace imstk {
+
+struct HD_state
+{
+    HDdouble pos[3];
+    HDdouble vel[3];
+    HDdouble trans[16];
+    HDint buttons;
+};
+
+///
+/// \class HDAPIDeviceClient
+/// \brief Subclass of DeviceClient for phantom omni
+///
+class HDAPIDeviceClient : public DeviceClient
+{
+public:
+
+    HDAPIDeviceClient(std::string name):
+        DeviceClient(name, "localhost")
+    {}
+
+    virtual ~HDAPIDeviceClient() {}
+
+protected:
+
+    void initModule() override;
+    void runModule() override;
+    void cleanUpModule() override;
+
+private:
+
+    static HDCallbackCode HDCALLBACK hapticCallback(void* pData);
+	
+    HHD m_handle; //!< device handle
+    HD_state m_state; //!< device reading state
+
+};
+}
+
+#endif // ifndef imstkHDAPIDeviceClient_h
diff --git a/Examples/Sandbox/main.cpp b/Examples/Sandbox/main.cpp
index abacade318eaf46abb5aac4cd69429b33ef108eb..9957e931407eabc9b558616d7418be38000b3cf0 100644
--- a/Examples/Sandbox/main.cpp
+++ b/Examples/Sandbox/main.cpp
@@ -28,6 +28,7 @@
 #include "imstkOneToOneMap.h"
 
 // Devices
+#include "imstkHDAPIDeviceClient.h"
 #include "imstkVRPNDeviceClient.h"
 #include "imstkVRPNDeviceServer.h"
 #include "imstkCameraController.h"
@@ -259,35 +260,23 @@ void testObjectController()
     auto sdk = std::make_shared<imstk::SimulationManager>();
     auto scene = sdk->createNewScene("SceneTestDevice");
 
-    // Device server
-    auto server = std::make_shared<imstk::VRPNDeviceServer>("127.0.0.1");
-    server->addDevice("device0", imstk::DeviceType::SPACE_EXPLORER_3DCONNEXION);
-    sdk->addDeviceServer(server);
-
     // Device Client
-    auto client = std::make_shared<imstk::VRPNDeviceClient>("device0", "localhost"); // localhost = 127.0.0.1
+    auto client = std::make_shared<imstk::HDAPIDeviceClient>("Default PHANToM", "localhost"); // localhost = 127.0.0.1
     sdk->addDeviceClient(client);
 
-    // Plane
-    auto planeGeom = std::make_shared<imstk::Plane>();
-    planeGeom->scale(5);
-    auto planeObj = std::make_shared<imstk::VisualObject>("VisualPlane");
-    planeObj->setVisualGeometry(planeGeom);
-    scene->addSceneObject(planeObj);
-
-    // Sphere
-    auto sphereGeom = std::make_shared<imstk::Sphere>();
-    sphereGeom->setPosition(imstk::UP_VECTOR);
-    sphereGeom->scale(0.2);
-    auto sphereObj = std::make_shared<imstk::VirtualCouplingObject>("VirtualSphere", client, 20);
-    sphereObj->setVisualGeometry(sphereGeom);
-    sphereObj->setCollidingGeometry(sphereGeom);
-    scene->addSceneObject(sphereObj);
+    // Object
+    auto geom = std::make_shared<imstk::Cube>();
+    geom->setPosition(imstk::UP_VECTOR);
+    geom->scale(2);
+    auto object = std::make_shared<imstk::VirtualCouplingObject>("VirtualObject", client, 0.1);
+    object->setVisualGeometry(geom);
+    object->setCollidingGeometry(geom);
+    scene->addSceneObject(object);
 
     // Update Camera position
     auto cam = scene->getCamera();
-    cam->setPosition(imstk::Vec3d(0,3,10));
-    cam->setFocalPoint(sphereGeom->getPosition());
+    cam->setPosition(imstk::Vec3d(0,0,10));
+    cam->setFocalPoint(geom->getPosition());
 
     // Run
     sdk->setCurrentScene("SceneTestDevice");