diff --git a/src/assets/stylesheets/presence-log.scss b/src/assets/stylesheets/presence-log.scss
index 2738cb3583eee8fe193d652232af609773a983a5..bed6d5cd50cae9b07dd406009c01917ac48477d8 100644
--- a/src/assets/stylesheets/presence-log.scss
+++ b/src/assets/stylesheets/presence-log.scss
@@ -34,6 +34,25 @@
     @media (max-width: 1000px) {
       max-width: 75%;
     }
+
+    &:local(.media) {
+      display: flex;
+      align-items: center;
+      min-height: 35px;
+
+      :local(.mediaBody) {
+        display: flex;
+        flex-direction: column;
+      }
+
+      img {
+        height: 35px;
+        margin-right: 8px;
+        border: 2px solid rgba(255,255,255,0.15);
+        display: block;
+        border-radius: 5px;
+      }
+    }
   }
 
   :local(.expired) {
diff --git a/src/components/camera-tool.js b/src/components/camera-tool.js
index 2ead31c99657728c3128ea63c6489e1bcf9b240b..eacfaf8f443a0131cef4cd189f0a05e5c9a58a20 100644
--- a/src/components/camera-tool.js
+++ b/src/components/camera-tool.js
@@ -156,6 +156,13 @@ AFRAME.registerComponent("camera-tool", {
         renderer.readRenderTargetPixels(this.renderTarget, 0, 0, width, height, this.snapPixels);
         pixelsToPNG(this.snapPixels, width, height).then(file => {
           const { entity, orientation } = addMedia(file, "#interactable-media", undefined, true);
+          entity.addEventListener(
+            "media_resolved",
+            () => {
+              this.el.emit("photo_taken", entity.components["media-loader"].data.src);
+            },
+            { once: true }
+          );
           orientation.then(() => {
             entity.object3D.position.copy(this.el.object3D.position).add(new THREE.Vector3(0, -0.5, 0));
             entity.object3D.rotation.copy(this.el.object3D.rotation);
diff --git a/src/hub.js b/src/hub.js
index 23ad9777ce407af238c9752de0c0af863027ec9d..9ffcc05046c34502883eb43c20514dcfb083a3f4 100644
--- a/src/hub.js
+++ b/src/hub.js
@@ -474,11 +474,11 @@ document.addEventListener("DOMContentLoaded", async () => {
     NAF.connection.adapter.onData(data);
   });
 
-  hubPhxChannel.on("message", data => {
-    const userInfo = hubPhxPresence.state[data.session_id];
+  hubPhxChannel.on("message", ({ session_id, type, body }) => {
+    const userInfo = hubPhxPresence.state[session_id];
     if (!userInfo) return;
 
-    addToPresenceLog({ type: "message", name: userInfo.metas[0].profile.displayName, body: data.body });
+    addToPresenceLog({ name: userInfo.metas[0].profile.displayName, type, body });
   });
 
   // Reticulum global channel
diff --git a/src/react-components/presence-log.js b/src/react-components/presence-log.js
index bf9a7b86cb2324546eba0d75c3c152d0b01db4c5..d46550957f1eb7918b9310263ada1a2f64537dfb 100644
--- a/src/react-components/presence-log.js
+++ b/src/react-components/presence-log.js
@@ -42,13 +42,34 @@ export default class PresenceLog extends Component {
             <b>{e.oldName}</b> <FormattedMessage id="presence.name_change" /> <b>{e.newName}</b>.
           </div>
         );
-      case "message":
+      case "chat":
         return (
           <div key={e.key} className={classNames(entryClasses)}>
             <b>{e.name}</b>:{" "}
             <Linkify properties={{ target: "_blank", rel: "noopener referrer" }}>{toEmojis(e.body)}</Linkify>
           </div>
         );
+      case "spawn": {
+        const { src } = e.body;
+        return (
+          <div key={e.key} className={classNames(entryClasses, styles.media)}>
+            <a href={src} target="_blank" rel="noopener noreferrer">
+              <img src={src} />
+            </a>
+            <div className={styles.mediaBody}>
+              <span>
+                <b>{e.name}</b>:
+              </span>
+              <span>
+                {"took a "}
+                <a href={src} target="_blank" rel="noopener noreferrer">
+                  photo
+                </a>
+              </span>
+            </div>
+          </div>
+        );
+      }
     }
   };
 
diff --git a/src/scene-entry-manager.js b/src/scene-entry-manager.js
index 5b62434849bd3427869f741f2cb5fba03e658398..45ae819bd133276e9e1876da5c0b290101ef2b77 100644
--- a/src/scene-entry-manager.js
+++ b/src/scene-entry-manager.js
@@ -278,6 +278,11 @@ export default class SceneEntryManager {
       });
       this.scene.appendChild(entity);
     });
+
+    this.scene.addEventListener("photo_taken", e => {
+      console.log(e);
+      this.hubChannel.sendMessage({ src: e.detail }, "spawn");
+    });
   };
 
   _spawnAvatar = () => {
diff --git a/src/utils/hub-channel.js b/src/utils/hub-channel.js
index 8b28c5db8ff7c1e62ff4b64761b4f17349e89a8c..350be0aa1facf770f21e4ba26cd1c0b34d9b15db 100644
--- a/src/utils/hub-channel.js
+++ b/src/utils/hub-channel.js
@@ -91,9 +91,9 @@ export default class HubChannel {
     this.channel.push("events:profile_updated", { profile: this.store.state.profile });
   };
 
-  sendMessage = body => {
-    if (body === "") return;
-    this.channel.push("message", { body });
+  sendMessage = (body, type = "chat") => {
+    if (!body) return;
+    this.channel.push("message", { body, type });
   };
 
   pin = (id, gltfNode) => {