diff --git a/SDK/C++/CMakeLists.txt b/SDK/C++/CMakeLists.txt
index 8bec8ee83108171e91209a80092813a0915d8466..b64eae3d9a119df248378b8b1e41f2b271187ba1 100644
--- a/SDK/C++/CMakeLists.txt
+++ b/SDK/C++/CMakeLists.txt
@@ -6,6 +6,9 @@ add_library(voltu SHARED
 	private/image_impl.cpp
 	private/observer_impl.cpp
 	private/pointcloud_impl.cpp
+	private/pipeline_impl.cpp
+	private/operator_impl.cpp
+	private/property_impl.cpp
 )
 
 target_include_directories(voltu
diff --git a/SDK/C++/private/feed_impl.cpp b/SDK/C++/private/feed_impl.cpp
index 001b2974f802d5d46091c9b8f820fdba72729ad3..edbb1d884f35dc5e4db0a4cf3aff9ac5b0370cd1 100644
--- a/SDK/C++/private/feed_impl.cpp
+++ b/SDK/C++/private/feed_impl.cpp
@@ -1,25 +1,82 @@
 #include "feed_impl.hpp"
+#include <voltu/types/errors.hpp>
 
-using voltu::internal::FeedImpl;
+using voltu::internal::InputFeedImpl;
+using voltu::internal::OutputFeedImpl;
 
-FeedImpl::FeedImpl(ftl::stream::Feed* feed, uint32_t fsid)
+// ==== Input ==================================================================
+
+InputFeedImpl::InputFeedImpl(ftl::stream::Feed* feed, uint32_t fsid)
  : feed_(feed)
 {
 	fsids_.insert(fsid);
 }
 
-FeedImpl::~FeedImpl()
+InputFeedImpl::~InputFeedImpl()
 {
-	remove();
+	//remove();
 }
 	
-std::string FeedImpl::getURI()
+std::string InputFeedImpl::getURI()
 {
 	return feed_->getURI(*fsids_.begin());
 }
 
-void FeedImpl::remove()
+void InputFeedImpl::remove()
+{
+	throw voltu::exceptions::NotImplemented();
+}
+
+void InputFeedImpl::submit(const voltu::FramePtr &frame)
+{
+	throw voltu::exceptions::ReadOnly();
+}
+
+voltu::FeedType InputFeedImpl::type()
+{
+	throw voltu::exceptions::NotImplemented();
+}
+
+voltu::PropertyPtr InputFeedImpl::property(voltu::FeedProperty)
+{
+	throw voltu::exceptions::NotImplemented();
+}
+
+// ==== Output =================================================================
+
+OutputFeedImpl::OutputFeedImpl(ftl::stream::Feed* feed, const std::string &uri)
+ : feed_(feed), uri_(uri)
+{
+
+}
+
+OutputFeedImpl::~OutputFeedImpl()
+{
+	//remove();
+}
+	
+std::string OutputFeedImpl::getURI()
+{
+	return uri_;
+}
+
+void OutputFeedImpl::remove()
 {
+	throw voltu::exceptions::NotImplemented();
+}
+
+void OutputFeedImpl::submit(const voltu::FramePtr &frame)
+{
+	throw voltu::exceptions::NotImplemented();
+}
 
+voltu::FeedType OutputFeedImpl::type()
+{
+	throw voltu::exceptions::NotImplemented();
+}
+
+voltu::PropertyPtr OutputFeedImpl::property(voltu::FeedProperty)
+{
+	throw voltu::exceptions::NotImplemented();
 }
 
diff --git a/SDK/C++/private/feed_impl.hpp b/SDK/C++/private/feed_impl.hpp
index 6eae89a31f769385770854475c416b74df5abdaa..ba0ab72d7d43eebaa03c5794b76d2b762e507706 100644
--- a/SDK/C++/private/feed_impl.hpp
+++ b/SDK/C++/private/feed_impl.hpp
@@ -9,20 +9,47 @@ namespace voltu
 namespace internal
 {
 
-class FeedImpl : public voltu::Feed
+class InputFeedImpl : public voltu::Feed
 {
 public:
-	FeedImpl(ftl::stream::Feed*, uint32_t fsid);
-	~FeedImpl();
+	InputFeedImpl(ftl::stream::Feed*, uint32_t fsid);
+	~InputFeedImpl() override;
 	
 	std::string getURI() override;
 
 	void remove() override;
 
+	void submit(const voltu::FramePtr &frame) override;
+
+	voltu::FeedType type() override;
+
+	voltu::PropertyPtr property(voltu::FeedProperty) override;
+
 private:
 	ftl::stream::Feed *feed_;
 	std::unordered_set<uint32_t> fsids_;
 };
 
+class OutputFeedImpl : public voltu::Feed
+{
+public:
+	OutputFeedImpl(ftl::stream::Feed*, const std::string &uri);
+	~OutputFeedImpl() override;
+	
+	std::string getURI() override;
+
+	void remove() override;
+
+	void submit(const voltu::FramePtr &frame) override;
+
+	voltu::FeedType type() override;
+
+	voltu::PropertyPtr property(voltu::FeedProperty) override;
+
+private:
+	ftl::stream::Feed *feed_;
+	std::string uri_;
+};
+
 }
 }
diff --git a/SDK/C++/private/frame_impl.cpp b/SDK/C++/private/frame_impl.cpp
index a6057f093bc149f9877e09cb096a6f661f8e39fe..0b0779b6a2a2d23a3250a0cb917e8486e88af4b9 100644
--- a/SDK/C++/private/frame_impl.cpp
+++ b/SDK/C++/private/frame_impl.cpp
@@ -24,6 +24,7 @@ std::list<voltu::ImagePtr> FrameImpl::getImageSet(voltu::Channel c)
 	{
 	case voltu::Channel::kColour	: channel = ftl::codecs::Channel::Colour; break;
 	case voltu::Channel::kDepth		: channel = ftl::codecs::Channel::Depth; break;
+	case voltu::Channel::kNormals	: channel = ftl::codecs::Channel::Normals; break;
 	default: throw voltu::exceptions::BadImageChannel();
 	}
 
@@ -49,6 +50,25 @@ voltu::PointCloudPtr FrameImpl::getPointCloud(voltu::PointCloudFormat cloudfmt,
 	return nullptr;
 }
 
+std::vector<std::string> FrameImpl::getMessages()
+{
+	std::vector<std::string> msgs;
+
+	for (const auto &fs : framesets_)
+	{
+		for (const auto &f : fs->frames)
+		{
+			if (f.hasChannel(ftl::codecs::Channel::Messages))
+			{
+				const auto &m = f.get<std::vector<std::string>>(ftl::codecs::Channel::Messages);
+				msgs.insert(msgs.end(), m.begin(), m.end());
+			}
+		}
+	}
+
+	return msgs;
+}
+
 void FrameImpl::pushFrameSet(const std::shared_ptr<ftl::data::FrameSet> &fs)
 {
 	framesets_.push_back(fs);
diff --git a/SDK/C++/private/frame_impl.hpp b/SDK/C++/private/frame_impl.hpp
index ee6aec4a7da48c086df4c6475125ab7cc36efc95..1b6074a8a6cc1c23dd9e317c09cf1a5596bd467c 100644
--- a/SDK/C++/private/frame_impl.hpp
+++ b/SDK/C++/private/frame_impl.hpp
@@ -14,12 +14,14 @@ class FrameImpl : public voltu::Frame
 {
 public:
 	FrameImpl();
-	~FrameImpl();
+	~FrameImpl() override;
 
 	std::list<voltu::ImagePtr> getImageSet(voltu::Channel) override;
 
 	voltu::PointCloudPtr getPointCloud(voltu::PointCloudFormat cloudfmt, voltu::PointFormat pointfmt) override;
 
+	std::vector<std::string> getMessages() override;
+
 	int64_t getTimestamp() override;
 
 	void pushFrameSet(const std::shared_ptr<ftl::data::FrameSet> &fs);
diff --git a/SDK/C++/private/image_impl.cpp b/SDK/C++/private/image_impl.cpp
index f8bf63a27001f0540f008eed4a5f4bb5bd8dfb75..76dea9a2234933e6dad53bdaecd7e83f52e257fd 100644
--- a/SDK/C++/private/image_impl.cpp
+++ b/SDK/C++/private/image_impl.cpp
@@ -30,6 +30,10 @@ voltu::ImageData ImageImpl::getHost()
 	{
 		r.format = voltu::ImageFormat::kFloat32;
 	}
+	else if (m.type() == CV_16FC4)
+	{
+		r.format = voltu::ImageFormat::kFloat16_4;
+	}
 
 	return r;
 }
@@ -51,6 +55,10 @@ voltu::ImageData ImageImpl::getDevice()
 	{
 		r.format = voltu::ImageFormat::kFloat32;
 	}
+	else if (m.type() == CV_16FC4)
+	{
+		r.format = voltu::ImageFormat::kFloat16_4;
+	}
 
 	return r;
 }
