diff --git a/demos/optical_bench/src/hmd_navigation_behavior.cpp b/demos/optical_bench/src/hmd_navigation_behavior.cpp
index 95ccb68ef66911ef9fcf420bd45519713e1d2c87..5625900226938363437d737d19adfae2fe823c09 100644
--- a/demos/optical_bench/src/hmd_navigation_behavior.cpp
+++ b/demos/optical_bench/src/hmd_navigation_behavior.cpp
@@ -335,7 +335,11 @@ void HMDNavigationBehavior::ButtonChange(
     } else {
       SetLastSelected(select_->GetHovered());
 
-      if (grip_mode_ == 0) grip_mode_ = 1;  // Hover without feedback sucks
+      if (grip_mode_ == 0) {
+        grip_mode_ = 1;  // Hover without feedback sucks
+      } else {
+        grip_mode_ = grip_mode_ + 1 % 3;
+      }
       UpdateHelpers();
     }
   }
diff --git a/demos/optical_bench/src/object_menu.cpp b/demos/optical_bench/src/object_menu.cpp
index 5345f61e4adb09d13a3458453d9316606d54a15a..915edcf69b7d9922635aa8b6511a4445cc3dcaaa 100644
--- a/demos/optical_bench/src/object_menu.cpp
+++ b/demos/optical_bench/src/object_menu.cpp
@@ -92,12 +92,12 @@ ObjectMenu::ObjectMenu(phx::Scene* scene, phx::Engine* engine) {
       glm::vec3(0, -0.0275f, -0.1f));
   create_frame_2_->SetImage(
       phx::ResourceUtils::LoadResourceFromFile<phx::Image>(
-          "laser/target/CheckerRed.png"));
+          "laser/target/Black.png"));
   create_frame_2_->SetHoldable(false);
