diff --git a/src/components/camera-focus-button.js b/src/components/camera-focus-button.js
new file mode 100644
index 0000000000000000000000000000000000000000..8d03357c329fff7104df0af87c11bcbf162769dc
--- /dev/null
+++ b/src/components/camera-focus-button.js
@@ -0,0 +1,39 @@
+AFRAME.registerComponent("camera-focus-button", {
+  schema: {
+    track: { default: false }
+  },
+
+  init() {
+    this.cameraSystem = this.el.sceneEl.systems.cameras;
+
+    NAF.utils.getNetworkedEntity(this.el).then(networkedEl => {
+      this.targetEl = networkedEl;
+    });
+
+    this.onClick = () => {
+      if (!this.cameraSystem) return;
+
+      const currentCamera = this.cameraSystem.getCurrent();
+      if (!currentCamera) return;
+
+      currentCamera.components["camera-tool"].focus(this.targetEl, this.data.track);
+    };
+  },
+
+  tick() {
+    const isVisible = this.el.getAttribute("visible");
+    const shouldBeVisible = !!(this.cameraSystem && this.cameraSystem.getCurrent());
+
+    if (isVisible !== shouldBeVisible) {
+      this.el.setAttribute("visible", shouldBeVisible);
+    }
+  },
+
+  play() {
+    this.el.addEventListener("grab-start", this.onClick);
+  },
+
+  pause() {
+    this.el.removeEventListener("grab-start", this.onClick);
+  }
+});
diff --git a/src/components/camera-tool.js b/src/components/camera-tool.js
index 2972d7fcd63b876aeaf22db191d998edb3183351..8f14d2ac1da7f5cc5b6503f44918b752fab983b7 100644
--- a/src/components/camera-tool.js
+++ b/src/components/camera-tool.js
@@ -88,6 +88,12 @@ AFRAME.registerComponent("camera-tool", {
       selfieScreen.scale.set(-2, 2, 2);
       this.el.setObject3D("selfieScreen", selfieScreen);
 
+      this.cameraSystem = this.el.sceneEl.systems.cameras;
+
+      if (this.cameraSystem) {
+        this.cameraSystem.register(this.el);
+      }
+
       this.updateRenderTargetNextTick = true;
     });
   },
@@ -100,12 +106,38 @@ AFRAME.registerComponent("camera-tool", {
     this.el.removeEventListener("stateadded", this.stateAdded);
   },
 
+  remove() {
+    if (this.cameraSystem) {
+      this.cameraSystem.deregister(this.el);
+    }
+  },
+
   stateAdded(evt) {
     if (evt.detail === "activated") {
       this.takeSnapshotNextTick = true;
     }
   },
 
+  focus(el, track) {
+    this.lookAt(el);
+
+    if (track) {
+      this.trackTarget = el;
+
+      this.trackTarget.addEventListener("componentremoved", e => {
+        if (e.detail.name === "position" && e.target === this.trackTarget) {
+          this.trackTarget = null;
+        }
+      });
+    }
+  },
+
+  lookAt(el) {
+    const targetPos = new THREE.Vector3();
+    targetPos.setFromMatrixPosition(el.object3D.matrixWorld);
+    this.el.object3D.lookAt(targetPos);
+  },
+
   tick() {
     const grabber = this.el.components.grabbable.grabbers[0];
     if (grabber && !!pathsMap[grabber.id]) {
@@ -114,6 +146,10 @@ AFRAME.registerComponent("camera-tool", {
         this.takeSnapshotNextTick = true;
       }
     }
+
+    if (this.trackTarget) {
+      this.lookAt(this.trackTarget);
+    }
   },
 
   tock: (function() {
diff --git a/src/hub.html b/src/hub.html
index 6ecee5f04f1d91ab168c22853497067f5923d52d..68c89358dea3278a3eaac1020b8a1ad70958e36c 100644
--- a/src/hub.html
+++ b/src/hub.html
@@ -188,6 +188,12 @@
                         <a-entity class="freeze-menu" visible-while-frozen="withinDistance: 3;">
                             <a-entity mixin="rounded-text-action-button" pin-networked-object-button="labelSelector:.pin-button-label; hideWhenPinnedSelector:.hide-when-pinned; uiSelector:.interactable-ui" position="0 0.125 0.01"> </a-entity>
                             <a-entity class="pin-button-label" text=" value:pin; width:1.75; align:center;" text-raycast-hack position="0 0.125 0.02"></a-entity>
+                            <a-entity mixin="rounded-text-button" camera-focus-button="track: false" position="-0.25 0.375 0.01">
+                                <a-entity text=" value:focus; width:1.75; align:center;" text-raycast-hack position="0 0 0.02"></a-entity>
+                            </a-entity>
+                            <a-entity mixin="rounded-text-button" camera-focus-button="track: true" position="0.25 0.375 0.01">
+                                <a-entity text=" value:track; width:1.75; align:center;" text-raycast-hack position="0 0 0.02"></a-entity>
+                            </a-entity>
                             <a-entity mixin="rounded-text-button" class="hide-when-pinned" remove-networked-object-button position="0 -0.125 0.01"> </a-entity>
                             <a-entity text=" value:remove; width:1.75; align:center;" class="hide-when-pinned" text-raycast-hack position="0 -0.125 0.02"></a-entity>
                         </a-entity>
diff --git a/src/hub.js b/src/hub.js
index 553f2a3a1fa72b5d5082d6ae391fcf2aefa165b0..5229110fa50ce615603ff57bba5b5013745fca5a 100644
--- a/src/hub.js
+++ b/src/hub.js
@@ -59,6 +59,7 @@ import "./components/position-at-box-shape-border";
 import "./components/pinnable";
 import "./components/pin-networked-object-button";
 import "./components/remove-networked-object-button";
+import "./components/camera-focus-button";
 import "./components/destroy-at-extreme-distances";
 import "./components/gamma-factor";
 import "./components/visible-to-owner";
@@ -83,6 +84,7 @@ import "./systems/nav";
 import "./systems/personal-space-bubble";
 import "./systems/app-mode";
 import "./systems/exit-on-blur";
+import "./systems/cameras";
 import "./systems/userinput/userinput";
 
 import "./gltf-component-mappings";