diff --git a/applications/gui2/src/modules/camera.cpp b/applications/gui2/src/modules/camera.cpp
index 218b1d29cf43d3fd5b5a4ae8ab59a6ec595e25e1..670695023f9cbcb04cacd6326cf21e115e6d7300 100644
--- a/applications/gui2/src/modules/camera.cpp
+++ b/applications/gui2/src/modules/camera.cpp
@@ -76,11 +76,14 @@ void Camera::update(double delta) {
 				if (caps.count(Capability::TOUCH)) jmeta["Touch"] = nlohmann::json{{"icon", ENTYPO_ICON_MOUSE_POINTER},{"value", true}};
 				else jmeta.erase("Touch");
 
-				if (caps.count(Capability::MOVABLE)) jmeta["Movable"] = nlohmann::json{{"icon", ENTYPO_ICON_COMPASS},{"value", true}};
+				if (caps.count(Capability::MOVABLE)) jmeta["Movable"] = nlohmann::json{{"icon", ENTYPO_ICON_DIRECTION},{"value", true}};
 				else jmeta.erase("Movable");
 
 				if (caps.count(Capability::VR)) jmeta["VR"] = nlohmann::json{{"value", true}};
 				else jmeta.erase("VR");
+
+				if (caps.count(Capability::EQUI_RECT)) jmeta["360"] = nlohmann::json{{"icon", ENTYPO_ICON_COMPASS},{"value", true}};
+				else jmeta.erase("360");
 			}
 
 			std::map<ftl::data::Message,std::string> messages;
diff --git a/components/rgbd-sources/include/ftl/rgbd/capabilities.hpp b/components/rgbd-sources/include/ftl/rgbd/capabilities.hpp
index dd938fc1b77637ce83a135b0f16d1facdbe3f9f9..dacab1e522d841900bf2fd864f3db536d3af46e5 100644
--- a/components/rgbd-sources/include/ftl/rgbd/capabilities.hpp
+++ b/components/rgbd-sources/include/ftl/rgbd/capabilities.hpp
@@ -22,7 +22,9 @@ enum class Capability : int {
 	VR,			// Is a VR device, so provides own active pose etc
 	LIVE,		// Live, not recorded (removed from ftl file sources)
 	FUSED,		// Reconstruction has been performed
-	STREAMED	// Means it came from a stream and not device
+	STREAMED,	// Means it came from a stream and not device
+	EQUI_RECT,	// 360 rendered (Equirectangular Render)
+	STEREO		// Side-by-side stereo render
 };
 
 std::string capabilityName(Capability);
diff --git a/components/rgbd-sources/include/ftl/rgbd/frame.hpp b/components/rgbd-sources/include/ftl/rgbd/frame.hpp
index 74e06aaac3c5c4541232152fedcb7a02060eb97a..509c4bec8815a3b80d173af4ba90dd3910c1b025 100644
--- a/components/rgbd-sources/include/ftl/rgbd/frame.hpp
+++ b/components/rgbd-sources/include/ftl/rgbd/frame.hpp
@@ -105,6 +105,8 @@ class Frame : public ftl::data::Frame {
 	inline bool isMovable() const { return hasCapability(ftl::rgbd::Capability::MOVABLE); }
 	inline bool isTouchable() const { return hasCapability(ftl::rgbd::Capability::TOUCH); }
 	inline bool isVR() const { return hasCapability(ftl::rgbd::Capability::VR); }
+	inline bool is360() const { return hasCapability(ftl::rgbd::Capability::EQUI_RECT); }
+	inline bool isSideBySideStereo() const { return hasCapability(ftl::rgbd::Capability::STEREO); }
 
 	void upload(ftl::codecs::Channel c);
 
diff --git a/components/streams/src/renderers/screen_render.cpp b/components/streams/src/renderers/screen_render.cpp
index 056477c6c186b3037ae6a4aade24b90403cf6e46..c60bd704761e9d6f40ba725360e99844f03d4ad4 100644
--- a/components/streams/src/renderers/screen_render.cpp
+++ b/components/streams/src/renderers/screen_render.cpp
@@ -33,10 +33,14 @@ ScreenRender::ScreenRender(ftl::render::Source *host, ftl::stream::Feed *feed)
 	);
 
 	intrinsics_ = ftl::create<ftl::Configurable>(host_, "intrinsics");
-	refresh_calibration_ = false;
 
 	intrinsics_->onAny({"focal","width","height"}, [this]() {
-		refresh_calibration_ = true;
+		calibration_uptodate_.clear();
+	});
+
+	renderer_->value("projection", 0);
+	renderer_->onAny({"projection"}, [this]() {
+		calibration_uptodate_.clear();
 	});
 
 	filter_ = nullptr;
@@ -85,18 +89,17 @@ bool ScreenRender::retrieve(ftl::data::Frame &frame_out) {
 	if (sets.size() > 0) {
 		ftl::rgbd::Frame &rgbdframe = frame_out.cast<ftl::rgbd::Frame>();
 
-		if (!frame_out.has(Channel::Calibration) || refresh_calibration_) {
-			refresh_calibration_ = false;
+		if (!frame_out.has(Channel::Calibration) || calibration_uptodate_.test_and_set()) {
 			rgbdframe.setLeft() = ftl::rgbd::Camera::from(intrinsics_);
 
-			if (!frame_out.has(Channel::Capabilities)) {
-				auto &cap = frame_out.create<std::unordered_set<Capability>>(Channel::Capabilities);
-				cap.emplace(Capability::VIDEO);
-				cap.emplace(Capability::MOVABLE);
-				cap.emplace(Capability::ADJUSTABLE);
-				cap.emplace(Capability::VIRTUAL);
-				cap.emplace(Capability::LIVE);
-			}
+			auto &cap = frame_out.create<std::unordered_set<Capability>>(Channel::Capabilities);
+			cap.clear();
+			cap.emplace(Capability::VIDEO);
+			cap.emplace(Capability::MOVABLE);
+			cap.emplace(Capability::ADJUSTABLE);
+			cap.emplace(Capability::VIRTUAL);
+			cap.emplace(Capability::LIVE);
+			if (renderer_->value("projection", 0) == int(ftl::rgbd::Projection::EQUIRECTANGULAR)) cap.emplace(Capability::EQUI_RECT);
 
 			auto &meta = frame_out.create<std::map<std::string,std::string>>(Channel::MetaData);
 			meta["name"] = host_->value("name", host_->getID());
diff --git a/components/streams/src/renderers/screen_render.hpp b/components/streams/src/renderers/screen_render.hpp
index 4de8e9609f54847dd9c41285f1e3dc12b7f2a355..e8a5635a57d9c96bf327625c32ad3e9785d182a6 100644
--- a/components/streams/src/renderers/screen_render.hpp
+++ b/components/streams/src/renderers/screen_render.hpp
@@ -34,7 +34,7 @@ class ScreenRender : public ftl::render::BaseSourceImpl {
 	ftl::Configurable *intrinsics_;
 	uint32_t my_id_;
 	ftl::operators::Graph *post_pipe_;
-	bool refresh_calibration_;
+	std::atomic_flag calibration_uptodate_;
 };
 
 }