@@ -66,6 +74,7 @@ voltu::Channel ImageImpl::getChannel()
 	{
 	case ftl::codecs::Channel::Colour		: return voltu::Channel::kColour;
 	case ftl::codecs::Channel::Depth		: return voltu::Channel::kDepth;
+	case ftl::codecs::Channel::Normals		: return voltu::Channel::kNormals;
 	default: return voltu::Channel::kInvalid;
 	}
 }
diff --git a/SDK/C++/private/image_impl.hpp b/SDK/C++/private/image_impl.hpp
index 189a1c19fd13366e8728a50494a6ee9b1e2c4c5f..ac9a33c46cfb9211ab7d9896b65971b6a550e952 100644
--- a/SDK/C++/private/image_impl.hpp
+++ b/SDK/C++/private/image_impl.hpp
@@ -12,7 +12,7 @@ class ImageImpl : public voltu::Image
 {
 public:
 	ImageImpl(const ftl::rgbd::Frame&, ftl::codecs::Channel c);
-	~ImageImpl();
+	~ImageImpl() override;
 
 	voltu::ImageData getHost() override;
 
diff --git a/SDK/C++/private/observer_impl.cpp b/SDK/C++/private/observer_impl.cpp
index 325d24cf39eb8a0a32e5390f6ef0c84a5ab4a7f8..abdd38f8f03ae81c4703ae8aa227dad5a1b6000e 100644
--- a/SDK/C++/private/observer_impl.cpp
+++ b/SDK/C++/private/observer_impl.cpp
@@ -9,9 +9,10 @@ using voltu::internal::ObserverImpl;
 using ftl::rgbd::Capability;
 
 ObserverImpl::ObserverImpl(ftl::Configurable *base)
+ : id_(254)  // FIXME: Allocate this
 {
 	pool_ = new ftl::data::Pool(2,5);
-	rend_ = ftl::create<ftl::render::CUDARender>(base, "camN");
+	rend_ = ftl::create<ftl::render::CUDARender>(base, "camN");  // FIXME: Generate name properly
 
 	intrinsics_.fx = 700.0f;
 	intrinsics_.fy = 700.0f;
@@ -28,7 +29,9 @@ ObserverImpl::ObserverImpl(ftl::Configurable *base)
 
 ObserverImpl::~ObserverImpl()
 {
-
+	frameset_.reset();
+	delete rend_;
+	delete pool_;
 }
 
 void ObserverImpl::setResolution(uint32_t w, uint32_t h)
diff --git a/SDK/C++/private/observer_impl.hpp b/SDK/C++/private/observer_impl.hpp
index 817e2babdb45b2beb23380f7b6e4ee20ca7e87c2..dfccf7e59476ffd405e3a222b3e41e5d30d1f58a 100644
--- a/SDK/C++/private/observer_impl.hpp
+++ b/SDK/C++/private/observer_impl.hpp
@@ -17,7 +17,7 @@ class ObserverImpl : public voltu::Observer
 public:
 	explicit ObserverImpl(ftl::Configurable *base);
 
-	~ObserverImpl();
+	~ObserverImpl() override;
 
 	void setResolution(uint32_t w, uint32_t h) override;
 
diff --git a/SDK/C++/private/operator_impl.cpp b/SDK/C++/private/operator_impl.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..41f376c209d9d8ba7b5bfbd1c05a155890355643
--- /dev/null
+++ b/SDK/C++/private/operator_impl.cpp
@@ -0,0 +1,22 @@
+#include "operator_impl.hpp"
+#include "property_impl.hpp"
+#include <voltu/types/errors.hpp>
+
+using voltu::internal::OperatorImpl;
+
+OperatorImpl::OperatorImpl(ftl::Configurable *cfg)
+ : cfg_(cfg)
+{
+	
+}
+
+OperatorImpl::~OperatorImpl()
+{
+
+}
+
+voltu::PropertyPtr OperatorImpl::property(const std::string &name)
+{
+	if (!cfg_->has(name)) throw voltu::exceptions::BadPropertyName();
+	return std::make_shared<voltu::internal::CfgPropertyImpl>(cfg_, name);
+}
diff --git a/SDK/C++/private/operator_impl.hpp b/SDK/C++/private/operator_impl.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..cfe5d26ab0f24d1dafeb8fec2f0c1d4ea67b5fac
--- /dev/null
+++ b/SDK/C++/private/operator_impl.hpp
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <voltu/operator.hpp>
+#include <ftl/configurable.hpp>
+
+#include <string>
+
+namespace voltu
+{
+namespace internal
+{
+
+class OperatorImpl : public voltu::Operator
+{
+public:
+	OperatorImpl(ftl::Configurable*);
+	~OperatorImpl() override;
+
+	voltu::PropertyPtr property(const std::string &name) override;
+
+private:
+	ftl::Configurable *cfg_;
+};
+
+}
+}
\ No newline at end of file
diff --git a/SDK/C++/private/pipeline_impl.cpp b/SDK/C++/private/pipeline_impl.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ea58b18587568640ec20ec4606a910245fc2c533
--- /dev/null
+++ b/SDK/C++/private/pipeline_impl.cpp
@@ -0,0 +1,58 @@
+#include "pipeline_impl.hpp"
+#include "frame_impl.hpp"
+#include "operator_impl.hpp"
+#include <voltu/types/errors.hpp>
+
+#include <ftl/operators/fusion.hpp>
+#include <ftl/operators/gt_analysis.hpp>
+
+using voltu::internal::PipelineImpl;
+
+PipelineImpl::PipelineImpl(ftl::Configurable *root)
+{
+	graph_ = ftl::create<ftl::operators::Graph>(root, "pipe1");
+}
+
+PipelineImpl::~PipelineImpl()
+{
+	delete graph_;
+}
+
+void PipelineImpl::submit(const voltu::FramePtr &frame)
+{
+	auto *fimp = dynamic_cast<voltu::internal::FrameImpl*>(frame.get());
+	if (!fimp)
+	{
+		throw voltu::exceptions::InvalidFrameObject();
+	}
+
+	const auto &sets = fimp->getInternalFrameSets();
+
+	if (sets.size() > 1) throw voltu::exceptions::IncompatibleOperation();
+
+	for (const auto &fs : sets)
+	{
+		ready_ = false;
+		graph_->queue(fs, [this]()
+		{
+			ready_ = true;
+		});
+	}
+}
+
+bool PipelineImpl::waitCompletion(int timeout)
+{
+	int count = timeout / 5;
+	while (!ready_ && --count >= 0) std::this_thread::sleep_for(std::chrono::milliseconds(5));
+	return ready_;
+}
+
+voltu::OperatorPtr PipelineImpl::appendOperator(voltu::OperatorId id)
+{
+	switch (id)
+	{
+	case voltu::OperatorId::kFusion			: return std::make_shared<voltu::internal::OperatorImpl>(graph_->append<ftl::operators::Fusion>("fusion"));
+	case voltu::OperatorId::kGTEvaluator	: return std::make_shared<voltu::internal::OperatorImpl>(graph_->append<ftl::operators::GTAnalysis>("gtanal"));
+	default: throw voltu::exceptions::NotImplemented();
+	}
+}
\ No newline at end of file
diff --git a/SDK/C++/private/pipeline_impl.hpp b/SDK/C++/private/pipeline_impl.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..98fe41c9018fe328c1cb296e3819d052a8808a73
--- /dev/null
+++ b/SDK/C++/private/pipeline_impl.hpp
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <voltu/pipeline.hpp>
+#include <ftl/operators/operator.hpp>
+
+namespace voltu
+{
+namespace internal
+{
+
+class PipelineImpl : public voltu::Pipeline
+{
+public:
+	PipelineImpl(ftl::Configurable *root);
+	~PipelineImpl() override;
+
+	void submit(const voltu::FramePtr &frame) override;
+
+	bool waitCompletion(int timeout) override;
+
+	voltu::OperatorPtr appendOperator(voltu::OperatorId id) override;
+
+private:
+	ftl::operators::Graph *graph_;
+	bool ready_ = false;
+};
+
+}
+}
\ No newline at end of file
diff --git a/SDK/C++/private/property_impl.cpp b/SDK/C++/private/property_impl.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bb3cd8dcd0364d458b7c6dd23be94719c7031f02
--- /dev/null
+++ b/SDK/C++/private/property_impl.cpp
@@ -0,0 +1,135 @@
+#include "property_impl.hpp"
+#include <voltu/types/errors.hpp>
+
+using voltu::internal::CfgPropertyImpl;
+//using voltu::internal::FloatCfgProperty;
+
+CfgPropertyImpl::CfgPropertyImpl(ftl::Configurable *cfg, const std::string &name)
+ : cfg_(cfg), name_(name)
+{
+
+}
+
+CfgPropertyImpl::~CfgPropertyImpl()
+{
+
+}
+
+void CfgPropertyImpl::setInt(int value)
+{
+	if (cfg_->is<int>(name_))
+	{
+		cfg_->set(name_, value);
+	}
+	else
+	{
+		throw voltu::exceptions::BadPropertyType();
+	}
+}
+
+void CfgPropertyImpl::setFloat(float value)
+{
+	if (cfg_->is<float>(name_))
+	{
+		cfg_->set(name_, value);
+	}
+	else
+	{
+		throw voltu::exceptions::BadPropertyType();
+	}
+}
+
+void CfgPropertyImpl::setString(const std::string &value)
+{
+	if (cfg_->is<std::string>(name_))
+	{
+		cfg_->set(name_, value);
+	}
+	else
+	{
+		throw voltu::exceptions::BadPropertyType();
+	}
+}
+
+void CfgPropertyImpl::setBool(bool value)
+{
+	if (cfg_->is<bool>(name_))
+	{
+		cfg_->set(name_, value);
+	}
+	else
+	{
+		throw voltu::exceptions::BadPropertyType();
+	}
+}
+
+int CfgPropertyImpl::getInt()
+{
+	if (cfg_->is<int>(name_))
+	{
+		return *cfg_->get<int>(name_);
+	}
+	else
+	{
+		throw voltu::exceptions::BadPropertyType();
+	}
+}
+
+float CfgPropertyImpl::getFloat()
+{
+	if (cfg_->is<float>(name_))
+	{
+		return *cfg_->get<float>(name_);
+	}
+	else
+	{
+		throw voltu::exceptions::BadPropertyType();
+	}
+}
+
+std::string CfgPropertyImpl::getString()
+{
+	if (cfg_->is<std::string>(name_))
+	{
+		return *cfg_->get<std::string>(name_);
+	}
+	else
+	{
+		throw voltu::exceptions::BadPropertyType();
+	}
+}
+
+bool CfgPropertyImpl::getBool()
+{
+	if (cfg_->is<bool>(name_))
+	{
+		return *cfg_->get<bool>(name_);
+	}
+	else
+	{
+		throw voltu::exceptions::BadPropertyType();
+	}
+}
+
+// ==== Float ====
+
+/*FloatCfgProperty::FloatCfgProperty(ftl::Configurable *cfg, const std::string &name)
+ : CfgPropertyImpl(cfg, name)
+{
+
+}
+
+FloatCfgProperty::~FloatCfgProperty()
+{
+
+}
+
+void FloatCfgProperty::setFloat(float)
+{
+
+}
+
+float FloatCfgProperty::getFloat()
+{
+
+}*/
diff --git a/SDK/C++/private/property_impl.hpp b/SDK/C++/private/property_impl.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2adf96de3f172d07357a13cfd568ea7304dbd00d
--- /dev/null
+++ b/SDK/C++/private/property_impl.hpp
@@ -0,0 +1,51 @@
+#pragma once
+
+#include <ftl/configurable.hpp>
+#include <voltu/types/property.hpp>
+#include <string>
+
+namespace voltu
+{
+namespace internal
+{
+
+class CfgPropertyImpl : public voltu::Property
+{
+public:
+	CfgPropertyImpl(ftl::Configurable *cfg, const std::string &name);
+	virtual ~CfgPropertyImpl() override;
+
+	virtual void setInt(int) override;
+
+	virtual void setFloat(float) override;
+
+	virtual void setString(const std::string &) override;
+
+	virtual void setBool(bool) override;
+
+	virtual int getInt() override;
+
+	virtual float getFloat() override;
+
+	virtual std::string getString() override;
+
+	virtual bool getBool() override;
+
+private:
+	ftl::Configurable *cfg_;
+	std::string name_;
+};
+
+/*class FloatCfgProperty : public CfgPropertyImpl
+{
+public:
+	FloatCfgProperty(ftl::Configurable *cfg, const std::string &name);
+	~FloatCfgProperty() override;
+
+	void setFloat(float) override;
+
+	float getFloat() override;
+};*/
+
+}
+}
diff --git a/SDK/C++/private/room_impl.cpp b/SDK/C++/private/room_impl.cpp
index 9df583c86610ecfeb4c0140ee659201ba76a1f29..76ab1b92bad65d098c8a0f2a34685edc9219e458 100644
--- a/SDK/C++/private/room_impl.cpp
+++ b/SDK/C++/private/room_impl.cpp
@@ -11,6 +11,11 @@ RoomImpl::RoomImpl(ftl::stream::Feed* feed)
 
 }
 
+RoomImpl::~RoomImpl()
+{
+	if (filter_) filter_->remove();
+}
+
 bool RoomImpl::waitNextFrame(int64_t timeout)
 {
 	if (!filter_)
diff --git a/SDK/C++/private/room_impl.hpp b/SDK/C++/private/room_impl.hpp
index 6c57dfcbb499c32f487d952c7b021da3ce148faa..be8516a637b316c9a3964b14952f11fcadd23459 100644
--- a/SDK/C++/private/room_impl.hpp
+++ b/SDK/C++/private/room_impl.hpp
@@ -13,6 +13,8 @@ class RoomImpl : public voltu::Room
 {
 public:
 	explicit RoomImpl(ftl::stream::Feed*);
+	
+	~RoomImpl() override;
 
 	bool waitNextFrame(int64_t) override;
 
diff --git a/SDK/C++/private/system.cpp b/SDK/C++/private/system.cpp
index dd18eef256b5d16dba15f00d9f5170380b286e48..b93a4bdc621dae6268032fa6525a6bca89b383d5 100644
--- a/SDK/C++/private/system.cpp
+++ b/SDK/C++/private/system.cpp
@@ -2,6 +2,7 @@
 #include "feed_impl.hpp"
 #include "room_impl.hpp"
 #include "observer_impl.hpp"
+#include "pipeline_impl.hpp"
 #include <voltu/voltu.hpp>
 #include <voltu/types/errors.hpp>
 #include <ftl/timer.hpp>
@@ -44,7 +45,11 @@ SystemImpl::SystemImpl()
 SystemImpl::~SystemImpl()
 {
 	ftl::timer::stop(true);
+	net_->shutdown();
 	ftl::pool.stop(true);
+	delete feed_;
+	delete net_;
+	delete root_;
 }
 
 voltu::Version SystemImpl::getVersion() const
@@ -61,7 +66,7 @@ voltu::FeedPtr SystemImpl::open(const std::string& uri)
 	try
 	{
 		uint32_t fsid = feed_->add(uri);
-		return std::make_shared<voltu::internal::FeedImpl>(feed_, fsid);
+		return std::make_shared<voltu::internal::InputFeedImpl>(feed_, fsid);
 	}
 	catch(const std::exception &e)
 	{
@@ -88,3 +93,13 @@ voltu::ObserverPtr SystemImpl::createObserver()
 {
 	return std::make_shared<voltu::internal::ObserverImpl>(root_);
 }
+
+voltu::FeedPtr SystemImpl::createFeed(const std::string &uri)
+{
+	return std::make_shared<voltu::internal::OutputFeedImpl>(feed_, uri);
+}
+
+voltu::PipelinePtr SystemImpl::createPipeline()
+{
+	return std::make_shared<voltu::internal::PipelineImpl>(root_);
+}
diff --git a/SDK/C++/private/system_impl.hpp b/SDK/C++/private/system_impl.hpp
index f11f48891d7a3b838303abdda47f240c63d60da7..dfc2fae09a7b8fd2dafaa5161e49ba0964af092c 100644
--- a/SDK/C++/private/system_impl.hpp
+++ b/SDK/C++/private/system_impl.hpp
@@ -14,7 +14,7 @@ class SystemImpl : public voltu::System
 {
 public:
 	SystemImpl();
-	~SystemImpl();
+	~SystemImpl() override;
 
 	voltu::Version getVersion() const override;
 
@@ -28,6 +28,10 @@ public:
 
 	voltu::RoomPtr getRoom(voltu::RoomId) override;
 
+	voltu::FeedPtr createFeed(const std::string &uri) override;
+
+	voltu::PipelinePtr createPipeline() override;
+
 private:
 	ftl::Configurable* root_;
 	ftl::stream::Feed* feed_;
diff --git a/SDK/C++/public/CMakeLists.txt b/SDK/C++/public/CMakeLists.txt
index ae8c7da08bfad909195d334e699fdbba5655e0bf..a0eb7eaf58262395b4232cba3a0421cb144ccfca 100644
--- a/SDK/C++/public/CMakeLists.txt
+++ b/SDK/C++/public/CMakeLists.txt
@@ -62,6 +62,11 @@ add_executable(voltu_basic_virtual_cam
 )
 target_link_libraries(voltu_basic_virtual_cam voltu_sdk)
 
+add_executable(voltu_fusion_evaluator
+	samples/fusion_evaluator/main.cpp
+)
+target_link_libraries(voltu_fusion_evaluator voltu_sdk)
+
 if (WITH_PYTHON)
 	add_subdirectory(ext/pybind11)
 	add_subdirectory(python)
diff --git a/SDK/C++/public/include/voltu/feed.hpp b/SDK/C++/public/include/voltu/feed.hpp
index 7a58301960f8b065955528d32d309198b85ad96e..0a19d30174be9c09ad5461d2c071073741ed33f9 100644
--- a/SDK/C++/public/include/voltu/feed.hpp
+++ b/SDK/C++/public/include/voltu/feed.hpp
@@ -1,6 +1,8 @@
 #pragma once
 
 #include "defines.hpp"
+#include <voltu/types/frame.hpp>
+#include <voltu/types/property.hpp>
 
 #include <memory>
 #include <string>
@@ -11,22 +13,40 @@ namespace voltu
 enum class FeedType
 {
 	kInvalid = 0,
-	kMonoCamera = 1,
-	kStereoCamera = 2,
-	kDepthCamera = 3,
+	kDevice = 1,
+	kFile = 2,
+	kStream = 3,
 	kVirtual = 4,
-	kScreen = 5,
-	kRoom = 6,
-	kRooms = 7
+	kMultiple = 5
+};
+
+enum class FeedProperty
+{
+	kInvalid			= 0,
+	kColourCodec		= 2001,
+	kDepthCodec			= 2002,
+	kFPSLimit			= 2003,
+	kColourBitrate		= 2004,
+	kDepthBitrate		= 2005,
+	kFileLooping		= 2006,
+	kFileSpeed			= 2007
 };
 
 class Feed
 {
 public:
+	virtual ~Feed() = default;
+	
 	PY_API virtual std::string getURI() = 0;
 
 	PY_API virtual void remove() = 0;
 
+	PY_API virtual void submit(const voltu::FramePtr &frame) = 0;
+
+	PY_API virtual voltu::FeedType type() = 0;
+
+	PY_API virtual voltu::PropertyPtr property(voltu::FeedProperty) = 0;
+
 	// Get rooms
 };
 
diff --git a/SDK/C++/public/include/voltu/observer.hpp b/SDK/C++/public/include/voltu/observer.hpp
index 03323b0793212d14bcc4f45f6112c1af33b30363..f42ffaf9a8173b737831a978704ac5810a61620a 100644
--- a/SDK/C++/public/include/voltu/observer.hpp
+++ b/SDK/C++/public/include/voltu/observer.hpp
@@ -25,6 +25,8 @@ enum class ObserverProperty
 class Observer
 {
 public:
+	virtual ~Observer() = default;
+	
 	PY_API virtual void setResolution(uint32_t w, uint32_t h) = 0;
 
 	PY_API virtual void setFocalLength(uint32_t f) = 0;
@@ -40,6 +42,8 @@ public:
 	PY_API PY_RV_LIFETIME_PARENT virtual voltu::FramePtr getFrame() = 0;
 
 	PY_API virtual voltu::PropertyPtr property(voltu::ObserverProperty) = 0;
+
+	//PY_API virtual voltu::PropertyPtr property(const std::string &name) = 0;
 };
 
 typedef std::shared_ptr<Observer> ObserverPtr;
diff --git a/SDK/C++/public/include/voltu/operator.hpp b/SDK/C++/public/include/voltu/operator.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a7f4ce9961567912cdb9b73d95731d0e1867d6a5
--- /dev/null
+++ b/SDK/C++/public/include/voltu/operator.hpp
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <voltu/types/property.hpp>
+#include <memory>
+
+namespace voltu
+{
+
+enum class OperatorId
+{
+	kInvalid		= 0,
+	kDepth			= 1,
+	kGTEvaluator	= 2,
+	kFusion			= 3,
+	kClipping		= 4,
+	kAruco			= 5,
+	kPixelAnalysis	= 6
+};
+
+class Operator
+{
+public:
+	virtual ~Operator() = default;
+	
+	PY_API virtual voltu::PropertyPtr property(const std::string &name) = 0;
+};
+
+typedef std::shared_ptr<Operator> OperatorPtr;
+
+}
\ No newline at end of file
diff --git a/SDK/C++/public/include/voltu/pipeline.hpp b/SDK/C++/public/include/voltu/pipeline.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c9894f06366b3a7577ddb01ba322e4784d218aa7
--- /dev/null
+++ b/SDK/C++/public/include/voltu/pipeline.hpp
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <voltu/defines.hpp>
+#include <voltu/types/frame.hpp>
+#include <voltu/operator.hpp>
+#include <memory>
+
+namespace voltu
+{
+
+class Pipeline
+{
+public:
+	virtual ~Pipeline() = default;
+	
+	PY_API virtual void submit(const voltu::FramePtr &frame) = 0;
+
+	PY_API virtual bool waitCompletion(int timeout) = 0;
+
+	PY_API virtual voltu::OperatorPtr appendOperator(voltu::OperatorId id) = 0;
+};
+
+typedef std::shared_ptr<Pipeline> PipelinePtr;
+
+}
diff --git a/SDK/C++/public/include/voltu/room.hpp b/SDK/C++/public/include/voltu/room.hpp
index da09776227d4e25aeef9f763a0f26270941e090b..4971439b869b514fb9aaca54c826fb4ec2ef8c41 100644
--- a/SDK/C++/public/include/voltu/room.hpp
+++ b/SDK/C++/public/include/voltu/room.hpp
@@ -19,6 +19,8 @@ typedef unsigned int RoomId;
 class Room
 {
 public:
+	virtual ~Room() = default;
+	
 	PY_API virtual bool waitNextFrame(int64_t) = 0;
 
 	PY_API inline bool hasNextFrame() { return waitNextFrame(0); };
diff --git a/SDK/C++/public/include/voltu/system.hpp b/SDK/C++/public/include/voltu/system.hpp
index cf927eeba5cb61615ab598a1af61da9aaec2a997..0f35bc88caa89f9e9e6fd09807421b147487780b 100644
--- a/SDK/C++/public/include/voltu/system.hpp
+++ b/SDK/C++/public/include/voltu/system.hpp
@@ -4,6 +4,7 @@
 #include <voltu/room.hpp>
 #include <voltu/observer.hpp>
 #include <voltu/feed.hpp>
+#include <voltu/pipeline.hpp>
 #include <list>
 
 namespace voltu
@@ -11,25 +12,41 @@ namespace voltu
 
 struct Version
 {
-	int major;
-	int minor;
-	int patch;
+	int major;  // API Incompatible change
+	int minor;	// Possible binary incompatible, extensions
+	int patch;	// Internal only fixes.
 };
 
+/**
+ * Singleton Voltu system instance. Provides access to the key components such
+ * as opening streams or files and creating virtual cameras.
+ */
 class System
 {
 public:
+	virtual ~System() = default;
+	
+	/** Get the semantic version information. */
 	virtual voltu::Version getVersion() const = 0;
 
+	/** Make a virtual room or composite room. */
 	PY_API virtual voltu::RoomPtr createRoom() = 0;
 
+	/** Create a virtual observer. This renderers virtual camera views. */
 	PY_API virtual voltu::ObserverPtr createObserver() = 0;
 
-	PY_API virtual voltu::FeedPtr open(const std::string&) = 0;
+	/** Open a file, device or network stream using a URI. */
+	PY_API virtual voltu::FeedPtr open(const std::string &uri) = 0;
 
 	PY_API virtual std::list<voltu::RoomId> listRooms() = 0;
 
-	PY_API virtual voltu::RoomPtr getRoom(voltu::RoomId) = 0;
+	PY_API virtual voltu::RoomPtr getRoom(voltu::RoomId room) = 0;
+
+	/** Make a file or streaming feed, to which you can send frames. */
+	PY_API virtual voltu::FeedPtr createFeed(const std::string &uri) = 0;
+
+	/** Make an empty operator pipeline for frame processing. */
+	PY_API virtual voltu::PipelinePtr createPipeline() = 0;
 };
 
 }
diff --git a/SDK/C++/public/include/voltu/types/errors.hpp b/SDK/C++/public/include/voltu/types/errors.hpp
index 9426428014094370c26160ab2f4ec2c893205a97..bbb8be620813c4d4fc94e5a7d868c05f3cff30aa 100644
--- a/SDK/C++/public/include/voltu/types/errors.hpp
+++ b/SDK/C++/public/include/voltu/types/errors.hpp
@@ -31,7 +31,15 @@ VOLTU_EXCEPTION(BadSourceURI, Exception, "Bad source URI");
 VOLTU_EXCEPTION(InvalidFrameObject, Exception, "Invalid Frame object");
 VOLTU_EXCEPTION(InternalRenderError, Exception, "Internal renderer error");
 VOLTU_EXCEPTION(InvalidProperty, Exception, "Unknown property enum");
+VOLTU_EXCEPTION(PropertyUnavailable, Exception, "Property currently not available");
+VOLTU_EXCEPTION(BadPropertyName, Exception, "Not a valid property name");
+VOLTU_EXCEPTION(BadPropertyType, Exception, "Incorrect property data type");
+VOLTU_EXCEPTION(BadPropertyValue, Exception, "Property value out of allowed range");
 VOLTU_EXCEPTION(BadParameterValue, Exception, "Method parameter is not valid");
+VOLTU_EXCEPTION(NotImplemented, Exception, "Functionality not implemented");
+VOLTU_EXCEPTION(ReadOnly, Exception, "Read only, write not allowed");
+VOLTU_EXCEPTION(WriteOnly, Exception, "Write only, read not allowed");
+VOLTU_EXCEPTION(IncompatibleOperation, Exception, "The input data and operator are incompatible");
 
 }
 }
diff --git a/SDK/C++/public/include/voltu/types/frame.hpp b/SDK/C++/public/include/voltu/types/frame.hpp
index adbd84c260984a52e43e25d185120341da5aa093..eebc557eae0c2f12c5b8fcaad66e72914c0d2968 100644
--- a/SDK/C++/public/include/voltu/types/frame.hpp
+++ b/SDK/C++/public/include/voltu/types/frame.hpp
@@ -14,10 +14,14 @@ namespace voltu
 class Frame
 {
 public:
+	virtual ~Frame() = default;
+	
 	PY_API PY_RV_LIFETIME_PARENT virtual std::list<voltu::ImagePtr> getImageSet(voltu::Channel channel) = 0;
 
 	PY_API PY_RV_LIFETIME_PARENT virtual voltu::PointCloudPtr getPointCloud(voltu::PointCloudFormat cloudfmt, voltu::PointFormat pointfmt) = 0;
 
+	PY_API virtual std::vector<std::string> getMessages() = 0;
+
 	PY_API virtual int64_t getTimestamp() = 0;
 };
 
diff --git a/SDK/C++/public/include/voltu/types/image.hpp b/SDK/C++/public/include/voltu/types/image.hpp
index 76d5ec5f099223e36d49cdc6dce4f9435d64d8aa..406c81df8b2f4ba9e2a396b5df56097c50828e0c 100644
--- a/SDK/C++/public/include/voltu/types/image.hpp
+++ b/SDK/C++/public/include/voltu/types/image.hpp
@@ -15,7 +15,8 @@ enum class ImageFormat
 {
 	kInvalid = 0,
 	kFloat32 = 1,
-	kBGRA8 = 2
+	kBGRA8 = 2,
+	kFloat16_4 = 3
 };
 
 PY_NO_SHARED_PTR struct ImageData
@@ -30,6 +31,8 @@ PY_NO_SHARED_PTR struct ImageData
 class Image
 {
 public:
+	virtual ~Image() = default;
+
 	PY_API PY_RV_LIFETIME_PARENT virtual ImageData getHost() = 0;
 
 	virtual ImageData getDevice() = 0;
diff --git a/SDK/C++/public/include/voltu/types/property.hpp b/SDK/C++/public/include/voltu/types/property.hpp
index 2fa3c43b44ab95503b1cca587ef4aab8cd1cf66b..2a5fbe53c0e53a4fecd40d79d60916c05d0fdf06 100644
--- a/SDK/C++/public/include/voltu/types/property.hpp
+++ b/SDK/C++/public/include/voltu/types/property.hpp
@@ -2,6 +2,7 @@
 
 #include "../defines.hpp"
 #include <memory>
+#include <string>
 
 namespace voltu
 {
@@ -9,6 +10,8 @@ namespace voltu
 class Property
 {
 public:
+	virtual ~Property() = default;
+
 	PY_API virtual void setInt(int) = 0;
 
 	PY_API virtual void setFloat(float) = 0;
diff --git a/SDK/C++/public/include/voltu/voltu.hpp b/SDK/C++/public/include/voltu/voltu.hpp
index acb2836f45b5c643465bf20a999795555d8225de..17f251ed56989dd2058198297c8ac380b74bc963 100644
--- a/SDK/C++/public/include/voltu/voltu.hpp
+++ b/SDK/C++/public/include/voltu/voltu.hpp
@@ -2,7 +2,7 @@
 
 // Bump these for each release
 #define VOLTU_VERSION_MAJOR 0    // For API incompatible changes
-#define VOLTU_VERSION_MINOR 1    // For binary compatibility and extensions
+#define VOLTU_VERSION_MINOR 2    // For binary compatibility and extensions
 #define VOLTU_VERSION_PATCH 0    // Binary compatible internal fixes
 
 #define VOLTU_VERSION ((VOLTU_VERSION_MAJOR*10000) + (VOLTU_VERSION_MINOR*100) + VOLTU_VERSION_PATCH)
diff --git a/SDK/C++/public/samples/fusion_evaluator/main.cpp b/SDK/C++/public/samples/fusion_evaluator/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..908d0925ed80bf5d4d125d6f184e6dc0d7a9f906
--- /dev/null
+++ b/SDK/C++/public/samples/fusion_evaluator/main.cpp
@@ -0,0 +1,67 @@
+#include <voltu/voltu.hpp>
+#include <voltu/opencv.hpp>
+#include <iostream>
+#include <thread>
+#include <chrono>
+
+#include <opencv2/highgui.hpp>
+
+using std::cout;
+using std::endl;
+using std::string;
+
+int main(int argc, char **argv)
+{
+	if (argc != 2) return -1;
+
+	auto vtu = voltu::instance();
+
+	if (!vtu->open(argv[1]))
+	{
+		cout << "Could not open source" << endl;
+		return -1;
+	}
+
+	while (vtu->listRooms().size() == 0)
+	{
+		std::this_thread::sleep_for(std::chrono::milliseconds(100));
+	}
+
+	auto room = vtu->getRoom(vtu->listRooms().front());
+	if (!room)
+	{
+		cout << "Could not get room" << endl;
+		return -1;
+	}
+
+	auto frame = room->getFrame();
+
+	auto pipe = vtu->createPipeline();
+	auto op1 = pipe->appendOperator(voltu::OperatorId::kFusion);
+	auto op2 = pipe->appendOperator(voltu::OperatorId::kGTEvaluator);
+
+	op2->property("show_colour")->setBool(true);
+
+	pipe->submit(frame);
+	pipe->waitCompletion(1000);
+
+	auto imgset = frame->getImageSet(voltu::Channel::kColour);
+
+	for (auto img : imgset)
+	{
+		cv::Mat m;
+		voltu::cv::visualise(img, m);
+		cv::imshow(string("Image-") + img->getName(), m);
+		break;
+	}
+
+	std::vector<std::string> msgs = frame->getMessages();
+	for (const auto &s : msgs)
+	{
+		cout << s << endl;
+	}
+
+	cv::waitKey(-1);
+
+	return 0;
+}
diff --git a/SDK/C++/public/voltu_cv.cpp b/SDK/C++/public/voltu_cv.cpp
index 3b77155b6450560eb7808f7294013926fea382e7..0fae307111f1311bb73c675560bea18ecd00f539 100644
--- a/SDK/C++/public/voltu_cv.cpp
+++ b/SDK/C++/public/voltu_cv.cpp
@@ -1,4 +1,5 @@
 #include <voltu/opencv.hpp>
+#include <voltu/types/errors.hpp>
 
 #include <opencv2/imgproc.hpp>
 
@@ -14,6 +15,10 @@ void voltu::cv::convert(voltu::ImagePtr img, ::cv::Mat &mat)
 	{
 		mat = ::cv::Mat(data.height, data.width, CV_32FC1, data.data);
 	}
+	else if (data.format == voltu::ImageFormat::kFloat16_4)
+	{
+		mat = ::cv::Mat(data.height, data.width, CV_16FC4, data.data);
+	}
 	else
 	{
 		mat = ::cv::Mat();
@@ -22,7 +27,7 @@ void voltu::cv::convert(voltu::ImagePtr img, ::cv::Mat &mat)
 
 void voltu::cv::convert(voltu::ImagePtr img, ::cv::cuda::GpuMat &mat)
 {
-
+	throw voltu::exceptions::NotImplemented();
 }
 
 void voltu::cv::visualise(voltu::ImagePtr img, ::cv::Mat &mat)
@@ -47,4 +52,13 @@ void voltu::cv::visualise(voltu::ImagePtr img, ::cv::Mat &mat)
 		::cv::applyColorMap(tmp, mat, ::cv::COLORMAP_INFERNO);
 		//#endif
 	}
+	else if (data.format == voltu::ImageFormat::kFloat16_4)
+	{
+		::cv::Mat tmp;
+		voltu::cv::convert(img, tmp);
+		tmp.convertTo(tmp, CV_32FC4);
+		tmp += 1.0f;
+		tmp *= 127.0f;
+		tmp.convertTo(mat, CV_8UC4);
+	}
 }
diff --git a/components/codecs/include/ftl/codecs/packet.hpp b/components/codecs/include/ftl/codecs/packet.hpp
index 1b37d88b4a07c0351bfe9bf92f1d0e427f9eed2e..8f2359d5be4b77cf166ab797700831d17e730588 100644
--- a/components/codecs/include/ftl/codecs/packet.hpp
+++ b/components/codecs/include/ftl/codecs/packet.hpp
@@ -13,12 +13,14 @@ namespace codecs {
 static constexpr uint8_t kAllFrames = 255;
 static constexpr uint8_t kAllFramesets = 255;
 
+static constexpr uint8_t kCurrentFTLVersion = 5;
+
 /**
  * First bytes of our file format.
  */
 struct Header {
 	const char magic[4] = {'F','T','L','F'};
-	uint8_t version = 5;
+	uint8_t version = kCurrentFTLVersion;
 };
 
 /**
@@ -56,7 +58,7 @@ static constexpr unsigned int kStreamCap_NewConnection = 0x04;
 
 /** V4 packets have no stream flags field */
 struct StreamPacketV4 {
-	int version;			// FTL version, Not encoded into stream
+	int version=4;			// FTL version, Not encoded into stream
 
 	int64_t timestamp;
 	uint8_t streamID;  		// Source number [or v4 frameset id]
@@ -81,7 +83,7 @@ struct StreamPacketV4 {
  * or included before a frame packet structure.
  */
 struct StreamPacket {
-	int version;			// FTL version, Not encoded into stream
+	int version = kCurrentFTLVersion;	// FTL version, Not encoded into stream
 
 	int64_t timestamp;
 	uint8_t streamID;  		// Source number [or v4 frameset id]
diff --git a/components/common/cpp/include/ftl/configurable.hpp b/components/common/cpp/include/ftl/configurable.hpp
index b2a66943d35e7cba6a86d6842e037d0068d15feb..3d2a115e670e977fc45e4b8fdb32ff055789dd6c 100644
--- a/components/common/cpp/include/ftl/configurable.hpp
+++ b/components/common/cpp/include/ftl/configurable.hpp
@@ -67,6 +67,11 @@ class Configurable {
 
 	std::string getID() { return *get<std::string>("$id"); }
 
+	template <typename T>
+	inline bool is(const std::string &name) { return false; }
+
+	bool has(const std::string &name) const;
+
 	/**
 	 * Get a configuration property, but return a default if not found.
 	 */
@@ -191,6 +196,20 @@ extern template void ftl::Configurable::set<std::string>(const std::string &name
 extern template void ftl::Configurable::set<std::vector<std::string>>(const std::string &name, std::vector<std::string> value);
 extern template void ftl::Configurable::set<nlohmann::json>(const std::string &name, nlohmann::json value);
 
+template <> bool ftl::Configurable::is<float>(const std::string &name);
+template <> bool ftl::Configurable::is<int>(const std::string &name);
+template <> bool ftl::Configurable::is<unsigned int>(const std::string &name);
+template <> bool ftl::Configurable::is<std::string>(const std::string &name);
+template <> bool ftl::Configurable::is<bool>(const std::string &name);
+template <> bool ftl::Configurable::is<double>(const std::string &name);
+
+extern template bool ftl::Configurable::is<float>(const std::string &name);
+extern template bool ftl::Configurable::is<int>(const std::string &name);
+extern template bool ftl::Configurable::is<unsigned int>(const std::string &name);
+extern template bool ftl::Configurable::is<std::string>(const std::string &name);
+extern template bool ftl::Configurable::is<bool>(const std::string &name);
+extern template bool ftl::Configurable::is<double>(const std::string &name);
+
 template <typename T, typename... ARGS>
 T *ftl::Configurable::create(const std::string &name, ARGS ...args) {
 	return ftl::config::create<T>(this, name, args...);
diff --git a/components/common/cpp/src/configurable.cpp b/components/common/cpp/src/configurable.cpp
index 1f377fc5185cdc533d8c5823514a3fb4bfda3e24..c5f6d9d868806dbe3dc87f64f1069d887bf26b01 100644
--- a/components/common/cpp/src/configurable.cpp
+++ b/components/common/cpp/src/configurable.cpp
@@ -121,6 +121,40 @@ template void ftl::Configurable::set<std::string>(const std::string &name, std::
 template void ftl::Configurable::set<std::vector<std::string>>(const std::string &name, std::vector<std::string> value);
 template void ftl::Configurable::set<nlohmann::json>(const std::string &name, nlohmann::json value);
 
+template <>
+bool ftl::Configurable::is<float>(const std::string &name) {
+	return getConfig()[name].is_number_float();
+}
+
+template <>
+bool ftl::Configurable::is<int>(const std::string &name) {
+	return getConfig()[name].is_number_integer();
+}
+
+template <>
+bool ftl::Configurable::is<unsigned int>(const std::string &name) {
+	return getConfig()[name].is_number_unsigned();
+}
+
+template <>
+bool ftl::Configurable::is<std::string>(const std::string &name) {
+	return getConfig()[name].is_string();
+}
+
+template <>
+bool ftl::Configurable::is<bool>(const std::string &name) {
+	return getConfig()[name].is_boolean();
+}
+
+template <>
+bool ftl::Configurable::is<double>(const std::string &name) {
+	return getConfig()[name].is_number_float();
+}
+
+bool ftl::Configurable::has(const std::string &name) const {
+	return (config_) ? config_->contains(name) : false;
+}
+
 void Configurable::required(const char *f, const std::vector<std::tuple<std::string, std::string, std::string>> &r) {
 	bool diderror = false;
 	for (auto i : r) {
diff --git a/components/operators/include/ftl/operators/antialiasing.hpp b/components/operators/include/ftl/operators/antialiasing.hpp
index 5548c08578e96879f36f2c3a11b281b78ca4bf43..9c249f3d82a89aae0c4cdadba74e0079d69121d9 100644
--- a/components/operators/include/ftl/operators/antialiasing.hpp
+++ b/components/operators/include/ftl/operators/antialiasing.hpp
@@ -19,6 +19,8 @@ class FXAA : public ftl::operators::Operator {
 
     bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 }
diff --git a/components/operators/include/ftl/operators/clipping.hpp b/components/operators/include/ftl/operators/clipping.hpp
index 25d8b76ff7bdbfc99717b01652497395e5ff7ea1..a45ba760ad2f444d1091a56d96e69acb7e921b38 100644
--- a/components/operators/include/ftl/operators/clipping.hpp
+++ b/components/operators/include/ftl/operators/clipping.hpp
@@ -19,6 +19,8 @@ class ClipScene : public ftl::operators::Operator {
 
     bool apply(ftl::rgbd::FrameSet &in, ftl::rgbd::FrameSet &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 }
diff --git a/components/operators/include/ftl/operators/colours.hpp b/components/operators/include/ftl/operators/colours.hpp
index a54539a67f829a7d1e9aa6c6306bb76a707081fa..d6745d17c58f01e1db5ecffae723aa7d863d1165 100644
--- a/components/operators/include/ftl/operators/colours.hpp
+++ b/components/operators/include/ftl/operators/colours.hpp
@@ -15,6 +15,8 @@ class ColourChannels : public ftl::operators::Operator {
 
     bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
     private:
     cv::cuda::GpuMat temp_;
 	cv::cuda::GpuMat rbuf_;
diff --git a/components/operators/include/ftl/operators/depth.hpp b/components/operators/include/ftl/operators/depth.hpp
index 14f46b12d12b3adf933bd3ea7bd67463b36f19f2..38596dcc012e8395020d390762ebae913c0b0762 100644
--- a/components/operators/include/ftl/operators/depth.hpp
+++ b/components/operators/include/ftl/operators/depth.hpp
@@ -18,6 +18,8 @@ class DepthBilateralFilter : public::ftl::operators::Operator {
 	inline ftl::operators::Operator::Type type() const override { return ftl::operators::Operator::Type::OneToOne; }
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	cv::cuda::GpuMat disp_int_;
 	cv::cuda::GpuMat disp_int_result_;
diff --git a/components/operators/include/ftl/operators/detectandtrack.hpp b/components/operators/include/ftl/operators/detectandtrack.hpp
index 0d8b063d34cc2a9059d6cabe54d6c25484015891..2341d2d40bf544621950743ebcadb89676ce4809 100644
--- a/components/operators/include/ftl/operators/detectandtrack.hpp
+++ b/components/operators/include/ftl/operators/detectandtrack.hpp
@@ -48,6 +48,8 @@ class DetectAndTrack : public ftl::operators::Operator {
 
 	void wait(cudaStream_t) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	protected:
 	bool init();
 
@@ -126,6 +128,8 @@ class ArUco : public ftl::operators::Operator {
 	ftl::codecs::Channel channel_in_;
 	ftl::codecs::Channel channel_out_;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	bool estimate_pose_;
 	float marker_size_;
diff --git a/components/operators/include/ftl/operators/disparity.hpp b/components/operators/include/ftl/operators/disparity.hpp
index 17993f24c5e1778957cc7f3f29762ccaa611b0cc..2e4365d8ce361d186aba42fcafeaebcb3ebc7dec 100644
--- a/components/operators/include/ftl/operators/disparity.hpp
+++ b/components/operators/include/ftl/operators/disparity.hpp
@@ -29,6 +29,8 @@ public:
 
 	bool isMemoryHeavy() const override { return true; }
 
+	static void configuration(ftl::Configurable*) {}
+
 private:
 	bool init();
 
@@ -54,6 +56,8 @@ class FixstarsSGM : public ftl::operators::Operator {
 
 	bool isMemoryHeavy() const override { return true; }
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	bool init();
 	bool updateParameters();
@@ -99,6 +103,8 @@ class DisparityBilateralFilter : public::ftl::operators::Operator {
 	inline Operator::Type type() const override { return Operator::Type::OneToOne; }
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	cv::Ptr<cv::cuda::DisparityBilateralFilter> filter_;
 	cv::cuda::GpuMat disp_int_;
@@ -121,6 +127,8 @@ class DisparityToDepth : public ftl::operators::Operator {
 	~DisparityToDepth() {};
 	inline Operator::Type type() const override { return Operator::Type::OneToOne; }
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
+
+	static void configuration(ftl::Configurable*) {}
 };
 
 /**
@@ -138,6 +146,8 @@ class DepthChannel : public ftl::operators::Operator {
 	bool apply(ftl::rgbd::FrameSet &in, ftl::rgbd::FrameSet &out, cudaStream_t stream) override;
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	ftl::operators::Graph *pipe_;
 	std::vector<cv::cuda::GpuMat> rbuf_;
@@ -160,6 +170,8 @@ class OpticalFlowTemporalSmoothing : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	void _init(ftl::Configurable* cfg);
 	bool init();
diff --git a/components/operators/include/ftl/operators/filling.hpp b/components/operators/include/ftl/operators/filling.hpp
index ed2b3f39a20a949fa28dfe61232d44d15f56affe..c26b9ef80e7aa0705fb6877f06a1154ee8fc0078 100644
--- a/components/operators/include/ftl/operators/filling.hpp
+++ b/components/operators/include/ftl/operators/filling.hpp
@@ -19,6 +19,8 @@ class ScanFieldFill : public ftl::operators::Operator {
 
     bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 class CrossSupportFill : public ftl::operators::Operator {
@@ -30,6 +32,8 @@ class CrossSupportFill : public ftl::operators::Operator {
 
     bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 }
diff --git a/components/operators/include/ftl/operators/fusion.hpp b/components/operators/include/ftl/operators/fusion.hpp
index ed0e5f9cd324f1e57979d9f20fc9d9a98f796ce0..d1e72ecfed516545a109f4530ecde9f330bb0205 100644
--- a/components/operators/include/ftl/operators/fusion.hpp
+++ b/components/operators/include/ftl/operators/fusion.hpp
@@ -17,6 +17,8 @@ class Fusion : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::FrameSet &in, ftl::rgbd::FrameSet &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	ftl::cuda::MLSMultiIntensity mls_;
 	std::vector<cv::cuda::GpuMat> weights_;
diff --git a/components/operators/include/ftl/operators/gt_analysis.hpp b/components/operators/include/ftl/operators/gt_analysis.hpp
index cea34c9dba5477012bd2a620ebbbe0e079fdea51..efe0b64e63b27b6b999f79baa10ecf18f68ad3a9 100644
--- a/components/operators/include/ftl/operators/gt_analysis.hpp
+++ b/components/operators/include/ftl/operators/gt_analysis.hpp
@@ -21,6 +21,8 @@ class GTAnalysis : public ftl::operators::Operator {
 
     bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*);
+
 	private:
 	ftl::cuda::GTAnalysisData *output_;
 };
diff --git a/components/operators/include/ftl/operators/mask.hpp b/components/operators/include/ftl/operators/mask.hpp
index 7d877d6579c329f7af513840b7140f187dbf7eed..863e085c2518700954a0aaac365edd3e37f418c6 100644
--- a/components/operators/include/ftl/operators/mask.hpp
+++ b/components/operators/include/ftl/operators/mask.hpp
@@ -21,6 +21,8 @@ class DiscontinuityMask : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 /**
@@ -35,6 +37,8 @@ class BorderMask : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 /**
@@ -49,6 +53,8 @@ class DisplayMask : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 /**
@@ -63,6 +69,8 @@ class CullDiscontinuity : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 }
diff --git a/components/operators/include/ftl/operators/mvmls.hpp b/components/operators/include/ftl/operators/mvmls.hpp
index 0c0d6f4afb9715814199304ca0b430ac094071c4..56aafac1fe779832f9f7b6e857a1d67063113fdd 100644
--- a/components/operators/include/ftl/operators/mvmls.hpp
+++ b/components/operators/include/ftl/operators/mvmls.hpp
@@ -15,6 +15,8 @@ class MultiViewMLS : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::FrameSet &in, ftl::rgbd::FrameSet &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	std::vector<ftl::cuda::TextureObject<float4>*> centroid_horiz_;
 	std::vector<ftl::cuda::TextureObject<float4>*> centroid_vert_;
diff --git a/components/operators/include/ftl/operators/normals.hpp b/components/operators/include/ftl/operators/normals.hpp
index c4d1a0190d27e9f307474eeed3f2234ac99e2e23..398b006c7e6286575bd9ed986b02f5b8d5a7fc73 100644
--- a/components/operators/include/ftl/operators/normals.hpp
+++ b/components/operators/include/ftl/operators/normals.hpp
@@ -19,6 +19,8 @@ class Normals : public ftl::operators::Operator {
 
     bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 /**
@@ -36,6 +38,8 @@ class NormalDot : public ftl::operators::Operator {
 
     bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 /**
@@ -51,6 +55,8 @@ class SmoothNormals : public ftl::operators::Operator {
 
     bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	ftl::cuda::TextureObject<half4> temp_;
 
diff --git a/components/operators/include/ftl/operators/operator.hpp b/components/operators/include/ftl/operators/operator.hpp
index 3f77b6900e4456898076031b3d97ba5bae190f90..938f5e781ab8bd4435f7a80c7bca6c9e0afcceca 100644
--- a/components/operators/include/ftl/operators/operator.hpp
+++ b/components/operators/include/ftl/operators/operator.hpp
@@ -81,7 +81,7 @@ struct ConstructionHelperBase {
 
 template <typename T>
 struct ConstructionHelper : public ConstructionHelperBase {
-	explicit ConstructionHelper(ftl::Configurable *cfg) : ConstructionHelperBase(cfg) {}
+	explicit ConstructionHelper(ftl::Configurable *cfg) : ConstructionHelperBase(cfg) { T::configuration(cfg); }
 	~ConstructionHelper() {}
 	ftl::operators::Operator *make(Graph *g) override {
 		return new T(g, config);
@@ -92,6 +92,7 @@ template <typename T, typename... ARGS>
 struct ConstructionHelper2 : public ConstructionHelperBase {
 	explicit ConstructionHelper2(ftl::Configurable *cfg, ARGS... args) : ConstructionHelperBase(cfg) {
 		arguments_ = std::make_tuple(args...);
+		T::configuration(cfg);
 	}
 	~ConstructionHelper2() {}
 	ftl::operators::Operator *make(Graph *g) override {
diff --git a/components/operators/include/ftl/operators/opticalflow.hpp b/components/operators/include/ftl/operators/opticalflow.hpp
index 21d8bbb200383b85a4f0c99a1f35e5e2e7b51385..cea1e7ba44f6f7b2f670cef318eb1ced4fcf7b7b 100644
--- a/components/operators/include/ftl/operators/opticalflow.hpp
+++ b/components/operators/include/ftl/operators/opticalflow.hpp
@@ -20,6 +20,8 @@ class NVOpticalFlow : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	protected:
 	bool init();
 
diff --git a/components/operators/include/ftl/operators/poser.hpp b/components/operators/include/ftl/operators/poser.hpp
index 8d762f8ba2ee371342adb8e20b07d359211d081e..2a6a4d6eb276471b6791e5efd55248a02dfb1db0 100644
--- a/components/operators/include/ftl/operators/poser.hpp
+++ b/components/operators/include/ftl/operators/poser.hpp
@@ -24,6 +24,8 @@ class Poser : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	static bool get(const std::string &name, Eigen::Matrix4d &pose);
 	static bool get(const std::string &name, ftl::codecs::Shape3D &shape);
 	static std::list<ftl::codecs::Shape3D*> getAll(int32_t fsid);
diff --git a/components/operators/include/ftl/operators/segmentation.hpp b/components/operators/include/ftl/operators/segmentation.hpp
index ec2f9e9a6d3ea8d683a6139a237849c82b8fe44a..0c3a4fff7337876d3d1a005acb12a8193ab958d9 100644
--- a/components/operators/include/ftl/operators/segmentation.hpp
+++ b/components/operators/include/ftl/operators/segmentation.hpp
@@ -18,6 +18,8 @@ class CrossSupport : public ftl::operators::Operator {
 
     bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 /**
@@ -32,6 +34,8 @@ class VisCrossSupport : public ftl::operators::Operator {
 
     bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 }
diff --git a/components/operators/include/ftl/operators/smoothing.hpp b/components/operators/include/ftl/operators/smoothing.hpp
index db231cdd714a786df1dedf7ca54de06d2005ae4e..d035d399a74a2ecec1fd0dd06e118b20a54f6802 100644
--- a/components/operators/include/ftl/operators/smoothing.hpp
+++ b/components/operators/include/ftl/operators/smoothing.hpp
@@ -21,6 +21,8 @@ class HFSmoother : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	cv::cuda::GpuMat temp_;
 	//ftl::rgbd::Frame frames_[4];
@@ -42,6 +44,8 @@ class SmoothChannel : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	ftl::cuda::TextureObject<uchar4> temp_[6];
 };
@@ -60,6 +64,8 @@ class SimpleMLS : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	ftl::data::Frame temp_;
 };
@@ -77,6 +83,8 @@ class ColourMLS : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	ftl::data::Frame temp_;
 };
@@ -120,6 +128,8 @@ class AggreMLS : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	ftl::cuda::TextureObject<float4> centroid_horiz_;
 	ftl::cuda::TextureObject<float4> centroid_vert_;
@@ -144,6 +154,8 @@ class AdaptiveMLS : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 	private:
 	ftl::data::Frame temp_;
 };
diff --git a/components/operators/include/ftl/operators/weighting.hpp b/components/operators/include/ftl/operators/weighting.hpp
index 8256a08cb23d03d42b9ef18a7107a858a34ebb8b..d829de145e7ac42f1c99d356fa2abf60c869b6d4 100644
--- a/components/operators/include/ftl/operators/weighting.hpp
+++ b/components/operators/include/ftl/operators/weighting.hpp
@@ -29,6 +29,8 @@ class PixelWeights : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 class CullWeight : public ftl::operators::Operator {
@@ -40,6 +42,8 @@ class CullWeight : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 class DegradeWeight : public ftl::operators::Operator {
@@ -51,6 +55,8 @@ class DegradeWeight : public ftl::operators::Operator {
 
 	bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, cudaStream_t stream) override;
 
+	static void configuration(ftl::Configurable*) {}
+
 };
 
 }
diff --git a/components/operators/src/analysis/evaluation/gt_analysis.cpp b/components/operators/src/analysis/evaluation/gt_analysis.cpp
index d7f94d39946d58a877050c73371c178436a9e6f9..c9559084eb53da55e13c8c4b22ccb221fcd9cb3f 100644
--- a/components/operators/src/analysis/evaluation/gt_analysis.cpp
+++ b/components/operators/src/analysis/evaluation/gt_analysis.cpp
@@ -9,6 +9,11 @@ GTAnalysis::GTAnalysis(ftl::operators::Graph *g, ftl::Configurable *cfg) : ftl::
 	cudaMalloc(&output_, sizeof(ftl::cuda::GTAnalysisData));
 }
 
+void GTAnalysis::configuration(ftl::Configurable *cfg) {
+	cfg->value("use_disparity", true);
+	cfg->value("show_colour", false);
+}
+
 GTAnalysis::~GTAnalysis() {
 	cudaFree(output_);
 }
diff --git a/components/streams/src/filestream.cpp b/components/streams/src/filestream.cpp
index 96159a54475f4863a011bb360a2903ed01977c0b..cddf769b5f0528ac285973292842a214cdadfc4a 100644
--- a/components/streams/src/filestream.cpp
+++ b/components/streams/src/filestream.cpp
@@ -174,6 +174,7 @@ bool File::readPacket(std::tuple<ftl::codecs::StreamPacket,ftl::codecs::Packet>
 
 				auto &spkt = std::get<0>(data);
 				auto &spktv4 = std::get<0>(datav4);
+				spkt.version = 4;
 				spkt.streamID = spktv4.streamID;
 				spkt.channel = spktv4.channel;
 				spkt.frame_number = spktv4.frame_number;
@@ -213,7 +214,7 @@ void File::_patchPackets(ftl::codecs::StreamPacket &spkt, ftl::codecs::Packet &p
 		if (codec == ftl::codecs::codec_t::HEVC) pkt.codec = ftl::codecs::codec_t::HEVC_LOSSLESS;
 	}
 
-	spkt.version = 5;
+	spkt.version = ftl::codecs::kCurrentFTLVersion;
 
 	// Fix for flags corruption
 	if (pkt.data.size() == 0) {