diff --git a/src/xrt/drivers/opengloves/encoding/alpha_encoding.cpp b/src/xrt/drivers/opengloves/encoding/alpha_encoding.cpp
index e830fae32..ce8d851a8 100644
--- a/src/xrt/drivers/opengloves/encoding/alpha_encoding.cpp
+++ b/src/xrt/drivers/opengloves/encoding/alpha_encoding.cpp
@@ -140,6 +140,14 @@ static const std::map<std::string, int> opengloves_alpha_encoding_key_string{
     {"", OPENGLOVES_ALPHA_ENCODING_MAX}                   // Junk key
 };
 
+static const std::map<int, std::string> opengloves_alpha_encoding_output_key_string{
+    {OPENGLOVES_ALPHA_ENCODING_FinThumb, "A"},  // thumb force feedback
+    {OPENGLOVES_ALPHA_ENCODING_FinIndex, "B"},  // index force feedback
+    {OPENGLOVES_ALPHA_ENCODING_FinMiddle, "C"}, // middle force feedback
+    {OPENGLOVES_ALPHA_ENCODING_FinRing, "D"},   // ring force feedback
+    {OPENGLOVES_ALPHA_ENCODING_FinPinky, "E"},  // pinky force feedback
+};
+
 static std::map<int, std::string>
 opengloves_alpha_encoding_parse_to_map(const std::string &str)
 {
@@ -247,4 +255,20 @@ opengloves_alpha_encoding_decode(const char *data, struct opengloves_input *out)
 	out->gestures.grab.activated = input_map.find(OPENGLOVES_ALPHA_ENCODING_GesGrab) != input_map.end();
 	out->gestures.pinch.activated = input_map.find(OPENGLOVES_ALPHA_ENCODING_GesPinch) != input_map.end();
 	out->buttons.menu.pressed = input_map.find(OPENGLOVES_ALPHA_ENCODING_BtnMenu) != input_map.end();
-}
\ No newline at end of file
+}
+
+void
+opengloves_alpha_encoding_encode(const struct opengloves_output *output, char *out_buff)
+{
+	sprintf(out_buff, "%s%d%s%d%s%d%s%d%s%d\n",
+	        opengloves_alpha_encoding_output_key_string.at(OPENGLOVES_ALPHA_ENCODING_FinThumb).c_str(),
+	        (int)(output->force_feedback.thumb * 1000),
+	        opengloves_alpha_encoding_output_key_string.at(OPENGLOVES_ALPHA_ENCODING_FinIndex).c_str(),
+	        (int)(output->force_feedback.index * 1000),
+	        opengloves_alpha_encoding_output_key_string.at(OPENGLOVES_ALPHA_ENCODING_FinMiddle).c_str(),
+	        (int)(output->force_feedback.middle * 1000),
+	        opengloves_alpha_encoding_output_key_string.at(OPENGLOVES_ALPHA_ENCODING_FinRing).c_str(),
+	        (int)(output->force_feedback.ring * 1000),
+	        opengloves_alpha_encoding_output_key_string.at(OPENGLOVES_ALPHA_ENCODING_FinPinky).c_str(),
+	        (int)(output->force_feedback.little * 1000));
+}
diff --git a/src/xrt/drivers/opengloves/encoding/alpha_encoding.h b/src/xrt/drivers/opengloves/encoding/alpha_encoding.h
index 1ca7a1dd8..29b05f114 100644
--- a/src/xrt/drivers/opengloves/encoding/alpha_encoding.h
+++ b/src/xrt/drivers/opengloves/encoding/alpha_encoding.h
@@ -17,6 +17,8 @@ extern "C" {
 void
 opengloves_alpha_encoding_decode(const char *data, struct opengloves_input *out_kv);
 
+void
+opengloves_alpha_encoding_encode(const struct opengloves_output *output, char *out_buff);
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/xrt/drivers/opengloves/encoding/encoding.h b/src/xrt/drivers/opengloves/encoding/encoding.h
index 7775ee411..5713b6ec4 100644
--- a/src/xrt/drivers/opengloves/encoding/encoding.h
+++ b/src/xrt/drivers/opengloves/encoding/encoding.h
@@ -66,6 +66,20 @@ struct opengloves_input
 	struct opengloves_input_gestures gestures;
 };
 
