diff --git a/applications/gui/CMakeLists.txt b/applications/gui/CMakeLists.txt
index ac9ae7ec20881ab9c4112d9baf0696ce87693536..6b0d3944cdd5d4c581bf77b35ba05423bcaea4a3 100644
--- a/applications/gui/CMakeLists.txt
+++ b/applications/gui/CMakeLists.txt
@@ -6,6 +6,8 @@ set(GUISRC
 	src/main.cpp
 	src/ctrl_window.cpp
 	src/src_window.cpp
+	src/config_window.cpp
+	src/pose_window.cpp
 )
 
 add_executable(ftl-gui ${GUISRC})
diff --git a/applications/gui/src/config_window.cpp b/applications/gui/src/config_window.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cc422f4950070d9387ee92e3b5f5323782855a19
--- /dev/null
+++ b/applications/gui/src/config_window.cpp
@@ -0,0 +1,115 @@
+#include "config_window.hpp"
+
+#include <nanogui/layout.h>
+#include <nanogui/label.h>
+#include <nanogui/combobox.h>
+#include <nanogui/button.h>
+#include <nanogui/entypo.h>
+#include <nanogui/formhelper.h>
+
+#include <vector>
+#include <string>
+
+using ftl::gui::ConfigWindow;
+using std::string;
+using std::vector;
+using ftl::config::json_t;
+
+
+ConfigWindow::ConfigWindow(nanogui::Widget *parent, ftl::ctrl::Master *ctrl, const ftl::UUID &peer)
+		: nanogui::Window(parent, "Settings"), ctrl_(ctrl), peer_(peer) {
+	using namespace nanogui;
+
+	setLayout(new GroupLayout());
+	setPosition(Vector2i(parent->width()/2.0f - 100.0f, parent->height()/2.0f - 100.0f));
+	//setModal(true);
+
+	configurables_ = ctrl->getConfigurables(peer);
+
+	auto label = new Label(this, "Select Configurable","sans-bold");
+
+	auto select = new ComboBox(this, configurables_);
+	select->setCallback([this](int ix) {
+		LOG(INFO) << "Change configurable: " << ix;
+		_buildForm(configurables_[ix], ctrl_->get(peer_, configurables_[ix]));
+
+		setVisible(false);
+		//this->parent()->removeChild(this);
+		//delete this;
+		//screen()->removeChild(this);
+	});
+}
+
+ConfigWindow::~ConfigWindow() {
+
+}
+
+void ConfigWindow::_addElements(nanogui::FormHelper *form, const std::string &suri, const ftl::config::json_t &data) {
+	using namespace nanogui;
+
+	for (auto i=data.begin(); i!=data.end(); ++i) {
+		if (i.key() == "$id") continue;
+
+		if (i.key() == "$ref" && i.value().is_string()) {
+			LOG(INFO) << "Follow $ref: " << i.value();
+			_addElements(form, suri, ctrl_->get(peer_, i.value().get<string>()));
+			continue;
+		}
+
+		if (i.value().is_boolean()) {
+			string key = i.key();
+			form->addVariable<bool>(i.key(), [this,data,key,suri](const bool &b){
+				ctrl_->set(peer_, suri + string("/") + key, json_t(b));
+			}, [data,key]() -> bool {
+				return data[key].get<bool>();
+			});
+		} else if (i.value().is_number_integer()) {
+			string key = i.key();
+			form->addVariable<int>(i.key(), [this,data,key,suri](const int &f){
+				ctrl_->set(peer_, suri + string("/") + key, json_t(f));
+			}, [data,key]() -> int {
+				return data[key].get<int>();
+			});
+		} else if (i.value().is_number_float()) {
+			string key = i.key();
+			form->addVariable<float>(i.key(), [this,data,key,suri](const float &f){
+				ctrl_->set(peer_, suri + string("/") + key, json_t(f));
+			}, [data,key]() -> float {
+				return data[key].get<float>();
+			});
+		} else if (i.value().is_string()) {
+			string key = i.key();
+			form->addVariable<string>(i.key(), [this,data,key,suri](const string &f){
+				ctrl_->set(peer_, suri + string("/") + key, json_t(f));
+			}, [data,key]() -> string {
+				return data[key].get<string>();
+			});
+		} else if (i.value().is_object()) {
+			string key = i.key();
+			const ftl::config::json_t &v = i.value();
+		
+			form->addButton(i.key(), [this,form,suri,key,v]() {
+				_buildForm(suri+string("/")+key, v);
+			})->setIcon(ENTYPO_ICON_FOLDER);
+		}
+	}
+}
+
+void ConfigWindow::_buildForm(const std::string &suri, ftl::config::json_t data) {
+	using namespace nanogui;
+
+	ftl::URI uri(suri);
+
+	FormHelper *form = new FormHelper(this->screen());
+	//form->setWindow(this);
+	form->addWindow(Vector2i(100,50), uri.getFragment());
+	form->window()->setTheme(theme());
+
+	_addElements(form, suri, data);
+
+	auto closebutton = form->addButton("Close", [form]() {
+		form->window()->setVisible(false);
+		delete form;
+	});
+	closebutton->setIcon(ENTYPO_ICON_CROSS);
+}
diff --git a/applications/gui/src/config_window.hpp b/applications/gui/src/config_window.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..87170b1d30741a3d94eff5a54fcaa10d67718e86
--- /dev/null
+++ b/applications/gui/src/config_window.hpp
@@ -0,0 +1,33 @@
+#ifndef _FTL_GUI_CFGWINDOW_HPP_
+#define _FTL_GUI_CFGWINDOW_HPP_
+
+#include <nanogui/window.h>
+#include <ftl/master.hpp>
+#include <ftl/uuid.hpp>
+
+#include <nanogui/formhelper.h>
+
+namespace ftl {
+namespace gui {
+
+/**
+ * Allow configurable editing.
+ */
+class ConfigWindow : public nanogui::Window {
+	public:
+	ConfigWindow(nanogui::Widget *parent, ftl::ctrl::Master *ctrl, const ftl::UUID &peer);
+	~ConfigWindow();
+
+	private:
+	ftl::ctrl::Master *ctrl_;
+	ftl::UUID peer_;
+	std::vector<std::string> configurables_;
+	
+	void _buildForm(const std::string &uri, ftl::config::json_t data);
+	void _addElements(nanogui::FormHelper *form, const std::string &suri, const ftl::config::json_t &data);
+};
+
+}
+}
+
+#endif  // _FTL_GUI_CFGWINDOW_HPP_
diff --git a/applications/gui/src/ctrl_window.cpp b/applications/gui/src/ctrl_window.cpp
index ef28546400e123c1651504602ffd32809065f202..06543822027853969ca7fee277523896969a100f 100644
--- a/applications/gui/src/ctrl_window.cpp
+++ b/applications/gui/src/ctrl_window.cpp
@@ -1,5 +1,7 @@
 #include "ctrl_window.hpp"
 
+#include "config_window.hpp"
+
 #include <nanogui/layout.h>
 #include <nanogui/label.h>
 #include <nanogui/combobox.h>
@@ -10,30 +12,38 @@
 #include <string>
 
 using ftl::gui::ControlWindow;
+using ftl::gui::ConfigWindow;
 using std::string;
 using std::vector;
 
 
 ControlWindow::ControlWindow(nanogui::Widget *parent, ftl::ctrl::Master *ctrl)
