diff --git a/CMakeLists.txt b/CMakeLists.txt
index d12b4d1a411847972b0fa1380a4b487d81ac7b09..ca8b5b291db1eb9dc2e6e7ec8297d16d52a1ce14 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -114,6 +114,7 @@ endif()
 
 SET(CMAKE_USE_RELATIVE_PATHS ON)
 
+add_subdirectory(renderer)
 add_subdirectory(net)
 add_subdirectory(vision)
 add_subdirectory(reconstruct)
diff --git a/common/config/config.json b/common/config/config.json
index 104b18dc607ed2328c183e35292e3251e33e27e0..bafcaf0b4fedcfaa3aa154fe2afd9ac0ee0be026 100644
--- a/common/config/config.json
+++ b/common/config/config.json
@@ -66,7 +66,15 @@
 		"net": {
 			"peers": ["tcp://localhost:9001"]
 		},
-		"source": "ftl://utu.fi/dummy/rgb-d"
+		"source": "ftl://utu.fi/dummy/rgb-d",
+		"display": {
+			"flip_vert": false,
+			"disparity": false,
+			"points": true,
+			"depth": false,
+			"left": false,
+			"right": false
+		}
 	}
 }
 
diff --git a/net/cpp/src/dispatcher.cpp b/net/cpp/src/dispatcher.cpp
index 247a829bc4fdc123d60c5029382e86240be4a9b9..34861a04fcd08044f8f2b579f739911bbf723dbd 100644
--- a/net/cpp/src/dispatcher.cpp
+++ b/net/cpp/src/dispatcher.cpp
@@ -129,7 +129,7 @@ void ftl::net::Dispatcher::dispatch_notification(Peer &s, msgpack::object const
     auto &&name = std::get<1>(the_call);
     auto &&args = std::get<2>(the_call);
     
-    LOG(INFO) << "NOTIFICATION " << name << "() <- " << s.getURI();
+    //LOG(INFO) << "NOTIFICATION " << name << "() <- " << s.getURI();
 
     auto binding = _locateHandler(name);
 
diff --git a/reconstruct/CMakeLists.txt b/reconstruct/CMakeLists.txt
index 8dfbccded397b975de49f7782068ebeb59e19dc0..2b463475ac1c58618dfb6f8ff928bad167f7309d 100644
--- a/reconstruct/CMakeLists.txt
+++ b/reconstruct/CMakeLists.txt
@@ -12,6 +12,6 @@ add_executable(ftl-reconstruct ${REPSRC})
 add_dependencies(ftl-reconstruct ftlnet)
 
 #target_include_directories(cv-node PUBLIC ${PROJECT_SOURCE_DIR}/include)
-target_link_libraries(ftl-reconstruct Threads::Threads ZLIB::ZLIB ${OpenCV_LIBS} glog::glog ftlnet)
+target_link_libraries(ftl-reconstruct Threads::Threads ZLIB::ZLIB ${OpenCV_LIBS} glog::glog ftlnet ftlrender)
 
 
diff --git a/reconstruct/src/main.cpp b/reconstruct/src/main.cpp
index 8b7035dfccdf897a715e11a6c1f65881fcf01450..0ec511f879062e4c9e661604b527c5d31a89e077 100644
--- a/reconstruct/src/main.cpp
+++ b/reconstruct/src/main.cpp
@@ -18,6 +18,7 @@
 
 #include <opencv2/opencv.hpp>
 #include <ftl/net/universe.hpp>
+#include <ftl/display.hpp>
 #include <nlohmann/json.hpp>
 
 #include "opencv2/imgproc.hpp"
@@ -30,6 +31,7 @@
 #endif
 
 using ftl::net::Universe;
+using ftl::Display;
 using std::string;
 using std::vector;
 using std::map;
@@ -118,6 +120,8 @@ static void run(const string &file) {
 	Mat rgb, depth, Q;
 	mutex m;
 	
+	Display disp(config["display"]);
+	
 	// Make sure connections are complete
 	sleep_for(milliseconds(500));
 
@@ -144,13 +148,10 @@ static void run(const string &file) {
 		inflateEnd(&infstream);
 	});
 	
-	while (true) {
+	while (disp.active()) {
 		Mat idepth;
 		
 		unique_lock<mutex> lk(m);
-		if (rgb.cols > 0) {
-			cv::imshow("RGB", rgb);
-		}
 		if (depth.cols > 0) {
 			if (Q.rows == 0) {
 				auto buf = net.findOne<vector<unsigned char>>((string)config["source"]+"/calibration");
@@ -158,14 +159,23 @@ static void run(const string &file) {
 					Q = Mat(cv::Size(4,4), CV_32F);
 					memcpy((*buf).data(), Q.data, (*buf).size());
 					LOG(INFO) << "Have calibration";
+					if (Q.step*Q.rows != (*buf).size()) LOG(ERROR) << "Corrupted calibration";
+					disp.setCalibration(Q);
 				}
 			}
-			depth.convertTo(idepth, CV_8U, 255.0f / 256.0f);  // TODO(nick)
-    		applyColorMap(idepth, idepth, cv::COLORMAP_JET);
-			cv::imshow("Depth", idepth);
+			//depth.convertTo(idepth, CV_8U, 255.0f / 256.0f);  // TODO(nick)
+    		//applyColorMap(idepth, idepth, cv::COLORMAP_JET);
+			//cv::imshow("Depth", idepth);
 		}
+		
+		if (rgb.cols > 0) {
+			//cv::imshow("RGB", rgb);
+			disp.render(rgb,depth);
+		}
+		
 		lk.unlock();
-		if (cv::waitKey(40) == 27) break;
+		//if (cv::waitKey(40) == 27) break;
+		disp.wait(40);
 	}
 }
 
diff --git a/renderer/CMakeLists.txt b/renderer/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..dc0ad1f729f419af91db57fc2d6338f7676fb63b
--- /dev/null
+++ b/renderer/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_subdirectory(cpp)
+
diff --git a/renderer/cpp/CMakeLists.txt b/renderer/cpp/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d87223c42bb5637d83f2c8de25d04a86c3144a69
--- /dev/null
+++ b/renderer/cpp/CMakeLists.txt
@@ -0,0 +1,18 @@
+# Need to include staged files and libs
+#include_directories(${PROJECT_SOURCE_DIR}/net/cpp/include)
+#include_directories(${PROJECT_BINARY_DIR})
+
+
+add_library(ftlrender
+	src/display.cpp
+)
+
+target_include_directories(ftlrender PUBLIC
+	$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+	$<INSTALL_INTERFACE:include>
+	PRIVATE src)
+target_link_libraries(ftlrender Threads::Threads glog::glog ${OpenCV_LIBS})
+
+
+
+#ADD_SUBDIRECTORY(test)
diff --git a/vision/include/ftl/display.hpp b/renderer/cpp/include/ftl/display.hpp
similarity index 82%
rename from vision/include/ftl/display.hpp
rename to renderer/cpp/include/ftl/display.hpp
index 930f698d1dcf1e1db3f9d16bdd842fda511f7800..1daad059044aa856d7af3fb0b30be9e945aebedb 100644
--- a/vision/include/ftl/display.hpp
+++ b/renderer/cpp/include/ftl/display.hpp
@@ -9,7 +9,6 @@
 
 #include <nlohmann/json.hpp>
 #include <opencv2/opencv.hpp>
-#include <ftl/calibrate.hpp>
 #include "opencv2/highgui.hpp"
 
 namespace ftl {
@@ -19,15 +18,19 @@ namespace ftl {
  */
 class Display {
 	public:
-	Display(const Calibrate &cal, nlohmann::json &config);
+	Display(nlohmann::json &config);
 	~Display();
+	
+	void setCalibration(const cv::Mat &q) { q_ = q; }
 
 	bool render(const cv::Mat &rgb, const cv::Mat &depth);
 
 	bool active() const;
+	
+	void wait(int ms);
 
 	private:
-	const ftl::Calibrate &calibrate_;
+	cv::Mat q_;
 	nlohmann::json config_;
 
 	#if defined HAVE_VIZ
diff --git a/vision/src/display.cpp b/renderer/cpp/src/display.cpp
similarity index 77%
rename from vision/src/display.cpp
rename to renderer/cpp/src/display.cpp
index 52a8a39fa1d1ac3640ee7f7d5c4332f347a050c6..7ee8efe3c7a5099656bd89bc680049a0e8949f82 100644
--- a/vision/src/display.cpp
+++ b/renderer/cpp/src/display.cpp
@@ -7,12 +7,10 @@
 #include <ftl/display.hpp>
 
 using ftl::Display;
-using ftl::Calibrate;
 using cv::Mat;
 using cv::Vec3f;
 
-Display::Display(const Calibrate &cal, nlohmann::json &config) :
-		calibrate_(cal), config_(config) {
+Display::Display(nlohmann::json &config) : config_(config) {
 	#if defined HAVE_VIZ
 	window_ = new cv::viz::Viz3d("FTL");
 	window_->setBackgroundColor(cv::viz::Color::white());
@@ -29,12 +27,12 @@ Display::~Display() {
 bool Display::render(const cv::Mat &rgb, const cv::Mat &depth) {
 	Mat idepth;
 
-	if (config_["points"]) {
+	if (config_["points"] && q_.rows != 0) {
 		#if defined HAVE_VIZ
-		cv::Mat Q_32F;
-		calibrate_.getQ().convertTo(Q_32F, CV_32F);
+		//cv::Mat Q_32F;
+		//calibrate_.getQ().convertTo(Q_32F, CV_32F);
 		cv::Mat_<cv::Vec3f> XYZ(depth.rows, depth.cols);   // Output point cloud
-		reprojectImageTo3D(depth+20.0f, XYZ, Q_32F, true);
+		reprojectImageTo3D(depth+20.0f, XYZ, q_, true);
 
 		// Remove all invalid pixels from point cloud
 		XYZ.setTo(Vec3f(NAN, NAN, NAN), depth == 0.0f);
@@ -45,7 +43,7 @@ bool Display::render(const cv::Mat &rgb, const cv::Mat &depth) {
 		window_->showWidget("coosys", cv::viz::WCoordinateSystem());
 		window_->showWidget("Depth", cloud_widget);
 
-		window_->spinOnce(1, true);
+		//window_->spinOnce(40, true);
 
 		#else  // HAVE_VIZ
 
@@ -77,15 +75,30 @@ bool Display::render(const cv::Mat &rgb, const cv::Mat &depth) {
 
     	applyColorMap(idepth, idepth, cv::COLORMAP_JET);
 		cv::imshow("Disparity", idepth);
-		if(cv::waitKey(10) == 27) {
+		//if(cv::waitKey(40) == 27) {
 	        // exit if ESC is pressed
-	        active_ = false;
-	    }
+	    //    active_ = false;
+	    //}
     }
 
 	return true;
 }
 
+void Display::wait(int ms) {
+	if (config_["points"] && q_.rows != 0) {
+		#if defined HAVE_VIZ
+		window_->spinOnce(1, true);
+		#endif  // HAVE_VIZ
+	}
+	
+	if (config_["disparity"]) {
+		if(cv::waitKey(ms) == 27) {
+	        // exit if ESC is pressed
+	        active_ = false;
+	    }
+	}
+}
+
 bool Display::active() const {
 	#if defined HAVE_VIZ
 	return active_ && !window_->wasStopped();