diff --git a/.babelrc b/.babelrc
index 4ffef06dbfb53d6210bc68ec4218f3b3d74108fc..88f0c0ac53f9a7036d9ad906f256c6668ca51d98 100644
--- a/.babelrc
+++ b/.babelrc
@@ -1,3 +1,9 @@
 {
-  "presets": ["env", "react"]
+  "presets": [
+    "react",
+    ["env", {
+      "exclude": ["transform-regenerator"],
+      "useBuiltins": true
+    }]
+  ]
 }
diff --git a/public/room.html b/public/room.html
index d13ff038a2dd1a0bf8fa7b8a5f0eaa34811907fd..0b96981873dda6a51ecfb559275a31adbbc4a051 100644
--- a/public/room.html
+++ b/public/room.html
@@ -88,6 +88,10 @@
                 ></a-entity>
             </script>
 
+            <script id="video-template" type="text/html">
+                <a-entity class="video" geometry="primitive: plane;" material="side: double" networked-video-player></a-entity>
+            </script>
+
             <script id="nametag-template" type="text/html">
                 <a-entity
                     class="nametag"
diff --git a/src/components/audio-feedback.js b/src/components/audio-feedback.js
index 2b24abd946d7d8fe2ec35c018c4262f64fd00229..4f5a5c9e53fdf54ad22b9c90dbce4024606e6555 100644
--- a/src/components/audio-feedback.js
+++ b/src/components/audio-feedback.js
@@ -18,7 +18,6 @@ AFRAME.registerComponent("networked-audio-analyser", {
           );
         }
         const ownerId = networkedEl.components.networked.data.owner;