-		: nanogui::Window(parent, "Node Control"), ctrl_(ctrl) {
+		: nanogui::Window(parent, "Network Connections"), ctrl_(ctrl) {
 	setLayout(new nanogui::GroupLayout());
 
 	using namespace nanogui;
 
 	_updateDetails();
 
-	auto button = new Button(this, "Add Node", ENTYPO_ICON_PLUS);
+	auto tools = new Widget(this);
+    tools->setLayout(new BoxLayout(Orientation::Horizontal,
+                                       Alignment::Middle, 0, 6));
+
+	auto button = new Button(tools, "", ENTYPO_ICON_PLUS);
 	button->setCallback([this] {
 		// Show new connection dialog
+		_addNode();
 	});
-	button = new Button(this, "Restart All", ENTYPO_ICON_CYCLE);
+	button->setTooltip("Add new node");
+	button = new Button(tools, "", ENTYPO_ICON_CYCLE);
 	button->setCallback([this] {
 		ctrl_->restart();
 	});
-	button = new Button(this, "Pause All", ENTYPO_ICON_POWER_PLUG);
+	button = new Button(tools, "", ENTYPO_ICON_CONTROLLER_PAUS);
 	button->setCallback([this] {
 		ctrl_->pause();
 	});
+	button->setTooltip("Pause all nodes");
 
 	new Label(this, "Select Node","sans-bold");
 	auto select = new ComboBox(this, node_titles_);
@@ -42,15 +52,41 @@ ControlWindow::ControlWindow(nanogui::Widget *parent, ftl::ctrl::Master *ctrl)
 		_changeActive(ix);
 	});
 
-	button = new Button(this, "Restart Node", ENTYPO_ICON_CYCLE);
+	new Label(this, "Node Options","sans-bold");
+
+	tools = new Widget(this);
+    tools->setLayout(new BoxLayout(Orientation::Horizontal,
+                                       Alignment::Middle, 0, 6));
+
+	button = new Button(tools, "", ENTYPO_ICON_INFO);
+	button->setCallback([this] {
+		
+	});
+	button->setTooltip("Node status information");
+
+	button = new Button(tools, "", ENTYPO_ICON_COG);
+	button->setCallback([this,parent] {
+		auto cfgwin = new ConfigWindow(parent, ctrl_, _getActiveID());
+		cfgwin->setTheme(theme());
+	});
+	button->setTooltip("Edit node configuration");
+
+	button = new Button(tools, "", ENTYPO_ICON_CYCLE);
 	button->setCallback([this] {
 		ctrl_->restart(_getActiveID());
 	});
+	button->setTooltip("Restart this node");
 
-	button = new Button(this, "Pause Node", ENTYPO_ICON_POWER_PLUG);
+	button = new Button(tools, "", ENTYPO_ICON_CONTROLLER_PAUS);
 	button->setCallback([this] {
 		ctrl_->pause(_getActiveID());
 	});
+	button->setTooltip("Pause node processing");
+
+	ctrl->getNet()->onConnect([this,select](ftl::net::Peer *p) {
+		_updateDetails();
+		select->setItems(node_titles_);
+	});
 
 	_changeActive(0);
 }
@@ -59,6 +95,28 @@ ControlWindow::~ControlWindow() {
 
 }
 
+void ControlWindow::_addNode() {
+	using namespace nanogui;
+
+	FormHelper *form = new FormHelper(this->screen());
+	form->addWindow(Vector2i(100,100), "Add Node");
+
+	auto var = form->addVariable("URI", add_node_uri_);
+	var->setValue("tcp://localhost:9001");
+	var->setFixedWidth(200);
+
+	form->addButton("Add", [this,form](){
+		ctrl_->getNet()->connect(add_node_uri_);
+		form->window()->setVisible(false);
+		delete form;
+	})->setIcon(ENTYPO_ICON_PLUS);
+
+	form->addButton("Close", [form]() {
+		form->window()->setVisible(false);
+		delete form;
+	})->setIcon(ENTYPO_ICON_CROSS);
+}
+
 void ControlWindow::_updateDetails() {
 	node_details_ = ctrl_->getSlaves();
 
diff --git a/applications/gui/src/ctrl_window.hpp b/applications/gui/src/ctrl_window.hpp
index 58df7b9be88e7ddba9b344464336369c53d36d96..93a45a47e2710f3c13e4c6c1b1ae75a299dab0db 100644
--- a/applications/gui/src/ctrl_window.hpp
+++ b/applications/gui/src/ctrl_window.hpp
@@ -21,10 +21,12 @@ class ControlWindow : public nanogui::Window {
 	std::vector<ftl::config::json_t> node_details_;
 	std::vector<std::string> node_titles_;
 	int active_ix_;
+	std::string add_node_uri_;
 
 	void _updateDetails();
 	void _changeActive(int);
 	ftl::UUID _getActiveID();
+	void _addNode();
 };
 
 }
diff --git a/applications/gui/src/main.cpp b/applications/gui/src/main.cpp
index c9a807b6125fd9ac00334518985ed67d99e5ac69..69512cc07c20d6d1656ae85a453a0ad3938315de 100644
--- a/applications/gui/src/main.cpp
+++ b/applications/gui/src/main.cpp
@@ -15,6 +15,7 @@
 #include <nanogui/imageview.h>
 #include <nanogui/combobox.h>
 #include <nanogui/label.h>
+#include <nanogui/toolbutton.h>
 
 #include "ctrl_window.hpp"
 #include "src_window.hpp"
