diff --git a/.gitignore b/.gitignore
index 7574bf03a1675b186d231cfcfb4b6f59e0f9dc0f..c2fbdd6b5068032cf1bb480a58d97455ff534446 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ fabric/
 fabric-js/
 build/
 lab-designs/viewer
+.vscode
diff --git a/common/cpp/include/ctpl_stl.h b/common/cpp/include/ctpl_stl.h
index 5956cf095dfbad4402c400a7015bfa590aa09aab..82d8029850bad486e3e1acfcb9b3f51886e8f6d2 100644
--- a/common/cpp/include/ctpl_stl.h
+++ b/common/cpp/include/ctpl_stl.h
@@ -73,7 +73,7 @@ namespace ctpl {
     public:
 
         thread_pool() { this->init(); }
-        thread_pool(int nThreads) { this->init(); this->resize(nThreads); }
+        explicit thread_pool(int nThreads) { this->init(); this->resize(nThreads); }
 
         // the destructor waits for all the functions in the queue to be finished
         ~thread_pool() {
diff --git a/common/cpp/src/configuration.cpp b/common/cpp/src/configuration.cpp
index 6a7df835da6b6b2ad8c914de3a8c0ee95528c219..51a58f0348f4397b3bbfcf6740ff50e2b21e2231 100644
--- a/common/cpp/src/configuration.cpp
+++ b/common/cpp/src/configuration.cpp
@@ -191,7 +191,7 @@ static void process_options(const map<string, string> &opts) {
 		if (opt.first == "config") continue;
 
 		if (opt.first == "version") {
-			std::cout << "FTL Vision Node - v" << FTL_VERSION << std::endl;
+			std::cout << "Future-Tech Lab - v" << FTL_VERSION << std::endl;
 			std::cout << FTL_VERSION_LONG << std::endl;
 			exit(0);
 		}
diff --git a/net/cpp/src/ws_internal.cpp b/net/cpp/src/ws_internal.cpp
index 1b8038d5277bdcaabc89510c2cab1bc5fa2c1926..4c3e72a1bb2bec776616a6e859eb4e863157c7e8 100644
--- a/net/cpp/src/ws_internal.cpp
+++ b/net/cpp/src/ws_internal.cpp
@@ -159,7 +159,7 @@ bool ftl::net::ws_connect(int sockfd, const URI &uri) {
 	http += "Sec-WebSocket-Version: 13\r\n";
 	http += "\r\n";
 	int rc = ::send(sockfd, http.c_str(), http.length(), 0);
-	if (rc != http.length()) {
+	if (rc != (int)http.length()) {
 		LOG(ERROR) << "Could not send Websocket http request...";
 		std::cout << http;
 		return false;
diff --git a/net/js/src/index.js b/net/js/src/index.js
index bddfe81424d151315e579f8c25f1f1c387373734..c5ea8c620a15dd6ec16d00ae9132aeec4f78d5b9 100644
--- a/net/js/src/index.js
+++ b/net/js/src/index.js
@@ -1,2 +1,3 @@
-export.Socket = require('./socket.js');
+export.Peer = require('./peer.js');
+export.Universe = require('./universe.js');
 
diff --git a/net/js/src/listener.js b/net/js/src/listener.js
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/net/js/src/peer.js b/net/js/src/peer.js
new file mode 100644
index 0000000000000000000000000000000000000000..d330f93c38290283f2255abe4628a0b9a6be241d
--- /dev/null
+++ b/net/js/src/peer.js
@@ -0,0 +1,280 @@
+const net = require('net');
+const ws = require('ws');
+const urijs = require('uri-js');
+const binary = require('bops');
+const browser = require('detect-browser').detect();
+const isbrowser = !browser || browser.name != "node";
+
+class Peer {
+	constructor(uri) {
+		let t = typeof uri;
+		this.handlers_ = {
+			'open': [],
+			'data': [],
+			'error': [],
+			'close': []
+		};
+		this.handshake_ = false;
+		this.lasterr_ = null;
+		this.connected_ = false;
+		this.valid_ = false;
+		this.uuid_ = binary.create(16);
+		if (!isbrowser) {
+			this.buffer_ = new Buffer(0); // Only in nodejs
+		}
+		if (t == "string") {
+			this._fromURI(uri);
+		}
+		else if (t == "object") {
+			this._fromObject(uri);
+		}
+	}
+	error(errno) {
+		this.lasterr_ = errno;
+		this.dispatch('error', [errno]);
+	}
+	isValid() {
+		return this.valid_;
+	}
+    /**
+     * Construct the correct kind of socket connection from a URI.
+     */
+	_fromURI(uri) {
+		let uriobj = urijs.parse(uri);
+		this.uri_ = uri;
+		this.scheme_ = uriobj.scheme;
+		// Could not parse uri so report error
+		if (uriobj.scheme === undefined || uriobj.host === undefined) {
+			this.error(Socket.ERROR_MALFORMEDURI);
+			return;
+		}
+		// Websocket protocol
+		if (this.scheme_ == "ws") {
+			// Detect if in browser or not, choose correct websocket object
+			if (typeof WebSocket == "undefined") {
+				// Nodejs
+				this.socket_ = new ws(uri);
+			}
+			else {
+				// Browser
+				this.socket_ = new WebSocket(uri);
+			}
+			this._initWebsocket();
+			// TCP
+		}
+		else if (this.scheme_ == "tcp") {
+			if (!isbrowser) {
+				this.socket_ = net.connect(uriobj.port, uriobj.host);
+				this._initTCPSocket();
+			}
+			else {
+				this.error(Socket.ERROR_TCPINBROWSER);
+			}
+			// Unrecognised protocol
+		}
+		else {
+			this.error(Socket.ERROR_BADPROTOCOL);
+		}
+	}
+	_fromObject(sock) {
+		this.socket_ = sock;
+		if (typeof WebSocket == "undefined") {
+			if (sock instanceof ws)
+				this.scheme_ = "ws";
+			else if (sock instanceof net.Socket)
+				this.scheme_ = "tcp";
+			else
+				this.scheme_ = null;
+		}
+		else {
+			if (sock instanceof WebSocket)
+				this.scheme_ = "ws";
+			else
+				this.scheme_ = null;
+		}
+		if (this.scheme_ == "ws")
+			this._initWebsocket();
+		else if (this.scheme_ == "tcp")
+			this._initTCPSocket();
+	}
+    /**
+     * Setup correct handlers for a websocket connection.
+     */
+	_initWebsocket() {
+		this.valid_ = true;
+		let dataHandler = (data) => {
+			this.processMessage(data);
+		};
+		if (this.socket_.addEventHandler) {
+			this.socket_.addEventHandler('message', event => {
+				dataHandler(event.data);
+			});
+		}
+		else {
+			this.socket_.on('message', dataHandler);
+		}
+		this.socket_.on('open', () => {
+			//this.connected_ = true;
+			//this.dispatch('open', []);
+		});
+		this.socket_.on('error', (err) => {
+			this.connected_ = false;
+			this.valid_ = false;
+			switch (err.errno) {
+				case 'ENOTFOUND':
+					this.lasterr_ = Socket.ERROR_BADHOST;
+					break;
+				default: this.lasterr_ = err.errno;
+			}
+			this.dispatch('error', [this.lasterr_]);
+		});
+		this.socket_.on('close', () => {
+			this.dispatch('close', []);
+		});
+	}
+	processMessage(buffer) {
+		if (!this.handshake_) {
+			// Check handshake
+			if (!checkMagic(buffer)) {
+				this.close();
+				this.error(Socket.ERROR_BADHANDSHAKE);
+				return 0;
+			}
+			binary.copy(buffer, this.uuid_, 0, 8, 16);
+			let proto_size = binary.readUInt32LE(buffer, 24);
+			this.handshake_ = true;
+			this.connected_ = true;
+			this.dispatch('open', []);
+			return 28 + proto_size;
+		}
+		else {
+			let size = binary.readUInt32LE(buffer, 0);
+			let service = binary.readUInt32LE(buffer, 4);
+			console.log("Message: " + service + "(size=" + size + ")");
+			// Do we have a complete message yet?
+			if (size > 1024 * 1024 * 100) {
+				this.error(Socket.ERROR_LARGEMESSAGE);
+				this.close();
+				return 0;
+			}
+			else if (buffer.length - 4 >= size) {
+				// Yes, so dispatch
+				this.dispatch(service, [size, binary.subarray(buffer, 8)]);
+				return size + 4;
+			}
+			else {
+				return 0;
+			}
+		}
+	}
+    /**
+     * Setup TCP socket handlers and message buffering mechanism.
+     */
+	_initTCPSocket() {
+		this.valid_ = true;
+		let dataHandler = (data) => {
+			this.buffer_ = Buffer.concat([this.buffer_, data]);
+			while (this.buffer_.length >= 8) {
+				let s = this.processMessage(this.buffer_);
+				if (s == 0)
+					break;
+				this.buffer_ = binary.subarray(this.buffer_, s);
+			}
+		};
+		this.socket_.on('data', dataHandler);
+		this.socket_.on('connect', () => {
+			//this.connected_ = true;
+			//this.dispatch('open', []);
+		});
+		this.socket_.on('error', (err) => {
+			this.connected_ = false;
+			this.valid_ = false;
+			switch (err.errno) {
+				case 'ENOTFOUND':
+					this.error(Socket.ERROR_BADHOST);
+					break;
+				default: this.error(err.errno);
+			}
+		});
+		this.socket_.on('close', () => {
+			this.dispatch('close', []);
+		});
+	}
+	isConnected() {
+		return this.connected_;
+	}
+    /**
+     * Register event handlers.
+     */
+	on(name, f) {
+		if (typeof name == "string") {
+			if (this.handlers_.hasOwnProperty(name)) {
+				this.handlers_[name].push(f);
+			}
+			else {
+				console.error("Unrecognised handler: ", name);
+			}
+			if (name == "error" && this.lasterr_ != null) {
+				f(this.lasterr_);
+			}
+		}
+		else if (typeof name == "number") {
+			if (this.handlers_[name] === undefined)
+				this.handlers_[name] = [];
+			this.handlers_[name].push(f);
+		}
+		else {
+			console.error("Invalid handler: ", name);
+		}
+	}
+	dispatch(h, args) {
+		if (this.handlers_.hasOwnProperty(h)) {
+			let hs = this.handlers_[h];
+			for (var i = 0; i < hs.length; i++) {
+				hs[i].apply(this, args);
+			}
+			return true;
+		}
+		else {
+			return false;
+		}
+	}
+	close() {
+		if (this.socket_ == null)
+			return;
+		if (this.scheme_ == "ws") {
+			this.socket_.close();
+		}
+		else {
+			this.socket_.destroy();
+		}
+		this.socket_ = null;
+	}
+	_socket() {
+		return this.socket_;
+	}
+	getURI() {
+		return this.uri_;
+	}
+	asyncCall(name, cb /*, ...*/) {
+	}
+	send(id /*, ...*/) {
+		//this.socket_.write(
+	}
+}
+
+Peer.ERROR_BADPROTOCOL = "Bad Protocol";
+Peer.ERROR_BADHOST = "Unknown host";
+Peer.ERROR_BADHANDSHAKE = "Invalid Handshake";
+Peer.ERROR_MALFORMEDURI = "Malformed URI";
+Peer.ERROR_TCPINBROWSER = "TCP invalid in browser";
+Peer.ERROR_LARGEMESSAGE = "Network message too large";
+
+function checkMagic(buffer) {
+	if (buffer.length < 8) return false;
+	let lo_magic = binary.readUInt32LE(buffer,0);
+	let hi_magic = binary.readUInt32LE(buffer,4);
+	return (lo_magic == 0x53640912 && hi_magic == 0x10993400)
+}
+
+module.exports = Peer;
diff --git a/net/js/src/socket.js b/net/js/src/socket.js
deleted file mode 100644
index 48c3725a00527bc8b02cbd4d54b3f92a4e977406..0000000000000000000000000000000000000000
--- a/net/js/src/socket.js
+++ /dev/null
@@ -1,285 +0,0 @@
-const net = require('net');
-const ws = require('ws');
-const urijs = require('uri-js');
-const binary = require('bops');
-const browser = require('detect-browser').detect();
-const isbrowser = !browser || browser.name != "node";
-
-function Socket(uri) {
-	let t = typeof uri;
-	
-	this.handlers_ = {
-		'open': [],
-		'data': [],
-		'error': [],
-		'close': []
-	};
-	
-	this.handshake_ = false;
-	this.lasterr_ = null;
-	this.connected_ = false;
-	this.valid_ = false;
-	this.uuid_ = binary.create(16);
-	
-	if (!isbrowser) {
-		this.buffer_ = new Buffer(0);  // Only in nodejs
-	}
-
-	if (t == "string") {
-		this._fromURI(uri);
-	} else if (t == "object") {
-		this._fromObject(uri);
-	}
-}
-
-Socket.ERROR_BADPROTOCOL = "Bad Protocol";
-Socket.ERROR_BADHOST = "Unknown host";
-Socket.ERROR_BADHANDSHAKE = "Invalid Handshake";
-Socket.ERROR_MALFORMEDURI = "Malformed URI";
-Socket.ERROR_TCPINBROWSER = "TCP invalid in browser";
-Socket.ERROR_LARGEMESSAGE = "Network message too large";
-
-Socket.prototype.error = function(errno) {
-	this.lasterr_ = errno;
-	this.dispatch('error', [errno]);
-}
-
-Socket.prototype.isValid = function() {
-	return this.valid_;
-}
-
-/**
- * Construct the correct kind of socket connection from a URI.
- */
-Socket.prototype._fromURI = function(uri) {
-	let uriobj = urijs.parse(uri);
-	this.uri_ = uri;
-	this.scheme_ = uriobj.scheme;
-	
-	// Could not parse uri so report error
-	if (uriobj.scheme === undefined || uriobj.host === undefined) {
-		this.error(Socket.ERROR_MALFORMEDURI);
-		return;
-	}
-	
-	// Websocket protocol
-	if (this.scheme_ == "ws") {
-		// Detect if in browser or not, choose correct websocket object
-		if (typeof WebSocket == "undefined") {
-			// Nodejs
-			this.socket_ = new ws(uri);
-		} else {
-			// Browser
-			this.socket_ = new WebSocket(uri);
-		}
-		this._initWebsocket();
-	// TCP
-	} else if (this.scheme_ == "tcp") {
-		if (!isbrowser) {
-			this.socket_ = net.connect(uriobj.port, uriobj.host);
-			this._initTCPSocket();
-		} else {
-			this.error(Socket.ERROR_TCPINBROWSER);
-		}
-	// Unrecognised protocol
-	} else {
-		this.error(Socket.ERROR_BADPROTOCOL)
-	}
-}
-
-Socket.prototype._fromObject = function(sock) {
-	this.socket_ = sock;
-	
-	if (typeof WebSocket == "undefined") {
-		if (sock instanceof ws) this.scheme_ = "ws";
-		else if (sock instanceof net.Socket) this.scheme_ = "tcp";
-		else this.scheme_ = null;
-	} else {
-		if (sock instanceof WebSocket) this.scheme_ = "ws";
-		else this.scheme_ = null;
-	}
-	
-	if (this.scheme_ == "ws") this._initWebsocket();
-	else if (this.scheme_ == "tcp") this._initTCPSocket();
-}
-
-/**
- * Setup correct handlers for a websocket connection.
- */
-Socket.prototype._initWebsocket = function() {
-	this.valid_ = true;
-
-	let dataHandler = (data) => {
-		this.processMessage(data);
-	};
-
-	if (this.socket_.addEventHandler) {
-		this.socket_.addEventHandler('message', event => {
-			dataHandler(event.data);
-		});	
-	} else {
-		this.socket_.on('message', dataHandler);
-	}
-	this.socket_.on('open', () => {
-		//this.connected_ = true;
-		//this.dispatch('open', []);
-	});
-	this.socket_.on('error', (err) => {
-		this.connected_ = false;
-		this.valid_ = false;
-		switch (err.errno) {
-		case 'ENOTFOUND'	: this.lasterr_ = Socket.ERROR_BADHOST; break;
-		default				: this.lasterr_ = err.errno;
-		}
-		this.dispatch('error', [this.lasterr_]);
-	});
-	this.socket_.on('close', () => {
-		this.dispatch('close', []);
-	});
-}
-
-function checkMagic(buffer) {
-	if (buffer.length < 8) return false;
-	let lo_magic = binary.readUInt32LE(buffer,0);
-	let hi_magic = binary.readUInt32LE(buffer,4);
-	return (lo_magic == 0x53640912 && hi_magic == 0x10993400)
-}
-
-Socket.prototype.processMessage = function(buffer) {
-	if (!this.handshake_) {
-		// Check handshake
-		if (!checkMagic(buffer)) {
-			this.close();
-			this.error(Socket.ERROR_BADHANDSHAKE);
-			return 0;
-		}
-		
-		binary.copy(buffer, this.uuid_, 0, 8, 16);
-		let proto_size = binary.readUInt32LE(buffer,24);
-		
-		this.handshake_ = true;
-		this.connected_ = true;
-		this.dispatch('open', []);
-		
-		return 28 + proto_size;
-	} else {
-		let size = binary.readUInt32LE(buffer,0);
-		let service = binary.readUInt32LE(buffer,4);
-		
-		console.log("Message: " + service + "(size="+size+")");
-		
-		// Do we have a complete message yet?
-		if (size > 1024*1024*100) {
-			this.error(Socket.ERROR_LARGEMESSAGE);
-			this.close();
-			return 0;
-		} else if (buffer.length-4 >= size) {
-			// Yes, so dispatch
-			this.dispatch(service, [size, binary.subarray(buffer,8)]);
-			return size+4;
-		} else {
-			return 0;
-		}
-	}
-}
-
-/**
- * Setup TCP socket handlers and message buffering mechanism.
- */
-Socket.prototype._initTCPSocket = function() {
-	this.valid_ = true;
-
-	let dataHandler = (data) => {
-		this.buffer_ = Buffer.concat([this.buffer_, data]);
-		
-		while (this.buffer_.length >= 8) {
-			let s = this.processMessage(this.buffer_);
-			if (s == 0) break;
-			this.buffer_ = binary.subarray(this.buffer_,s);
-		}
-	};
-	this.socket_.on('data', dataHandler);
-	this.socket_.on('connect', () => {
-		//this.connected_ = true;
-		//this.dispatch('open', []);
-	});
-	this.socket_.on('error', (err) => {
-		this.connected_ = false;
-		this.valid_ = false;
-		switch (err.errno) {
-		case 'ENOTFOUND'	: this.error(Socket.ERROR_BADHOST); break;
-		default				: this.error(err.errno);
-		}
-	});
-	this.socket_.on('close', () => {
-		this.dispatch('close', []);
-	});
-}
-
-Socket.prototype.isConnected = function() {
-	return this.connected_;
-}
-
-/**
- * Register event handlers.
- */
-Socket.prototype.on = function(name, f) {
-	if (typeof name == "string") {
-		if (this.handlers_.hasOwnProperty(name)) {
-			this.handlers_[name].push(f);
-		} else {
-			console.error("Unrecognised handler: ", name);
-		}
-		
-		if (name == "error" && this.lasterr_ != null) {
-			f(this.lasterr_);
-		}
-	} else if (typeof name == "number") {
-		if (this.handlers_[name] === undefined) this.handlers_[name] = [];
-		this.handlers_[name].push(f);
-	} else {
-		console.error("Invalid handler: ", name);
-	}
-}
-
-Socket.prototype.dispatch = function(h, args) {
-	if (this.handlers_.hasOwnProperty(h)) {
-		let hs = this.handlers_[h];
-		for (var i=0; i<hs.length; i++) {
-			hs[i].apply(this, args);
-		}
-		return true;
-	} else {
-		return false;
-	}
-}
-
-Socket.prototype.close = function() {
-	if (this.socket_ == null) return;
-	
-	if (this.scheme_ == "ws") {
-		this.socket_.close();
-	} else {
-		this.socket_.destroy();
-	}
-	this.socket_ = null;
-}
-
-Socket.prototype._socket = function() {
-	return this.socket_;
-}
-
-Socket.prototype.getURI = function() {
-	return this.uri_;
-}
-
-Socket.prototype.asyncCall = function(name, cb /*, ...*/) {
-
-}
-
-Socket.prototype.send = function(id /*, ...*/) {
-	//this.socket_.write(
-}
-
-module.exports = Socket;
-
diff --git a/net/js/src/universe.js b/net/js/src/universe.js
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/net/js/test/socket_unit.js b/net/js/test/peer_unit.js
similarity index 95%
rename from net/js/test/socket_unit.js
rename to net/js/test/peer_unit.js
index c5fd8fc7ea32ecc73a09f7a4f5ccf763883d096d..6e454af5cab968f373a3903b49a792ab3a7567cd 100644
--- a/net/js/test/socket_unit.js
+++ b/net/js/test/peer_unit.js
@@ -1,10 +1,10 @@
-const Socket = require('../src/socket.js');
+const Peer = require('../src/peer.js');
 const assert = require('chai').assert;
 const net = require('net');
-const binary = require('bops');
+//const binary = require('bops');
 const WebSocket = require('ws');
 
-describe("Socket()", function() {
+describe("Peer()", function() {
 	let server;
 	let wss;
 	let dobadhandshake = false;
@@ -34,7 +34,7 @@ describe("Socket()", function() {
 	
 	context("with a valid connection uri and handshake", () => {
 		it("make a tcp connection", function(done) {
-			let sock = new Socket("tcp://localhost:9000");
+			let sock = new Peer("tcp://localhost:9000");
 			sock.on('open', () => {
 				assert.isOk(sock.isConnected());
 				sock.close();
diff --git a/renderer/cpp/include/ftl/display.hpp b/renderer/cpp/include/ftl/display.hpp
index 1daad059044aa856d7af3fb0b30be9e945aebedb..c6d63ca4f8685c1c0cfa751c2fb87b5b10fb412e 100644
--- a/renderer/cpp/include/ftl/display.hpp
+++ b/renderer/cpp/include/ftl/display.hpp
@@ -18,7 +18,7 @@ namespace ftl {
  */
 class Display {
 	public:
-	Display(nlohmann::json &config);
+	explicit Display(nlohmann::json &config);
 	~Display();
 	
 	void setCalibration(const cv::Mat &q) { q_ = q; }
diff --git a/vision/src/calibrate.cpp b/vision/src/calibrate.cpp
index 092f5015d9371d44e3ab9163c14ed5d3921e4563..7151a01bb2dfab9fa7b480612c3839f344e666a7 100644
--- a/vision/src/calibrate.cpp
+++ b/vision/src/calibrate.cpp
@@ -377,11 +377,6 @@ bool Calibrate::_recalibrate(vector<vector<Point2f>> *imagePoints,
 
     float grid_width = settings_.squareSize * (settings_.boardSize.width - 1);
     bool release_object = false;
-
-    // vector<vector<Point2f> > imagePoints;
-    // Mat cameraMatrix, distCoeffs;
-    // Size imageSize;
-    int mode = CAPTURING;
     double prevTimestamp = 0.0;
     const Scalar RED(0, 0, 255), GREEN(0, 255, 0);