-        console.log("audio Analyser for " + ownerId);
         return NAF.connection.adapter.getMediaStream(ownerId);
       })
       .then(stream => {
@@ -60,11 +59,9 @@ AFRAME.registerComponent("matcolor-audio-feedback", {
     this.onAudioFrequencyChange = this.onAudioFrequencyChange.bind(this);
 
     this.el.addEventListener("model-loaded", () => {
-      console.log(this.data.objectName);
       this.mat = this.el.object3D.getObjectByName(
         this.data.objectName
       ).material;
-      console.log("mat", this.mat);
     });
   },
 
diff --git a/src/components/mute-mic.js b/src/components/mute-mic.js
index 12c1ad397229b512606b40e4f5cfa86637935396..272b40e3b41f5d4aa49d7107b6aa41785b8d5596 100644
--- a/src/components/mute-mic.js
+++ b/src/components/mute-mic.js
@@ -32,7 +32,6 @@ AFRAME.registerComponent("mute-mic", {
 
   play: function() {
     const { eventSrc, toggleEvents, muteEvents, unmuteEvents } = this.data;
-    console.log(eventSrc);
     bindAllEvents(eventSrc, toggleEvents, this.onToggle);
     bindAllEvents(eventSrc, muteEvents, this.onMute);
     bindAllEvents(eventSrc, unmuteEvents, this.onUnmute);
diff --git a/src/components/networked-video-player.css b/src/components/networked-video-player.css
new file mode 100644
index 0000000000000000000000000000000000000000..6c10dd330f8265dff0da39de02ba854b550a9e36
--- /dev/null
+++ b/src/components/networked-video-player.css
@@ -0,0 +1,7 @@
+:local(.video) {
+  position: absolute;
+  bottom: 0;
+  height: 100px;
+  background: black;
+  display: none;
+}
diff --git a/src/components/networked-video-player.js b/src/components/networked-video-player.js
new file mode 100644
index 0000000000000000000000000000000000000000..e00dfbf15296c5d52736254706ad3ca44e73ca6f
--- /dev/null
+++ b/src/components/networked-video-player.js
@@ -0,0 +1,52 @@
+import styles from "./networked-video-player.css";
+
+const nafConnected = function() {
+  return new Promise(resolve => {
+    NAF.clientId
+      ? resolve()
+      : document.body.addEventListener("connected", resolve);
+  });
+};
+
+AFRAME.registerComponent("networked-video-player", {
+  schema: {},
+  async init() {
+    await nafConnected();
+
+    const networkedEl = NAF.utils.getNetworkedEntity(this.el);
+    if (!networkedEl) {
+      throw new Error(
+        "Video player must be added on a node, or a child of a node, with the `networked` component."
+      );
+    }
+
+    const ownerId = networkedEl.components.networked.data.owner;
+    const stream = await NAF.connection.adapter.getMediaStream(ownerId);
+    if (!stream) {
+      return;
+    }
+
+    const v = document.createElement("video");
+    v.classList.add(styles.video);
+    v.srcObject = stream;
+    document.body.appendChild(v);
+    v.play();
+
+    this.videoEl = v;
+
+    v.onloadedmetadata = () => {
+      const ratio = v.videoWidth / v.videoHeight;
+      this.el.setAttribute("geometry", {
+        width: ratio * 1,
+        height: 1
+      });
+      this.el.setAttribute("material", "src", v);
+    };
+  },
+
+  remove() {
+    if (this.videoEl) {
+      this.videoEl.parent.removeChild(this.videoEl);
+    }
+  }
+});
diff --git a/src/components/offset-relative-to.js b/src/components/offset-relative-to.js
new file mode 100644
index 0000000000000000000000000000000000000000..923eae3e5673488e8eceb726b9e91f592285f546
--- /dev/null
+++ b/src/components/offset-relative-to.js
@@ -0,0 +1,32 @@
+AFRAME.registerComponent("offset-relative-to", {
+  schema: {
+    target: {
+      type: "selector"
+    },
+    offset: {
+      type: "vec3"
+    },
+    on: {
+      type: "string"
+    }
+  },
+  init() {
+    this.updateOffset();
+    this.el.sceneEl.addEventListener(
+      this.data.on,
+      this.updateOffset.bind(this)
+    );
+  },
+  updateOffset() {
+    const offsetVector = new THREE.Vector3().copy(this.data.offset);
+    this.data.target.object3D.localToWorld(offsetVector);
+    this.el.setAttribute("position", offsetVector);
+
+    const headWorldRotation = this.data.target.object3D.getWorldRotation();
+    this.el.setAttribute("rotation", {
+      x: headWorldRotation.x * THREE.Math.RAD2DEG,
+      y: headWorldRotation.y * THREE.Math.RAD2DEG,
+      z: headWorldRotation.z * THREE.Math.RAD2DEG
+    });
+  }
+});
diff --git a/src/index.js b/src/index.js
index ca84a32ebbb202586f0b3942afa8f70718941004..41760f90bd34b7b6e71b524cec3f48a7ef673d37 100644
--- a/src/index.js
+++ b/src/index.js
@@ -19,6 +19,8 @@ import "./components/body-controller";
 import "./components/hand-controls2";
 import "./components/character-controller";
 import "./components/split-axis-events";
+import "./components/networked-video-player";
+import "./components/offset-relative-to";
 import "./systems/personal-space-bubble";
 
 import registerNetworkScheams from "./network-schemas";
@@ -29,8 +31,30 @@ import Config from "./config";
 registerNetworkScheams();
 registerInputMappings();
 
+function shareScreen() {
+  const track = NAF.connection.adapter.localMediaStream.getVideoTracks()[0];
+
+  const id = `${NAF.clientId}-screen`;
+  let entity = document.getElementById(id);
+  if (!entity) {
+    const sceneEl = document.querySelector("a-scene");
+    entity = document.createElement("a-entity");
+    entity.id = id;
+    entity.setAttribute("offset-relative-to", {
+      target: "#head",
+      offset: "0 0 -2",
+      on: "action_share_screen"
+    });
+    entity.setAttribute("networked", { template: "#video-template" });
+    sceneEl.appendChild(entity);
+  }
+
+  track.enabled = !track.enabled;
+  entity.setAttribute("visible", track.enabled);
+}
+
 window.App = {
-  onSceneLoad() {
+  async onSceneLoad() {
     const qs = queryString.parse(location.search);
     const scene = document.querySelector("a-scene");
 
@@ -60,7 +84,25 @@ window.App = {
 
     document.body.addEventListener("connected", App.onConnect);
 
+    scene.addEventListener("action_share_screen", shareScreen);
+
+    const mediaStream = await navigator.mediaDevices.getUserMedia({
+      audio: true,
+      video:
+        qs.screen === "true"
+          ? { mediaSource: "screen", height: 720, frameRate: 30 }
+          : false
+    });
+
+    // Don't send video by deafult
+    const videoTracks = mediaStream.getVideoTracks();
+    if (videoTracks.length) {
+      videoTracks[0].enabled = false;
+    }
+
     scene.components["networked-scene"].connect();
+    // @TODO ideally the adapter should exist before connect, but it currently doesnt so we have to do this after calling connect. This might be a race condition in other adapters.
+    NAF.connection.adapter.setLocalMediaStream(mediaStream);
   },
 
   onConnect() {
diff --git a/src/input-mappings.js b/src/input-mappings.js
index b4a0dbe6dd3dfcc3eaa6238f297f8ec69c335d2b..d57ff6496b1d35bf0a435b06cfdb74dd807008e6 100644
--- a/src/input-mappings.js
+++ b/src/input-mappings.js
@@ -31,6 +31,7 @@ export default function registerInputMappings() {
           m_press: "action_mute",
           q_press: "action_snap_rotate_left",
           e_press: "action_snap_rotate_right",
+          v_press: "action_share_screen",
           w_down: "action_move_forward",
           w_up: "action_dont_move_forward",
           a_down: "action_move_left",
diff --git a/src/network-schemas.js b/src/network-schemas.js
index 987f0afe385a141f519621cb016e812b841b8bfa..d93c552cb6b90a68ee158c66cd93475338c60408 100644
--- a/src/network-schemas.js
+++ b/src/network-schemas.js
@@ -19,6 +19,11 @@ function registerNetworkSchemas() {
     template: "#left-hand-template",
     components: ["position", "rotation", "visible"]
   });
+
+  NAF.schemas.add({
+    template: "#video-template",
+    components: ["position", "rotation", "visible"]
+  });
 }
 
 export default registerNetworkSchemas;