+struct opengloves_output_force_feedback
+{
+	float thumb;
+	float index;
+	float middle;
+	float ring;
+	float little;
+};
+
+struct opengloves_output
+{
+	struct opengloves_output_force_feedback force_feedback;
+};
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/xrt/drivers/opengloves/opengloves_device.c b/src/xrt/drivers/opengloves/opengloves_device.c
index 6b69be333..920c0297a 100644
--- a/src/xrt/drivers/opengloves/opengloves_device.c
+++ b/src/xrt/drivers/opengloves/opengloves_device.c
@@ -159,6 +159,45 @@ opengloves_device_update_inputs(struct xrt_device *xdev)
 	os_mutex_unlock(&od->lock);
 }
 
+static void
+opengloves_ffb_location_convert(const struct xrt_output_force_feedback *xrt_ffb,
+                                struct opengloves_output_force_feedback *out_ffb)
+{
+	switch (xrt_ffb->location) {
+	case XRT_FORCE_FEEDBACK_LOCATION_LEFT_THUMB: out_ffb->thumb = xrt_ffb->value; break;
+	case XRT_FORCE_FEEDBACK_LOCATION_LEFT_INDEX: out_ffb->index = xrt_ffb->value; break;
+	case XRT_FORCE_FEEDBACK_LOCATION_LEFT_MIDDLE: out_ffb->middle = xrt_ffb->value; break;
+	case XRT_FORCE_FEEDBACK_LOCATION_LEFT_RING: out_ffb->ring = xrt_ffb->value; break;
+	case XRT_FORCE_FEEDBACK_LOCATION_LEFT_PINKY: out_ffb->little = xrt_ffb->value; break;
+	}
+}
+
+static void
+opengloves_device_set_output(struct xrt_device *xdev, enum xrt_output_name name, const union xrt_output_value *value)
+{
+	struct opengloves_device *od = opengloves_device(xdev);
+
+	switch (name) {
+	case XRT_OUTPUT_NAME_FORCE_FEEDBACK_LEFT:
+	case XRT_OUTPUT_NAME_FORCE_FEEDBACK_RIGHT: {
+		struct opengloves_output out;
+
+		int location_count = value->force_feedback.force_feedback_location_count;
+		const struct xrt_output_force_feedback *ffb = value->force_feedback.force_feedback;
+
+		for (int i = 0; i < location_count; i++) {
+			opengloves_ffb_location_convert(ffb + i, &out.force_feedback);
+		}
+
+		char buff[64];
+		opengloves_alpha_encoding_encode(&out, buff);
+
+		opengloves_communication_device_write(od->ocd, buff, strlen(buff));
+	}
+	default: break;
+	}
+}
+
 static void
 opengloves_device_destroy(struct xrt_device *xdev)
 {
@@ -235,7 +274,8 @@ struct xrt_device *
 opengloves_device_create(struct opengloves_communication_device *ocd, enum xrt_hand hand)
 {
 	enum u_device_alloc_flags flags = (enum u_device_alloc_flags)(U_DEVICE_ALLOC_TRACKING_NONE);
-	struct opengloves_device *od = U_DEVICE_ALLOCATE(struct opengloves_device, flags, 8, 0);
+
+	struct opengloves_device *od = U_DEVICE_ALLOCATE(struct opengloves_device, flags, 8, 1);
 
 	od->base.name = XRT_DEVICE_HAND_TRACKER;
 	od->base.device_type = XRT_DEVICE_TYPE_HAND_TRACKER;
@@ -251,6 +291,7 @@ opengloves_device_create(struct opengloves_communication_device *ocd, enum xrt_h
 	    od->hand == XRT_HAND_LEFT ? XRT_INPUT_GENERIC_HAND_TRACKING_LEFT : XRT_INPUT_GENERIC_HAND_TRACKING_RIGHT;
 
 	od->base.hand_tracking_supported = true;
+	od->base.force_feedback_supported = true;
 
 	// inputs
 	od->base.update_inputs = opengloves_device_update_inputs;
@@ -266,6 +307,11 @@ opengloves_device_create(struct opengloves_communication_device *ocd, enum xrt_h
 	od->base.inputs[OPENGLOVES_INDEX_JOYSTICK_MAIN].name = XRT_INPUT_INDEX_THUMBSTICK;
 	od->base.inputs[OPENGLOVES_INDEX_JOYSTICK_MAIN_CLICK].name = XRT_INPUT_INDEX_THUMBSTICK_CLICK;
 
+	// outputs
+	od->base.outputs[0].name =
+	    od->hand == XRT_HAND_LEFT ? XRT_OUTPUT_NAME_FORCE_FEEDBACK_LEFT : XRT_OUTPUT_NAME_FORCE_FEEDBACK_RIGHT;
+	od->base.set_output = opengloves_device_set_output;
+
 	// startup thread
 	int ret = os_thread_helper_init(&od->oth);
 	if (ret != 0) {