-  create_frame_2_->SetOnPress([this, object_manager](phx::Transform* t,
-                                                     glm::mat4) {
-    object_manager->CreateTarget(t->GetGlobalTranslation(), "CheckerRed.png");
-  });
+  create_frame_2_->SetOnPress(
+      [this, object_manager](phx::Transform* t, glm::mat4) {
+        object_manager->CreateTarget(t->GetGlobalTranslation(), "Black.png");
+      });
 
   create_frame_3_ = new MenuButton(engine, scene, MenuButton::RECTANGULAR);
   create_frame_3_->GetTransform()->SetParent(
diff --git a/demos/optical_bench/src/ray_pass.cpp b/demos/optical_bench/src/ray_pass.cpp
index 4ff03c2241609aa57a61acedaaecd375d1263c05..7a704e984e74673beacf7060d9e8c1acef7988c6 100644
--- a/demos/optical_bench/src/ray_pass.cpp
+++ b/demos/optical_bench/src/ray_pass.cpp
@@ -35,6 +35,7 @@
 #include "phx/suppress_warnings.hpp"
 
 SUPPRESS_WARNINGS_BEGIN
+#include <glm/gtx/quaternion.hpp>
 #include "glm/glm.hpp"
 #include "glm/gtc/matrix_transform.hpp"
 SUPPRESS_WARNINGS_END
@@ -261,7 +262,7 @@ void RayPass::CreateLaser() {
   buffer_laser_dir_Optix_ =
       context->createBuffer(RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 50, 50);
 
-  LoadAllPatterns("Rainbow");
+  LoadAllPatterns("Circle");
   SetLaserWaveLength(450.0f);
 
   context["allowTir"]->setInt(0);
@@ -351,7 +352,8 @@ void RayPass::CreateTarget() {
   target_acceleration_ = context->createAcceleration("Trbvh");
   target_group_->setAcceleration(target_acceleration_);
 
-  target_geometry_["p1"]->setFloat(0, 0.0275f, 0.0289f);
+  glm::vec3 p1 = glm::vec3(0, 0.0275f, 0.0289f);
+  target_geometry_["p1"]->setFloat(p1.x, p1.y, p1.z);
   target_geometry_["p2"]->setFloat(-0.129f, 0.08f, 0.0f);
 
   target_geometry_["stretchXY1"]->setFloat(0.05f, 0.05f);
@@ -368,23 +370,56 @@ void RayPass::CreateTarget() {
   optix_context_manager_->GetTopObject()->addChild(target_transform_);
   optix_context_manager_->GetTopObject()->getAcceleration()->markDirty();
 
-  auto selector = target_entity_->AddComponent<phx::Selector>(target_entity_);
-  selector->SetMove([this](phx::Transform* t, glm::mat4 r) {
-    target_entity_->GetFirstComponent<phx::Transform>()->SetGlobalMatrix(
-        t->GetGlobalMatrix() * r);
+  auto obj_m = engine_->GetSystem<ObjectManager>();
+
+  auto selector =
+      target_entity_->AddComponent<phx::Selector>(target_entity_, true);
+  target_support_rod_ =
+      new SupportRod(engine_->GetScene().get(), selector, false);
+
+  selector->SetMove([this, p1, obj_m](phx::Transform* t, glm::mat4 r) {
+    // Offset translation
+
+    auto offset4 =
+        glm::toMat4(target_entity_->GetFirstComponent<phx::Transform>()
+                        ->GetGlobalRotation()) *
+        glm::vec4(p1, 1);
+
+    auto pos = glm::vec3((t->GetGlobalMatrix() * r)[3]);  // Normal translation
+    auto pos_offset = glm::vec3(offset4) / offset4.w + pos;
+
+    auto pos_proj =
+        glm::dot(pos_offset - obj_m->GetCenterAxisPos(), glm::vec3(0, 0, 1)) *
+            glm::vec3(0, 0, 1) +
+        obj_m->GetCenterAxisPos();
+
+    // close enough? Then snap!
+    if (glm::length(pos_offset - pos_proj) <= obj_m->GetSnapDistance()) {
+      pos = pos_proj - glm::vec3(offset4) / offset4.w;
+    }
 
+    target_entity_->GetFirstComponent<phx::Transform>()->SetGlobalTranslation(
+        pos);
     glm::mat4 trans =
         target_entity_->GetFirstComponent<phx::Transform>()->GetGlobalMatrix();
     target_transform_->setMatrix(true, &(trans[0][0]), &(inverse(trans)[0][0]));
+
+    target_support_rod_->Moved();
+    optix_context_manager_->GetTopObject()->getAcceleration()->markDirty();
+  });
+  selector->SetExternalUpdate([this]() {
+    glm::mat4 trans =
+        target_entity_->GetFirstComponent<phx::Transform>()->GetGlobalMatrix();
+    target_transform_->setMatrix(true, &(trans[0][0]), &(inverse(trans)[0][0]));
+
+    target_support_rod_->Moved();
     optix_context_manager_->GetTopObject()->getAcceleration()->markDirty();
   });
   selector->SetRelease([this]() {
     if (GetTargetBufferWrite()) ClearTargetBuffers();
   });
 
-  selector->SetIntersectionDistance(1.5f);
-
-  target_support_rod_ = new SupportRod(engine_->GetScene().get(), selector);
+  target_support_rod_->Moved();
 }
 
 void RayPass::CopyAndScaleTargetData() {
diff --git a/demos/optical_bench/src/ray_pass.hpp b/demos/optical_bench/src/ray_pass.hpp
index 19cb4784b5798e1bbaa7356892324101f9e26bea..ac7cf7dd73e26d0e4b865ae9122cb7be2bdf488d 100644
--- a/demos/optical_bench/src/ray_pass.hpp
+++ b/demos/optical_bench/src/ray_pass.hpp
@@ -79,7 +79,8 @@ class RayPass : public RenderPass {
     return optix_context_manager_->GetContext()["laserBeamWidth"]->getFloat();
   };
   void SetLaserWaveLength(float wavelength) {
-    optix_context_manager_->GetContext()["laserWaveLength"]->setFloat(wavelength);
+    optix_context_manager_->GetContext()["laserWaveLength"]->setFloat(
+        wavelength);
   };
   float GetLaserWaveLength() {
     return optix_context_manager_->GetContext()["laserWaveLength"]->getFloat();
@@ -101,8 +102,8 @@ class RayPass : public RenderPass {
                  glm::vec3 up, glm::mat3 rot_mat);
 
   const unsigned int kLaserMaxDepth_ = 20;
-  const std::vector<std::string> kPatternFiles_ = {"Crosshair", "Pointer",
-                                                   "Rainbow", "RayTest"};
+  const std::vector<std::string> kPatternFiles_ = {"Cross", "Pointer", "Circle",
+                                                   "Quad"};
 
   Engine* engine_;
   OptixContextManager* optix_context_manager_;
diff --git a/demos/optical_bench/src/test_pattern_frame.cpp b/demos/optical_bench/src/test_pattern_frame.cpp
index 751d7ee9a04e51f0cbac324eb3b407af2d1eee3c..52cf0993bda3b4cf240f587b3000e1a5c264aac8 100644
--- a/demos/optical_bench/src/test_pattern_frame.cpp
+++ b/demos/optical_bench/src/test_pattern_frame.cpp
@@ -98,8 +98,6 @@ TestPatternFrame::TestPatternFrame(phx::Scene* scene,
   selector->SetGrab([this](phx::Transform*, glm::mat4) { grabbed_ = true; });
   selector->SetRelease([this]() { grabbed_ = false; });
 
-  selector->SetIntersectionDistance(1.5f);
-
   selector->SetCollider(
       glm::vec3(-size.x / 2.0f, -size.y / 2.0f, -size.z / 2.0f),
       glm::vec3(size.x / 2.0f, size.y / 2.0f, size.z / 2.0f));
diff --git a/demos/optical_bench/src/translation_helper.cpp b/demos/optical_bench/src/translation_helper.cpp
index cb8c462705f7c40e29c1de804856a0cb7b4e5be8..d1f9951d17227590f538acf778b0c2014d6ab9db 100644
--- a/demos/optical_bench/src/translation_helper.cpp
+++ b/demos/optical_bench/src/translation_helper.cpp
@@ -109,19 +109,22 @@ TranslationHelper::TranslationHelper(phx::Scene* scene, phx::Engine* engine) {
     glm::vec3 global_pos =
         root_t->GetGlobalTranslation() - obj_m->GetCenterAxisPos();
 
-    static char pos[] = "(+250.11cm, +250.11cm, +250.11cm)";
+    static char pos[] = "(+2500.11mm, +2500.11mm, +2500.11mm)";
     if (dist == 0.0f) {
-      snprintf(pos, sizeof(pos), "(%.1fcm, %.1fcm, Front)",
-               root_t->GetGlobalTranslation().x,
-               root_t->GetGlobalTranslation().y);
+      snprintf(pos, sizeof(pos), "(%.1fmm, %.1fmm, Front)",
+               root_t->GetGlobalTranslation().x * 1000,
+               root_t->GetGlobalTranslation().y * 1000);
     } else {
-      snprintf(pos, sizeof(pos), "(%.1fcm, %.1fcm, %.1fcm)", global_pos.x * 100,
-               global_pos.y * 100, dist * 100);
+      snprintf(pos, sizeof(pos), "(%.1fmm, %.1fmm, %.1fmm)",
+               global_pos.x * 1000, global_pos.y * 1000, dist * 1000);
     }
 
     text_node->SetText(std::string(pos));
   });
   text_node->SetBillboard(true);
+  // intially attach to x
+  text_trans->SetParent(x_rod_grip_->GetFirstComponent<phx::Transform>(),
+                        false);
 
   selector_x->SetGrab([this, text_trans](phx::Transform*, glm::mat4) {
     x_grip_start_ = parent_->GetGlobalTranslation().x;
@@ -242,6 +245,8 @@ void TranslationHelper::SetParent(phx::Transform* parent) {
 
   root_->GetFirstComponent<phx::Transform>()->SetParent(parent, false);
   root_->GetFirstComponent<phx::Transform>()->SetGlobalRotation(glm::vec3(0));
+
+  text_->GetFirstComponent<phx::Text>()->ManualUpdate();
 }
 
 bool TranslationHelper::SelectablePartOf(phx::Entity* o) {
diff --git a/resources/laser/pattern/Rainbow.png b/resources/laser/pattern/Circle.png
similarity index 100%
rename from resources/laser/pattern/Rainbow.png
rename to resources/laser/pattern/Circle.png
diff --git a/resources/laser/pattern/Rainbow_dir.png b/resources/laser/pattern/Circle_dir.png
similarity index 100%
rename from resources/laser/pattern/Rainbow_dir.png
rename to resources/laser/pattern/Circle_dir.png
diff --git a/resources/laser/pattern/Cross.png b/resources/laser/pattern/Cross.png
new file mode 100644
index 0000000000000000000000000000000000000000..325c6c0de3aea7185377816f2b046bb2e7c5e1c8
--- /dev/null
+++ b/resources/laser/pattern/Cross.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7e94bf7039c11ab1d7be9108ddc24cc80335f31a5439d07d103c1fcee4a92c2e
+size 260
diff --git a/resources/laser/pattern/Crosshair_dir.png b/resources/laser/pattern/Cross_dir.png
similarity index 100%
rename from resources/laser/pattern/Crosshair_dir.png
rename to resources/laser/pattern/Cross_dir.png
diff --git a/resources/laser/pattern/Crosshair.png b/resources/laser/pattern/Crosshair.png
deleted file mode 100644
index 5e56f6a9548cddf8f0258d4bcc5fa441450f2f5f..0000000000000000000000000000000000000000
--- a/resources/laser/pattern/Crosshair.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:770bc6710e3a0997795be7c7a8f19eca16f40562924df8daeeebe470cbc416c2
-size 246
diff --git a/resources/laser/pattern/RayTest.png b/resources/laser/pattern/Quad.png
similarity index 100%
rename from resources/laser/pattern/RayTest.png
rename to resources/laser/pattern/Quad.png
diff --git a/resources/laser/pattern/RayTest_dir.png b/resources/laser/pattern/Quad_dir.png
similarity index 100%
rename from resources/laser/pattern/RayTest_dir.png
rename to resources/laser/pattern/Quad_dir.png
diff --git a/resources/laser/target/Black.png b/resources/laser/target/Black.png
new file mode 100644
index 0000000000000000000000000000000000000000..863691b43adce7d67ae7744ff26993f4d08420e0
--- /dev/null
+++ b/resources/laser/target/Black.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d06886c23e79a94162356a4a6dc4a57a5737f14272a9ce9b369d040cf5045133
+size 134
diff --git a/resources/laser/target/CheckerRed.png b/resources/laser/target/CheckerRed.png
deleted file mode 100644
index 4939b2d37437817c55f293a97314da43842853b7..0000000000000000000000000000000000000000
--- a/resources/laser/target/CheckerRed.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:1b3679858717cc583eed5734686ed6c6079ca0d2bf586594bebcd4a1405ae9de
-size 142
diff --git a/resources/models/opticalBench/laser/target.obj b/resources/models/opticalBench/laser/target.obj
index 665087703892e9800fa9d07bd661e15dbfb38081..f5c157aa6e7cf2e7292f430211aad92294d19180 100644
--- a/resources/models/opticalBench/laser/target.obj
+++ b/resources/models/opticalBench/laser/target.obj
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:73f6845fd9e333316c5a4069e4e2151864826c45b394aac0bec92f90a8ee9969
-size 73995
+oid sha256:3f7b43b4966f2e7108d1135f6409411a1a54c8cd3a51119cebb7d1344835f600
+size 88797