@@ -241,16 +242,120 @@ static Eigen::Affine3f create_rotation_matrix(float ax, float ay, float az) {
 
 class FTLApplication : public nanogui::Screen {
 	public:
-	explicit FTLApplication(ftl::Configurable *root, ftl::net::Universe *net, ftl::ctrl::Master *controller) : nanogui::Screen(Eigen::Vector2i(1024, 768), "FT-Lab GUI") {
+	explicit FTLApplication(ftl::Configurable *root, ftl::net::Universe *net, ftl::ctrl::Master *controller) : nanogui::Screen(Eigen::Vector2i(1024, 768), "FT-Lab Remote Presence") {
 		using namespace nanogui;
 		net_ = net;
 		ctrl_ = controller;
 
+		status_ = "FT-Lab Remote Presence System";
+
+		setSize(Vector2i(1280,720));
+
+		Theme *toolbuttheme = new Theme(*theme());
+		toolbuttheme->mBorderDark = nanogui::Color(0,0);
+		toolbuttheme->mBorderLight = nanogui::Color(0,0);
+		toolbuttheme->mButtonGradientBotFocused = nanogui::Color(60,255);
+		toolbuttheme->mButtonGradientBotUnfocused = nanogui::Color(0,0);
+		toolbuttheme->mButtonGradientTopFocused = nanogui::Color(60,255);
+		toolbuttheme->mButtonGradientTopUnfocused = nanogui::Color(0,0);
+		toolbuttheme->mButtonGradientTopPushed = nanogui::Color(60,180);
+		toolbuttheme->mButtonGradientBotPushed = nanogui::Color(60,180);
+
+		Theme *windowtheme = new Theme(*theme());
+		windowtheme->mWindowFillFocused = nanogui::Color(220, 200);
+		windowtheme->mWindowFillUnfocused = nanogui::Color(220, 200);
+		windowtheme->mWindowHeaderGradientBot = nanogui::Color(60,230);
+		windowtheme->mWindowHeaderGradientTop = nanogui::Color(60,230);
+		windowtheme->mTextColor = nanogui::Color(20,255);
+		windowtheme->mWindowCornerRadius = 2;
+		windowtheme->mButtonGradientBotFocused = nanogui::Color(210,255);
+		windowtheme->mButtonGradientBotUnfocused = nanogui::Color(190,255);
+		windowtheme->mButtonGradientTopFocused = nanogui::Color(230,255);
+		windowtheme->mButtonGradientTopUnfocused = nanogui::Color(230,255);
+		windowtheme->mButtonGradientTopPushed = nanogui::Color(170,255);
+		windowtheme->mButtonGradientBotPushed = nanogui::Color(210,255);
+		windowtheme->mBorderDark = nanogui::Color(150,255);
+		windowtheme->mBorderMedium = nanogui::Color(165,255);
+		windowtheme->mBorderLight = nanogui::Color(230,255);
+		windowtheme->mButtonFontSize = 16;
+		windowtheme->mTextColorShadow = nanogui::Color(0,0);
+		windowtheme->mWindowTitleUnfocused = windowtheme->mWindowTitleFocused;
+		windowtheme->mWindowTitleFocused = nanogui::Color(240,255);
+		windowtheme->mIconScale = 0.85f;
+
+		auto toolbar = new Window(this, "");
+		toolbar->setPosition(Vector2i(0,0));
+		toolbar->setFixedWidth(50);
+		toolbar->setFixedHeight(height());
+		//toolbar->setLayout(new BoxLayout(Orientation::Vertical,
+        //                               Alignment::Middle, 0, 10));
+
+		setResizeCallback([this,toolbar](Vector2i s) {
+			toolbar->setFixedHeight(s[1]);
+		});
+
+		auto innertool = new Widget(toolbar);
+		innertool->setLayout(new BoxLayout(Orientation::Vertical,
+                                       Alignment::Middle, 0, 10));
+		innertool->setPosition(Vector2i(5,10));
+
+		// Padding widget
+		//auto w = new Widget(innertool);
+		//w->setHeight(10);
+
+		auto button = new ToolButton(innertool, ENTYPO_ICON_HOME);
+		button->setIconExtraScale(1.5f);
+		button->setTheme(toolbuttheme);
+		button->setTooltip("Home");
+		button->setFixedSize(Vector2i(40,40));
+		button->setCallback([this]() {
+			//swindow_->setVisible(true);
+		});
+
+		button = new ToolButton(innertool, ENTYPO_ICON_EDIT);
+		button->setIconExtraScale(1.5f);
+		button->setTheme(toolbuttheme);
+		button->setTooltip("Edit Scene");
+		button->setFixedSize(Vector2i(40,40));
+		button->setCallback([this]() {
+			//swindow_->setVisible(true);
+		});
+
+		button = new ToolButton(innertool, ENTYPO_ICON_CAMERA);
+		button->setIconExtraScale(1.5f);
+		button->setTheme(toolbuttheme);
+		button->setTooltip("Camera Sources");
+		button->setFixedSize(Vector2i(40,40));
+		button->setCallback([this]() {
+			swindow_->setVisible(true);
+		});
+
+		button = new ToolButton(innertool, ENTYPO_ICON_SIGNAL);
+		button->setIconExtraScale(1.5f);
+		button->setTheme(toolbuttheme);
+		button->setTooltip("Connections");
+		button->setFixedSize(Vector2i(40,40));
+		button->setCallback([this]() {
+			cwindow_->setVisible(true);
+		});
+
+		button = new ToolButton(toolbar, ENTYPO_ICON_COG);
+		button->setIconExtraScale(1.5f);
+		button->setTheme(toolbuttheme);
+		button->setTooltip("Settings");
+		button->setFixedSize(Vector2i(40,40));
+		button->setPosition(Vector2i(5,height()-50));
+
+		//configwindow_ = new ConfigWindow(parent, ctrl_);
 		cwindow_ = new ftl::gui::ControlWindow(this, controller);
 		swindow_ = new ftl::gui::SourceWindow(this, controller);
 
-		cwindow_->setPosition(Eigen::Vector2i(20, 20));
-		swindow_->setPosition(Eigen::Vector2i(20, 400));
+		cwindow_->setPosition(Eigen::Vector2i(80, 20));
+		swindow_->setPosition(Eigen::Vector2i(80, 400));
+		cwindow_->setVisible(false);
+		swindow_->setVisible(false);
+		cwindow_->setTheme(windowtheme);
+		swindow_->setTheme(windowtheme);
 
 		//src_ = nullptr;
 		eye_ = Eigen::Vector3f(0.0f, 0.0f, 0.0f);
@@ -358,8 +463,8 @@ class FTLApplication : public nanogui::Screen {
 				neye_ += r.matrix()*Vector4f(0.0,scalar,0.0,1.0);
 				return true;
 			} else if (action == 1 && key == 'H') {
-				swindow_->setVisible(!swindow_->visible());
-				cwindow_->setVisible(!cwindow_->visible());
+				swindow_->setVisible(false);
+				cwindow_->setVisible(false);
 			} else if (action == 1 && key == 32) {
 				ctrl_->pause();
 			}
@@ -434,8 +539,9 @@ class FTLApplication : public nanogui::Screen {
 			}
 		}
 
+		Vector2f screenSize = size().cast<float>();
+
 		if (imageSize[0] > 0) {
-			Vector2f screenSize = size().cast<float>();
 			auto mScale = (screenSize.cwiseQuotient(imageSize).minCoeff());
 			Vector2f scaleFactor = mScale * imageSize.cwiseQuotient(screenSize);
 			Vector2f positionInScreen(0.0f, 0.0f);
@@ -457,7 +563,8 @@ class FTLApplication : public nanogui::Screen {
 			//glDisable(GL_SCISSOR_TEST);
 		}
 
-		nvgText(ctx, 10, 20, "FT-Lab Remote Presence System", NULL);
+		nvgTextAlign(ctx, NVG_ALIGN_RIGHT);
+		nvgText(ctx, screenSize[0]-10, screenSize[1]-20, status_.c_str(), NULL);
 
 		/* Draw the user interface */
 		screen()->performLayout(ctx);
@@ -484,6 +591,7 @@ class FTLApplication : public nanogui::Screen {
 	float delta_;
 	Eigen::Vector2f imageSize;
 	ftl::ctrl::Master *ctrl_;
+	std::string status_;
 };
 
 int main(int argc, char **argv) {
diff --git a/applications/gui/src/pose_window.cpp b/applications/gui/src/pose_window.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a3d5da3ba43872ec7183681b88d5acd88509e58f
--- /dev/null
+++ b/applications/gui/src/pose_window.cpp
@@ -0,0 +1,133 @@
+#include "pose_window.hpp"
+
+#include <nanogui/combobox.h>
+#include <nanogui/label.h>
+#include <nanogui/layout.h>
+#include <nanogui/button.h>
+
+using ftl::gui::PoseWindow;
+using std::string;
+
+PoseWindow::PoseWindow(nanogui::Widget *parent, ftl::ctrl::Master *ctrl, const std::string &src)
+		: nanogui::Window(parent, "Pose Adjust"), ctrl_(ctrl), src_(src) {
+	using namespace nanogui;
+
+	//setLayout(new nanogui::GroupLayout());
+	setLayout(new BoxLayout(Orientation::Vertical,
+                                       Alignment::Middle, 0, 6));
+	
+	pose_param_ = kPoseTranslation;
+	pose_precision_ = 0.1;
+
+	pose_ = ctrl_->getPose(src_);
+
+	//Widget *tools = new Widget(this);
+	//    tools->setLayout(new BoxLayout(Orientation::Horizontal,
+	//                                   Alignment::Middle, 0, 6));
+
+	auto grouping = new Widget(this);
+	grouping->setLayout(new GroupLayout());
+
+	new Label(grouping, "Select source","sans-bold");
+	available_ = ctrl->getNet()->findAll<string>("list_streams");
+	auto select = new ComboBox(grouping, available_);
+	select->setSelectedIndex(std::distance(available_.begin(), std::find(available_.begin(), available_.end(), src_)));
+	select->setCallback([this,select](int ix) {
+		src_ = available_[ix];
+		pose_ = ctrl_->getPose(src_);
+	});
+
+	ctrl->getNet()->onConnect([this,select](ftl::net::Peer *p) {
+		available_ = ctrl_->getNet()->findAll<string>("list_streams");
+		select->setItems(available_);
+	});
+
+	new Label(grouping, "Pose Options","sans-bold");
+
+	auto tools = new Widget(grouping);
+    tools->setLayout(new BoxLayout(Orientation::Horizontal,
+                                       Alignment::Middle, 0, 6));
+
+	auto button_rgb = new Button(tools, "Translation");
+	button_rgb->setTooltip("Adjust camera location");
+	button_rgb->setFlags(Button::RadioButton);
+	button_rgb->setPushed(true);
+	button_rgb->setChangeCallback([this](bool state) { pose_param_ = kPoseTranslation; });
+
+	auto button_depth = new Button(tools, "Rotation");
+	button_depth->setFlags(Button::RadioButton);
+	button_depth->setChangeCallback([this](bool state) { pose_param_ = kPoseRotation; });
+
+	auto button_stddev = new Button(tools, "Raw");
+	button_stddev->setTooltip("Edit the numbers directly");
+	button_stddev->setFlags(Button::RadioButton);
+	button_stddev->setChangeCallback([this](bool state) { pose_param_ = kPoseRaw; });
+
+	tools = new Widget(grouping);
+    tools->setLayout(new BoxLayout(Orientation::Horizontal,
+                                       Alignment::Middle, 0, 6));
+
+	auto button = new Button(tools, "0.1m");
+	button->setFlags(Button::RadioButton);
+	button->setPushed(true);
+	button->setCallback([this](){
+		pose_precision_ = 0.1f;
+	});
+
+	button = new Button(tools, "0.01m");
+	button->setFlags(Button::RadioButton);
+	button->setCallback([this](){
+		pose_precision_ = 0.01f;
+	});
+
+	button = new Button(tools, "0.001m");
+	button->setFlags(Button::RadioButton);
+	button->setCallback([this](){
+		pose_precision_ = 0.001f;
+	});
+
+	tools = new Widget(this);
+	auto grid = new GridLayout(Orientation::Horizontal, 3, Alignment::Middle, 5, 4);
+	tools->setLayout(grid);
+	tools->setFixedWidth(150);
+	
+
+	new Widget(tools);
+	button = new Button(tools, "", ENTYPO_ICON_CHEVRON_UP);
+	button->setCallback([this]() {
+		Eigen::Affine3d transform(Eigen::Translation3d(0.0,0.0,-pose_precision_));
+		Eigen::Matrix4d matrix = transform.matrix();
+		pose_ *= matrix;
+		ctrl_->setPose(src_, pose_);
+	});
+	new Widget(tools);
+
+	button = new Button(tools, "", ENTYPO_ICON_CHEVRON_LEFT);
+	button->setCallback([this]() {
+		Eigen::Affine3d transform(Eigen::Translation3d(-pose_precision_,0.0,0.0));
+		Eigen::Matrix4d matrix = transform.matrix();
+		pose_ *= matrix;
+		ctrl_->setPose(src_, pose_);
+	});
+	new Widget(tools);
+	button = new Button(tools, "", ENTYPO_ICON_CHEVRON_RIGHT);
+	button->setCallback([this]() {
+		Eigen::Affine3d transform(Eigen::Translation3d(pose_precision_,0.0,0.0));
+		Eigen::Matrix4d matrix = transform.matrix();
+		pose_ *= matrix;
+		ctrl_->setPose(src_, pose_);
+	});
+
+	new Widget(tools);
+	button = new Button(tools, "", ENTYPO_ICON_CHEVRON_DOWN);
+	button->setCallback([this]() {
+		Eigen::Affine3d transform(Eigen::Translation3d(0.0,0.0,pose_precision_));
+		Eigen::Matrix4d matrix = transform.matrix();
+		pose_ *= matrix;
+		ctrl_->setPose(src_, pose_);
+	});
+}
+
+PoseWindow::~PoseWindow() {
+
+}
diff --git a/applications/gui/src/pose_window.hpp b/applications/gui/src/pose_window.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..cfe03adffaeec7e023243f4880ff5df669d0ac49
--- /dev/null
+++ b/applications/gui/src/pose_window.hpp
@@ -0,0 +1,38 @@
+#ifndef _FTL_GUI_POSEWINDOW_HPP_
+#define _FTL_GUI_POSEWINDOW_HPP_
+
+#include <nanogui/window.h>
+#include <ftl/master.hpp>
+#include <ftl/uuid.hpp>
+
+namespace ftl {
+namespace gui {
+
+/**
+ * Manage connected nodes and add new connections.
+ */
+class PoseWindow : public nanogui::Window {
+	public:
+	PoseWindow(nanogui::Widget *parent, ftl::ctrl::Master *ctrl, const std::string &src);
+	~PoseWindow();
+
+	private:
+	ftl::ctrl::Master *ctrl_;
+	std::vector<std::string> available_;
+	std::string src_;
+
+	enum poseparameter_t {
+		kPoseTranslation,
+		kPoseRotation,
+		kPoseRaw
+	};
+
+	poseparameter_t pose_param_;
+	float pose_precision_;
+	Eigen::Matrix4d pose_;
+};
+
+}
+}
+
+#endif  // _FTL_GUI_POSEWINDOW_HPP_
diff --git a/applications/gui/src/src_window.cpp b/applications/gui/src/src_window.cpp
index ad61ccccb14555418106b9e408dc48e3daeccee2..0a6b0feb62cba81eb2b2b331bc7509a79db6ed09 100644
--- a/applications/gui/src/src_window.cpp
+++ b/applications/gui/src/src_window.cpp
@@ -1,5 +1,7 @@
 #include "src_window.hpp"
 
+#include "pose_window.hpp"
+
 #include <nanogui/imageview.h>
 #include <nanogui/textbox.h>
 #include <nanogui/slider.h>
@@ -93,28 +95,41 @@ SourceWindow::SourceWindow(nanogui::Widget *parent, ftl::ctrl::Master *ctrl)
 	select->setCallback([this,select](int ix) {
 		LOG(INFO) << "Change source: " << ix;
 		src_->set("uri", available_[ix]);
-});
+	});
 
 	ctrl->getNet()->onConnect([this,select](ftl::net::Peer *p) {
 		available_ = ctrl_->getNet()->findAll<string>("list_streams");
 		select->setItems(available_);
 	});
 
-	auto button_rgb = new Button(this, "RGB (left)");
+	new Label(this, "Source Options","sans-bold");
+
+	auto tools = new Widget(this);
+    tools->setLayout(new BoxLayout(Orientation::Horizontal,
+                                       Alignment::Middle, 0, 6));
+
+	auto button_rgb = new Button(tools, "RGB");
+	button_rgb->setTooltip("RGB left image");
 	button_rgb->setFlags(Button::RadioButton);
 	button_rgb->setPushed(true);
 	button_rgb->setChangeCallback([this](bool state) { mode_ = Mode::rgb; });
 
-	auto button_depth = new Button(this, "Depth");
+	auto button_depth = new Button(tools, "Depth");
 	button_depth->setFlags(Button::RadioButton);
 	button_depth->setChangeCallback([this](bool state) { mode_ = Mode::depth; });
 
-	auto button_stddev = new Button(this, "Standard Deviation (25 frames)");
+	auto button_stddev = new Button(tools, "SD. 25");
+	button_stddev->setTooltip("Standard Deviation over 25 frames");
 	button_stddev->setFlags(Button::RadioButton);
 	button_stddev->setChangeCallback([this](bool state) { mode_ = Mode::stddev; });
 
+	auto button_pose = new Button(this, "Adjust Pose", ENTYPO_ICON_COMPASS);
+	button_pose->setCallback([this]() {
+		new PoseWindow(screen(), ctrl_, src_->getURI());
+	});
+
 #ifdef HAVE_LIBARCHIVE
-	auto button_snapshot = new Button(this, "Snapshot");
+	auto button_snapshot = new Button(this, "Snapshot", ENTYPO_ICON_IMAGES);
 	button_snapshot->setCallback([this] {
 		try {
 			char timestamp[18];
diff --git a/applications/reconstruct/include/ftl/ray_cast_sdf.hpp b/applications/reconstruct/include/ftl/ray_cast_sdf.hpp
index ff53d58c3f99a0bdee4a086ffd9d33fb488b9859..4f263065f009f90fdbcb317649eae74dc1aedd16 100644
--- a/applications/reconstruct/include/ftl/ray_cast_sdf.hpp
+++ b/applications/reconstruct/include/ftl/ray_cast_sdf.hpp
@@ -16,6 +16,27 @@ public:
 		auto &cfg = ftl::config::resolve(config);
 		create(parametersFromConfig(cfg));
 		hash_render_ = value("hash_renderer", false);
+
+		on("hash_renderer", [this](const ftl::config::Event &e) {
+			hash_render_ = value("hash_renderer", false);
+		});
+
+		on("width", [this](const ftl::config::Event &e) {
+			m_params.m_width = value("width", 640);
+		});
+
+		on("height", [this](const ftl::config::Event &e) {
+			m_params.m_height = value("height", 480);
+		});
+
+		on("width", [this](const ftl::config::Event &e) {
+			m_params.m_width = value("width", 640);
+		});
+
+		on("showBlockBorders", [this](const ftl::config::Event &e) {
+			if (value("showBlockBorders", false)) m_params.m_flags |= kShowBlockBorders;
+			else m_params.m_flags &= ~kShowBlockBorders;
+		});
 	}
 
 	bool isIntegerDepth() const { return hash_render_; }
diff --git a/applications/reconstruct/include/ftl/scene_rep_hash_sdf.hpp b/applications/reconstruct/include/ftl/scene_rep_hash_sdf.hpp
index 55303cef06bfc58a39e8ca780e3dfad03536790e..fa0d6f46ed5c344e113b63ec79e875d2bef5b40e 100644
--- a/applications/reconstruct/include/ftl/scene_rep_hash_sdf.hpp
+++ b/applications/reconstruct/include/ftl/scene_rep_hash_sdf.hpp
@@ -35,7 +35,7 @@ namespace voxhash {
 
 class SceneRep : public ftl::Configurable {
 	public:
-	SceneRep(nlohmann::json &config) : Configurable(config) {
+	SceneRep(nlohmann::json &config) : Configurable(config), do_reset_(false) {
 		REQUIRED({
 			{"hashNumBuckets", "Desired hash entries divide bucket size", "number"},
 			{"hashMaxCollisionLinkedListSize", "", "number"},
@@ -47,30 +47,52 @@ class SceneRep : public ftl::Configurable {
 			{"SDFIntegrationWeightSample", "", "number"},
 			{"SDFIntegrationWeightMax", "", "number"}
 		});
-		create(parametersFromConfig(config));
+		create(parametersFromConfig());
+
+		on("SDFVoxelSize", [this](const ftl::config::Event &e) {
+			do_reset_ = true;
+		});
+		on("hashNumSDFBlocks", [this](const ftl::config::Event &e) {
+			do_reset_ = true;
+		});
+		on("hashNumBuckets", [this](const ftl::config::Event &e) {
+			do_reset_ = true;
+		});
+		on("hashMaxCollisionLinkedListSize", [this](const ftl::config::Event &e) {
+			do_reset_ = true;
+		});
+		on("SDFTruncation", [this](const ftl::config::Event &e) {
+			m_hashParams.m_truncation = value("SDFTruncation", 0.1f);
+		});
+		on("SDFTruncationScale", [this](const ftl::config::Event &e) {
+			m_hashParams.m_truncScale = value("SDFTruncationScale", 0.01f);
+		});
+		on("SDFMaxIntegrationDistance", [this](const ftl::config::Event &e) {
+			m_hashParams.m_maxIntegrationDistance = value("SDFMaxIntegrationDistance", 10.0f);
+		});
 	}
 	~SceneRep() {
 		destroy();
 	}
 
-	static HashParams parametersFromConfig(nlohmann::json &config) {
-		auto &cfg = ftl::config::resolve(config);
+	HashParams parametersFromConfig() {
+		//auto &cfg = ftl::config::resolve(config);
 		HashParams params;
 		// First camera view is set to identity pose to be at the centre of
 		// the virtual coordinate space.
 		params.m_rigidTransform.setIdentity();
 		params.m_rigidTransformInverse.setIdentity();
-		params.m_hashNumBuckets = cfg["hashNumBuckets"];
+		params.m_hashNumBuckets = value("hashNumBuckets", 100000);
 		params.m_hashBucketSize = HASH_BUCKET_SIZE;
-		params.m_hashMaxCollisionLinkedListSize = cfg["hashMaxCollisionLinkedListSize"];
+		params.m_hashMaxCollisionLinkedListSize = value("hashMaxCollisionLinkedListSize", 7);
 		params.m_SDFBlockSize = SDF_BLOCK_SIZE;
-		params.m_numSDFBlocks = cfg["hashNumSDFBlocks"];
-		params.m_virtualVoxelSize = cfg["SDFVoxelSize"];
-		params.m_maxIntegrationDistance = cfg["SDFMaxIntegrationDistance"];
-		params.m_truncation = cfg["SDFTruncation"];
-		params.m_truncScale = cfg["SDFTruncationScale"];
-		params.m_integrationWeightSample = cfg["SDFIntegrationWeightSample"];
-		params.m_integrationWeightMax = cfg["SDFIntegrationWeightMax"];
+		params.m_numSDFBlocks = value("hashNumSDFBlocks",500000);
+		params.m_virtualVoxelSize = value("SDFVoxelSize", 0.006f);
+		params.m_maxIntegrationDistance = value("SDFMaxIntegrationDistance", 10.0f);
+		params.m_truncation = value("SDFTruncation", 0.1f);
+		params.m_truncScale = value("SDFTruncationScale", 0.01f);
+		params.m_integrationWeightSample = value("SDFIntegrationWeightSample", 10);
+		params.m_integrationWeightMax = value("SDFIntegrationWeightMax", 255);
 		// Note (Nick): We are not streaming voxels in/out of GPU
 		//params.m_streamingVoxelExtents = MatrixConversion::toCUDA(gas.s_streamingVoxelExtents);
 		//params.m_streamingGridDimensions = MatrixConversion::toCUDA(gas.s_streamingGridDimensions);
@@ -125,8 +147,14 @@ class SceneRep : public ftl::Configurable {
 
 	/* Nick: To reduce weights between frames */
 	void nextFrame() {
-		starveVoxelsKernelCUDA(m_hashData, m_hashParams);
-		m_numIntegratedFrames = 0;
+		if (do_reset_) {
+			do_reset_ = false;
+			destroy();
+			create(parametersFromConfig());
+		} else {
+			starveVoxelsKernelCUDA(m_hashData, m_hashParams);
+			m_numIntegratedFrames = 0;
+		}
 	}
 
 	//! resets the hash to the initial state (i.e., clears all data)
@@ -375,6 +403,7 @@ private:
 
 	//CUDAScan		m_cudaScan;
 	unsigned int	m_numIntegratedFrames;	//used for garbage collect
+	bool do_reset_;
 
 	// static Timer m_timer;
 };
diff --git a/applications/reconstruct/src/ray_cast_sdf.cu b/applications/reconstruct/src/ray_cast_sdf.cu
index ee2c16a0108f8928342409dbeb8c815cf675b96a..bf305af94a530cc982ea61cdc8d235711cb7a752 100644
--- a/applications/reconstruct/src/ray_cast_sdf.cu
+++ b/applications/reconstruct/src/ray_cast_sdf.cu
@@ -87,7 +87,7 @@ __global__ void clearDepthKernel(ftl::voxhash::HashData hashData, RayCastData ra
 
 	if (x < rayCastParams.m_width && y < rayCastParams.m_height) {
 		rayCastData.d_depth_i[y*rayCastParams.m_width+x] = 0x7FFFFFFF; //PINF;
-		rayCastData.d_colors[y*rayCastParams.m_width+x] = make_uchar3(0,0,0);
+		rayCastData.d_colors[y*rayCastParams.m_width+x] = make_uchar3(76,76,82);
 	}
 }
 
diff --git a/applications/reconstruct/src/virtual_source.cpp b/applications/reconstruct/src/virtual_source.cpp
index 0bc4abf4a72b089b50b786bd2d64daa4d1e94c02..80f08a0e35e9a6c3966da82923a775711a2b26c8 100644
--- a/applications/reconstruct/src/virtual_source.cpp
+++ b/applications/reconstruct/src/virtual_source.cpp
@@ -27,6 +27,19 @@ VirtualSource::VirtualSource(ftl::rgbd::Source *host)
 	rgb_ = cv::Mat(cv::Size(params_.width,params_.height), CV_8UC3);
 	idepth_ = cv::Mat(cv::Size(params_.width,params_.height), CV_32SC1);
 	depth_ = cv::Mat(cv::Size(params_.width,params_.height), CV_32FC1);
+
+	rays_->on("focal", [this](const ftl::config::Event &e) {
+		params_.fx = rays_->value("focal", 430.0f);
+		params_.fy = params_.fx;
+	});
+
+	rays_->on("width", [this](const ftl::config::Event &e) {
+		params_.width = rays_->value("width", 640);
+	});
+
+	rays_->on("height", [this](const ftl::config::Event &e) {
+		params_.height = rays_->value("height", 480);
+	});
 }
 
 VirtualSource::~VirtualSource() {
diff --git a/components/common/cpp/include/ftl/configuration.hpp b/components/common/cpp/include/ftl/configuration.hpp
index 18ce61f4c9bcfcca73a3ba05a690f18489a06f66..db6cb14383d4dd6e7342e2ea8e56b31bfff510ba 100644
--- a/components/common/cpp/include/ftl/configuration.hpp
+++ b/components/common/cpp/include/ftl/configuration.hpp
@@ -68,6 +68,8 @@ json_t &resolveWait(const std::string &);
  */
 Configurable *find(const std::string &uri);
 
+std::vector<std::string> list();
+
 /**
  * Adds a Configurable instance to the database of instances so that it can
  * then be resolved using find().
diff --git a/components/common/cpp/src/configuration.cpp b/components/common/cpp/src/configuration.cpp
index b849c6ff13376ee0e1046244b7880066546c7d7f..20c07b978f7b2db92387659267518e2e42478796 100644
--- a/components/common/cpp/src/configuration.cpp
+++ b/components/common/cpp/src/configuration.cpp
@@ -189,6 +189,14 @@ ftl::Configurable *ftl::config::find(const std::string &uri) {
 	else return (*ix).second;
 }
 
+std::vector<std::string> ftl::config::list() {
+	vector<string> r;
+	for (auto i : config_instance) {
+		r.push_back(i.first);
+	}
+	return r;
+}
+
 void ftl::config::registerConfigurable(ftl::Configurable *cfg) {
 	auto uri = cfg->get<string>("$id");
 	if (!uri) {
diff --git a/components/control/cpp/CMakeLists.txt b/components/control/cpp/CMakeLists.txt
index 0705bc6e094c68b04ed0d9ad75a18504009eac18..f55ec9c19f0e6c30c3eafbaf203ee6a2e49b4ceb 100644
--- a/components/control/cpp/CMakeLists.txt
+++ b/components/control/cpp/CMakeLists.txt
@@ -7,7 +7,7 @@ target_include_directories(ftlctrl PUBLIC
 	$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
 	$<INSTALL_INTERFACE:include>
 	PRIVATE src)
-target_link_libraries(ftlctrl ftlcommon ftlnet)
+target_link_libraries(ftlctrl ftlcommon ftlnet Eigen3::Eigen)
 
 install(TARGETS ftlctrl EXPORT ftlctrl-config
 	ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
diff --git a/components/control/cpp/include/ftl/master.hpp b/components/control/cpp/include/ftl/master.hpp
index 91a913ed93b0598aac656ae5196912be6a9a1d54..9b658aaadbedccdd86c452318ef1832230836ef7 100644
--- a/components/control/cpp/include/ftl/master.hpp
+++ b/components/control/cpp/include/ftl/master.hpp
@@ -7,6 +7,7 @@
 #include <functional>
 #include <string>
 #include <vector>
+#include <Eigen/Eigen>
 
 namespace ftl {
 namespace ctrl {
@@ -36,7 +37,7 @@ class Master {
 
 	void set(const std::string &uri, ftl::config::json_t &value);
 
-	void set(const ftl::UUID &peer, const std::string &uri, ftl::config::json_t &value);
+	void set(const ftl::UUID &peer, const std::string &uri, const ftl::config::json_t &value);
 
 	std::vector<std::string> getConfigurables();
 
@@ -52,6 +53,10 @@ class Master {
 
 	void watch(const std::string &uri, std::function<void()> f);
 
+	Eigen::Matrix4d getPose(const std::string &uri);
+
+	void setPose(const std::string &uri, const Eigen::Matrix4d &pose);
+
 	// Events
 
 	//void onError();
diff --git a/components/control/cpp/src/master.cpp b/components/control/cpp/src/master.cpp
index 823939d64b27ba53cca6a7aacd41048d3c795c99..086cfbcb1a0a8424c865bdc7d90b41bfb0b13c71 100644
--- a/components/control/cpp/src/master.cpp
+++ b/components/control/cpp/src/master.cpp
@@ -65,8 +65,9 @@ void Master::set(const string &uri, json_t &value) {
 	net_->broadcast("update_cfg", uri, (string)value);
 }
 
-void Master::set(const ftl::UUID &peer, const string &uri, json_t &value) {
-	net_->send(peer, "update_cfg", uri, (string)value);
+void Master::set(const ftl::UUID &peer, const string &uri, const json_t &value) {
+	LOG(INFO) << "CHANGE: " << uri;
+	net_->send(peer, "update_cfg", uri, value.dump());
 }
 
 vector<json_t> Master::getSlaves() {
@@ -83,7 +84,7 @@ vector<string> Master::getConfigurables() {
 }
 
 vector<string> Master::getConfigurables(const ftl::UUID &peer) {
-	return {};
+	return net_->call<vector<string>>(peer, "list_configurables");
 }
 
 vector<json_t> Master::get(const string &uri) {
@@ -95,13 +96,32 @@ json_t Master::getOne(const string &uri) {
 }
 
 json_t Master::get(const ftl::UUID &peer, const string &uri) {
-	return {};
+	return json_t::parse(net_->call<string>(peer, "get_cfg", uri));
 }
 
 void Master::watch(const string &uri, function<void()> f) {
 
 }
 
+Eigen::Matrix4d Master::getPose(const std::string &uri) {
+	auto r = net_->findOne<vector<unsigned char>>("get_pose", uri);
+	if (r) {
+		Eigen::Matrix4d pose;
+		memcpy(pose.data(), (*r).data(), (*r).size());
+		return pose;
+	} else {
+		LOG(WARNING) << "No pose found for " << uri;
+		Eigen::Matrix4d pose;
+		pose.setIdentity();
+		return pose;
+	}
+}
+
+void Master::setPose(const std::string &uri, const Eigen::Matrix4d &pose) {
+	vector<unsigned char> vec((unsigned char*)pose.data(), (unsigned char*)(pose.data()+(pose.size())));
+	net_->broadcast("set_pose", uri, vec);
+}
+
 // Events
 
 //void onError();
diff --git a/components/control/cpp/src/slave.cpp b/components/control/cpp/src/slave.cpp
index b4823e1d11f1df861e27dca5818ccf7f3b9ae901..88933ed7a445c76caaca3c69bab4971db62a39c0 100644
--- a/components/control/cpp/src/slave.cpp
+++ b/components/control/cpp/src/slave.cpp
@@ -39,7 +39,11 @@ Slave::Slave(Universe *net, ftl::Configurable *root) : net_(net), in_log_(false)
 	});
 
 	net->bind("get_cfg", [](const std::string &uri) -> std::string {
-		return ftl::config::resolve(uri);
+		return ftl::config::resolve(uri, false).dump();
+	});
+
+	net->bind("list_configurables", []() {
+		return ftl::config::list();
 	});
 
 	net->bind("node_details", [net,root]() -> std::vector<std::string> {
diff --git a/components/rgbd-sources/include/ftl/rgbd/camera.hpp b/components/rgbd-sources/include/ftl/rgbd/camera.hpp
index 3f46148874d7b5104f8f1431d675fab2170daee3..b5ad7ea1d2dceac1f3550cc437c1e734a681e6f9 100644
--- a/components/rgbd-sources/include/ftl/rgbd/camera.hpp
+++ b/components/rgbd-sources/include/ftl/rgbd/camera.hpp
@@ -14,6 +14,7 @@ struct Camera {
 	unsigned int height;
 	double minDepth;
 	double maxDepth;
+	double baseline;
 };
 
 };
diff --git a/components/rgbd-sources/src/calibrate.cpp b/components/rgbd-sources/src/calibrate.cpp
index 51767128eca13de622e4113ff8c53390cd9ef9fa..f0b98a50547e5fe3182dc1769c3eddaf3552b2e9 100644
--- a/components/rgbd-sources/src/calibrate.cpp
+++ b/components/rgbd-sources/src/calibrate.cpp
@@ -76,12 +76,11 @@ bool Calibrate::_loadCalibration(cv::Size img_size, std::pair<Mat, Mat> &map1, s
 		return false;
 	}
 
-	Mat M1, D1, M2, D2;
-	fs["M"] >> M1;
-	fs["D"] >> D1;
+	fs["M"] >> M1_;
+	fs["D"] >> D1_;
 
-	M2 = M1;
-	D2 = D1;
+	M2_ = M1_;
+	D2_ = D1_;
 
 	auto efile = ftl::locateFile("extrinsics.yml");
 	if (efile) {
@@ -97,24 +96,40 @@ bool Calibrate::_loadCalibration(cv::Size img_size, std::pair<Mat, Mat> &map1, s
 		return false;
 	}
 
-	Mat R, T, R1, P1, R2, P2;
-	fs["R"] >> R;
-	fs["T"] >> T;
-	fs["R1"] >> R1;
-	fs["R2"] >> R2;
-	fs["P1"] >> P1;
-	fs["P2"] >> P2;
+	fs["R"] >> R_;
+	fs["T"] >> T_;
+	fs["R1"] >> R1_;
+	fs["R2"] >> R2_;
+	fs["P1"] >> P1_;
+	fs["P2"] >> P2_;
 	fs["Q"] >> Q_;
 
-	P_ = P1;
+	P_ = P1_;
+
+	img_size_ = img_size;
 
 	// cv::cuda::remap() works only with CV_32FC1
-	initUndistortRectifyMap(M1, D1, R1, P1, img_size, CV_32FC1, map1.first, map2.first);
-	initUndistortRectifyMap(M2, D2, R2, P2, img_size, CV_32FC1, map1.second, map2.second);
+	initUndistortRectifyMap(M1_, D1_, R1_, P1_, img_size_, CV_32FC1, map1.first, map2.first);
+	initUndistortRectifyMap(M2_, D2_, R2_, P2_, img_size_, CV_32FC1, map1.second, map2.second);
 
 	return true;
 }
 
+void Calibrate::updateCalibration(const ftl::rgbd::Camera &p) {
+	std::pair<Mat, Mat> map1, map2;
+
+	Q_.at<double>(3,2) = 1.0 / p.baseline;
+
+	initUndistortRectifyMap(M1_, D1_, R1_, P1_, img_size_, CV_32FC1, map1.first, map2.first);
+	initUndistortRectifyMap(M2_, D2_, R2_, P2_, img_size_, CV_32FC1, map1.second, map2.second);
+
+	// CHECK Is this thread safe!!!!
+	map1_gpu_.first.upload(map1.first);
+	map1_gpu_.second.upload(map1.second);
+	map2_gpu_.first.upload(map2.first);
+	map2_gpu_.second.upload(map2.second);
+}
+
 void Calibrate::rectifyStereo(GpuMat &l, GpuMat &r, Stream &stream) {
 	// cv::cuda::remap() can not use same Mat for input and output
 	
diff --git a/components/rgbd-sources/src/calibrate.hpp b/components/rgbd-sources/src/calibrate.hpp
index c967de2e50f1a24560119b0f15b587e1213c8441..b8b98116bf07b54526a0f8f7a7d6602904b5e1d1 100644
--- a/components/rgbd-sources/src/calibrate.hpp
+++ b/components/rgbd-sources/src/calibrate.hpp
@@ -10,6 +10,7 @@
 #include <string>
 #include <vector>
 #include <nlohmann/json.hpp>
+#include <ftl/rgbd/camera.hpp>
 
 namespace cv {
 class FileStorage;
@@ -39,7 +40,9 @@ class Calibrate : public ftl::Configurable {
 	 */
 	void rectifyStereo(cv::cuda::GpuMat &l, cv::cuda::GpuMat &r, cv::cuda::Stream &stream);
 
-	bool isCalibrated(); // TODO ???
+	bool isCalibrated();
+
+	void updateCalibration(const ftl::rgbd::Camera &p);
 
 	/**
 	 * Get the camera matrix. Used to convert disparity map back to depth and
@@ -59,6 +62,9 @@ private:
 
 	cv::Mat P_;
 	cv::Mat Q_;
+	cv::Mat R_, T_, R1_, P1_, R2_, P2_;
+	cv::Mat M1_, D1_, M2_, D2_;
+	cv::Size img_size_;
 };
 
 }
diff --git a/components/rgbd-sources/src/net.cpp b/components/rgbd-sources/src/net.cpp
index 6f915f1270785ee0e75ceb5ceed29603842006cf..b71d18e08c12b8871d9b3063aed85642b800f54d 100644
--- a/components/rgbd-sources/src/net.cpp
+++ b/components/rgbd-sources/src/net.cpp
@@ -33,6 +33,12 @@ bool NetSource::_getCalibration(Universe &net, const UUID &peer, const string &s
 				}
 
 				LOG(INFO) << "Calibration received: " << p.cx << ", " << p.cy << ", " << p.fx << ", " << p.fy;
+
+				// Put calibration into config manually
+				host_->getConfig()["focal"] = p.fx;
+				host_->getConfig()["centre_x"] = p.cx;
+				host_->getConfig()["centre_y"] = p.cy;
+				host_->getConfig()["baseline"] = p.baseline;
 				
 				return true;
 			} else {
@@ -51,6 +57,25 @@ NetSource::NetSource(ftl::rgbd::Source *host)
 	gamma_ = host->value("gamma", 1.0f);
 	temperature_ = host->value("temperature", 6500);
 
+	host->on("gamma", [this,host](const ftl::config::Event&) {
+		gamma_ = host->value("gamma", 1.0f);
+	});
+
+	host->on("temperature", [this,host](const ftl::config::Event&) {
+		temperature_ = host->value("temperature", 6500);
+	});
+
+	host->on("focal", [this,host](const ftl::config::Event&) {
+		params_.fx = host_->value("focal", 400.0);
+		params_.fy = params_.fx;
+		host_->getNet()->send(peer_, "update_cfg", host_->getURI() + "/focal", host_->getConfig()["focal"].dump());
+	});
+
+	host->on("baseline", [this,host](const ftl::config::Event&) {
+		params_.baseline = host_->value("baseline", 400.0);
+		host_->getNet()->send(peer_, "update_cfg", host_->getURI() + "/baseline", host_->getConfig()["baseline"].dump());
+	});
+
 	_updateURI();
 
 	h_ = host_->getNet()->onConnect([this](ftl::net::Peer *p) {
diff --git a/components/rgbd-sources/src/snapshot_source.cpp b/components/rgbd-sources/src/snapshot_source.cpp
index 686e55b93e75ed55e5fb55dd6d548112b7ff95e9..0e8dec08b804af6f5dd926f87f56c8e75afd34e6 100644
--- a/components/rgbd-sources/src/snapshot_source.cpp
+++ b/components/rgbd-sources/src/snapshot_source.cpp
@@ -19,5 +19,9 @@ SnapshotSource::SnapshotSource(ftl::rgbd::Source *host, SnapshotReader &reader,
 
 	ftl::rgbd::colourCorrection(rgb_, host->value("gamma", 1.0f), host->value("temperature", 6500));
 
+	host->on("gamma", [this,host](const ftl::config::Event&) {
+		ftl::rgbd::colourCorrection(rgb_, host->value("gamma", 1.0f), host->value("temperature", 6500));
+	});
+
     setPose(pose);
 }
diff --git a/components/rgbd-sources/src/stereovideo.cpp b/components/rgbd-sources/src/stereovideo.cpp
index 86a9e10b2ba6bcd0d1ca0fd2a91e1bde2406e158..0fb5fd864584aa53786392adcfc295b1aab23c2a 100644
--- a/components/rgbd-sources/src/stereovideo.cpp
+++ b/components/rgbd-sources/src/stereovideo.cpp
@@ -69,8 +69,29 @@ void StereoVideoSource::init(const string &file) {
 		(unsigned int)lsrc_->width(),
 		(unsigned int)lsrc_->height(),
 		0.0f,	// 0m min
-		15.0f	// 15m max
+		15.0f,	// 15m max
+		1.0 / calib_->getQ().at<double>(3,2) // Baseline
 	};
+
+	// Add calibration to config object
+	host_->getConfig()["focal"] = params_.fx;
+	host_->getConfig()["centre_x"] = params_.cx;
+	host_->getConfig()["centre_y"] = params_.cy;
+	host_->getConfig()["baseline"] = params_.baseline;
+
+	// Add event handlers to allow calibration changes...
+	host_->on("baseline", [this](const ftl::config::Event &e) {
+		params_.baseline = host_->value("baseline", params_.baseline);
+		std::unique_lock<std::shared_mutex> lk(host_->mutex());
+		calib_->updateCalibration(params_);
+	});
+
+	host_->on("focal", [this](const ftl::config::Event &e) {
+		params_.fx = host_->value("focal", params_.fx);
+		params_.fy = params_.fx;
+		std::unique_lock<std::shared_mutex> lk(host_->mutex());
+		calib_->updateCalibration(params_);
+	});
 	
 	// left and right masks (areas outside rectified images)
 	// only left mask used
diff --git a/components/rgbd-sources/src/streamer.cpp b/components/rgbd-sources/src/streamer.cpp
index 45ac6d4962631d96eb14fcaf88f8cfc733799757..9a6e20b5bf41902895aa8e280fc908c4e9a4526d 100644
--- a/components/rgbd-sources/src/streamer.cpp
+++ b/components/rgbd-sources/src/streamer.cpp
@@ -57,6 +57,19 @@ Streamer::Streamer(nlohmann::json &config, Universe *net)
 		}
 	});
 
+	net->bind("get_pose", [this](const std::string &uri) -> std::vector<unsigned char> {
+		SHARED_LOCK(mutex_,slk);
+
+		if (sources_.find(uri) != sources_.end()) {
+			Eigen::Matrix4d pose = sources_[uri]->src->getPose();
+			vector<unsigned char> vec((unsigned char*)pose.data(), (unsigned char*)(pose.data()+(pose.size())));
+			return vec;
+		} else {
+			LOG(WARNING) << "Requested pose not found: " << uri;
+			return {};
+		}
+	});
+
 	// Allow remote users to access camera calibration matrix
 	net->bind("source_calibration", [this](const std::string &uri) -> vector<unsigned char> {
 		vector<unsigned char> buf;