diff --git a/applications/gui/src/camera.cpp b/applications/gui/src/camera.cpp
index 8686a608097805e60f715b2e16549c658812361d..0c3b587789b8abf744f3591d02abf00a4c1a8a2c 100644
--- a/applications/gui/src/camera.cpp
+++ b/applications/gui/src/camera.cpp
@@ -834,12 +834,17 @@ void ftl::gui::Camera::snapshot(const std::string &filename) {
 	do_snapshot_ = true;
 }
 
-void ftl::gui::Camera::startVideoRecording(const std::string &filename) {
+void ftl::gui::Camera::startVideoRecording(const std::string &filename, const std::string &uri) {
 	if (!record_stream_) {
-		record_stream_ = ftl::create<ftl::stream::File>(screen_->root(), "video2d");
-		record_stream_->setMode(ftl::stream::File::Mode::Write);
+		file_stream_ = ftl::create<ftl::stream::File>(screen_->root(), "video2d");
+		file_stream_->setMode(ftl::stream::File::Mode::Write);
+		net_stream_ = ftl::create<ftl::stream::Net>(screen_->root(), "liveStream", screen_->net());
+
+		record_stream_ = ftl::create<ftl::stream::Broadcast>(screen_->root(), "recordStream");
+		//record_stream_->add(file_stream_);
+		//record_stream_->add(net_stream_);
+
 		record_sender_ = ftl::create<ftl::stream::Sender>(screen_->root(), "videoEncode");
-		record_sender_->setStream(record_stream_);
 		record_sender_->value("codec", 2);  // Default H264
 		record_sender_->set("iframes", 50);  // Add iframes by default
 		record_sender_->value("stereo", true);  // If both channels, then default to stereo
@@ -847,8 +852,22 @@ void ftl::gui::Camera::startVideoRecording(const std::string &filename) {
 
 	if (record_stream_->active()) return;
 
-	record_stream_->set("filename", filename);
-	record_stream_->begin();
+	record_stream_->clear();
+
+	if (filename.size() > 0) {
+		file_stream_->set("filename", filename);
+		record_stream_->add(file_stream_);
+	}
+
+	if (uri.size() > 0) {
+		net_stream_->set("uri", uri);
+		record_stream_->add(net_stream_);
+	}
+
+	record_sender_->setStream(record_stream_);
+
+	LOG(INFO) << "About to record";
+	if (record_stream_->begin()) LOG(INFO) << "Recording started...";
 }
 
 void ftl::gui::Camera::stopVideoRecording() {
diff --git a/applications/gui/src/camera.hpp b/applications/gui/src/camera.hpp
index cb34ff0f7a3046719c438e0e2c81e752090c2294..730ba380dbe8586be61ecf4ceef4ad82ff5588fc 100644
--- a/applications/gui/src/camera.hpp
+++ b/applications/gui/src/camera.hpp
@@ -8,6 +8,7 @@
 #include "gltexture.hpp"
 
 #include <ftl/streams/filestream.hpp>
+#include <ftl/streams/netstream.hpp>
 #include <ftl/streams/sender.hpp>
 #include <ftl/codecs/faces.hpp>
 
@@ -96,7 +97,7 @@ class Camera {
 
 	void snapshot(const std::string &filename);
 
-	void startVideoRecording(const std::string &filename);
+	void startVideoRecording(const std::string &filename, const std::string &uri);
 
 	void stopVideoRecording();
 
@@ -173,7 +174,9 @@ class Camera {
 	ftl::operators::Graph *post_pipe_;
 	ftl::rgbd::Frame frame_;
 	ftl::rgbd::FrameState state_;
-	ftl::stream::File *record_stream_;
+	ftl::stream::File *file_stream_;
+	ftl::stream::Net *net_stream_;
+	ftl::stream::Broadcast *record_stream_;
 	ftl::stream::Sender *record_sender_;
 
 	std::string name_;
diff --git a/applications/gui/src/media_panel.cpp b/applications/gui/src/media_panel.cpp
index 182324deb9d628f716bac0d515a41d2c9c30c0ed..2801e2dc896d0861aa0b890cffa2e11405ef3e44 100644
--- a/applications/gui/src/media_panel.cpp
+++ b/applications/gui/src/media_panel.cpp
@@ -54,6 +54,12 @@ MediaPanel::MediaPanel(ftl::gui::Screen *screen, ftl::gui::SourceWindow *sourceW
 		recordbutton_->setTextColor(nanogui::Color(1.0f,0.1f,0.1f,1.0f));
 		recordbutton_->setPushed(false);
 	});
+	itembutton = new Button(recordpopup, "Virtual camera Live");
+	itembutton->setCallback([this]() {
+		_startRecording(RecordMode::Live2D);
+		recordbutton_->setTextColor(nanogui::Color(1.0f,0.1f,0.1f,1.0f));
+		recordbutton_->setPushed(false);
+	});
 	itembutton = new Button(recordpopup, "3D scene snapshot (.ftl)");
 	itembutton->setCallback([this]() {
 		_startRecording(RecordMode::Snapshot3D);
@@ -208,6 +214,7 @@ void MediaPanel::_startRecording(MediaPanel::RecordMode mode) {
 	case RecordMode::Snapshot3D		:
 	case RecordMode::Video3D		: filename += ".ftl"; break;
 	case RecordMode::Video2D		: filename += ".ftl"; break;
+	case RecordMode::Live2D			: break;
 	default: return;
 	}
 
@@ -218,14 +225,16 @@ void MediaPanel::_startRecording(MediaPanel::RecordMode mode) {
 		screen_->activeCamera()->snapshot(filename);
 	} else if (mode == RecordMode::Video2D) {
 		record_mode_ = mode;
-		screen_->activeCamera()->startVideoRecording(filename);
+		screen_->activeCamera()->startVideoRecording(filename, "");
+	} else if (mode == RecordMode::Live2D) {
+		screen_->activeCamera()->startVideoRecording("", "ftl://live.utu.fi");
 	}
 }
 
 void MediaPanel::_stopRecording() {
 	if (record_mode_ == RecordMode::Video3D) {
 		sourceWindow_->stopRecordingVideo();
-	} else if (record_mode_ == RecordMode::Video2D) {
+	} else if (record_mode_ == RecordMode::Video2D || record_mode_ == RecordMode::Live2D) {
 		screen_->activeCamera()->stopVideoRecording();
 	}
 	record_mode_ = RecordMode::None;
diff --git a/applications/gui/src/media_panel.hpp b/applications/gui/src/media_panel.hpp
index 94700cad073c00fec51bfd5135476938d510de06..f615be1f6079808fcce71500840f6fc8c6652ac9 100644
--- a/applications/gui/src/media_panel.hpp
+++ b/applications/gui/src/media_panel.hpp
@@ -56,7 +56,9 @@ class MediaPanel : public nanogui::Window {
 		Snapshot2D,
 		Snapshot3D,
 		Video2D,
-		Video3D
+		Video3D,
+		Live2D,
+		Live3D
 	};
 	RecordMode record_mode_;
 
diff --git a/components/net/cpp/src/ws_internal.cpp b/components/net/cpp/src/ws_internal.cpp
index c0cf9630bdb21e2bb23810335d6d44fa5391047a..db9393d2a5350f07c69b5ce514f59f6ccdfe68b5 100644
--- a/components/net/cpp/src/ws_internal.cpp
+++ b/components/net/cpp/src/ws_internal.cpp
@@ -203,7 +203,7 @@ bool ftl::net::ws_connect(SOCKET sockfd, const URI &uri) {
 	http += "\r\n";
 	int rc = ::send(sockfd, http.c_str(), (int)http.length(), 0);
 	if (rc != (int)http.length()) {
-		LOG(ERROR) << "Could not send Websocket http request...";
+		LOG(ERROR) << "Could not send Websocket http request... (" << rc << ", " << errno << ")";
 		std::cout << http;
 		return false;
 	}
diff --git a/components/streams/include/ftl/streams/stream.hpp b/components/streams/include/ftl/streams/stream.hpp
index 75aa9ab806baaf8ab7cf863188c3119856959a79..4baa178a97c7abd8fec3d7d3a2777dd81751cac4 100644
--- a/components/streams/include/ftl/streams/stream.hpp
+++ b/components/streams/include/ftl/streams/stream.hpp
@@ -155,6 +155,8 @@ class Broadcast : public Stream {
 	virtual ~Broadcast();
 
 	void add(Stream *);
+	void remove(Stream *);
+	void clear();
 
 	bool onPacket(const std::function<void(const ftl::codecs::StreamPacket &, const ftl::codecs::Packet &)> &) override;
 
diff --git a/components/streams/src/netstream.cpp b/components/streams/src/netstream.cpp
index 0cba3f4dc533a3cfd71b116e0da9709718fbe3da..0484ed2e70ac13a4289d5b8af93c9f05bf25b0a8 100644
--- a/components/streams/src/netstream.cpp
+++ b/components/streams/src/netstream.cpp
@@ -231,6 +231,9 @@ bool Net::begin() {
 		LOG(INFO) << "Hosting stream: " << uri_;
 		// TODO: Register URI as available.
 		host_ = true;
+
+		net_->broadcast("add_stream", uri_);
+
 		return true;
 	} else {
 		//LOG(INFO) << "Net cfg: " << net_->call<std::string>(*p, "get_cfg", uri_);
@@ -275,7 +278,7 @@ bool Net::_sendRequest(Channel c, uint8_t frameset, uint8_t frames, uint8_t coun
 	StreamPacket spkt = {
 		4,
 		ftl::timer::get_time(),
-		frameset,
+		0,
 		frames,
 		c,
 		0,
diff --git a/components/streams/src/stream.cpp b/components/streams/src/stream.cpp
index 22d8768d473074b26a62d12d029349562180c287..43c53eae64c7ace22ef9026d2a4148c8889f5628 100644
--- a/components/streams/src/stream.cpp
+++ b/components/streams/src/stream.cpp
@@ -180,12 +180,29 @@ void Broadcast::add(Stream *s) {
 	UNIQUE_LOCK(mutex_,lk);
 
 	streams_.push_back(s);
-	s->onPacket([this](const ftl::codecs::StreamPacket &spkt, const ftl::codecs::Packet &pkt) {
+	s->onPacket([this,s](const ftl::codecs::StreamPacket &spkt, const ftl::codecs::Packet &pkt) {
+		LOG(INFO) << "BCAST Request: " << (int)spkt.streamID << " " << (int)spkt.channel << " " << spkt.timestamp;
 		SHARED_LOCK(mutex_, lk);
+		if (spkt.frameSetID() < 255) available(spkt.frameSetID()) += spkt.channel;
 		if (cb_) cb_(spkt, pkt);
+		if (spkt.streamID < 255) s->select(spkt.streamID, selected(spkt.streamID));
 	});
 }
 
+void Broadcast::remove(Stream *s) {
+	UNIQUE_LOCK(mutex_,lk);
+	s->onPacket(nullptr);
+	streams_.remove(s);
+}
+
+void Broadcast::clear() {
+	UNIQUE_LOCK(mutex_,lk);
+	for (auto s : streams_) {
+		s->onPacket(nullptr);
+	}
+	streams_.clear();
+}
+
 bool Broadcast::onPacket(const std::function<void(const ftl::codecs::StreamPacket &, const ftl::codecs::Packet &)> &cb) {
 	UNIQUE_LOCK(mutex_,lk);
 	cb_ = cb;
@@ -194,9 +211,11 @@ bool Broadcast::onPacket(const std::function<void(const ftl::codecs::StreamPacke
 
 bool Broadcast::post(const ftl::codecs::StreamPacket &spkt, const ftl::codecs::Packet &pkt) {
 	SHARED_LOCK(mutex_, lk);
-	
+	if (spkt.frameSetID() < 255) available(spkt.frameSetID()) += spkt.channel;
+
 	bool status = true;
 	for (auto s : streams_) {
+		//s->select(spkt.frameSetID(), selected(spkt.frameSetID()));
 		status = status && s->post(spkt, pkt);
 	}
 	return status;
@@ -219,6 +238,7 @@ bool Broadcast::end() {
 }
 
 bool Broadcast::active() {
+	if (streams_.size() == 0) return false;
 	bool r = true;
 	for (auto &s : streams_) {
 		r = r && s->active();
diff --git a/web-service/public/js/bundle.js b/web-service/public/js/bundle.js
index df7eec769a1efa2850c96cdede4cd698835a6c56..65d67c028e8ae6ee0975fe14e9bc9326a94de0e1 100644
--- a/web-service/public/js/bundle.js
+++ b/web-service/public/js/bundle.js
@@ -1,4 +1,5 @@
 (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
+(function (Buffer){
 const Peer = require('../../server/src/peer')
 const VideoConverter = require('./lib/dist/video-converter');
 
@@ -108,8 +109,8 @@ createCard = (url, viewers) => {
 
 createPeer = () => {
     // FOR PRODUCTION
-    const ws = new WebSocket("ws://" + location.host + ":" + (location.port == "" ? "80" : location.port) + location.pathname);
-    // const ws = new WebSocket("ws://localhost:8080")
+    //const ws = new WebSocket("ws://" + location.host + ":" + (location.port == "" ? "80" : location.port) + location.pathname);
+    const ws = new WebSocket("ws://localhost:8080")
     ws.binaryType = "arraybuffer";
     peer = new Peer(ws)
 }
@@ -121,20 +122,45 @@ webSocketTest = () => {
 
 connectToStream = () => {
     const element = document.getElementById('ftlab-stream-video');
-    const converter = new VideoConverter.default(element, 20, 6);
+    let converter = null;
+
+    let rxcount = 0;
+    let ts = 0;
+    let dts = 0;
 
     peer.bind(current_data.uri, (latency, streampckg, pckg) => {
         if(pckg[0] === 2){
-            function decode(value){
-                converter.appendRawData(value);
+            rxcount++;
+            if (rxcount >= 25) {
+                rxcount = 0;
+                peer.send(current_data.uri, 0, [1,0,255,0],[255,7,35,0,0,Buffer.alloc(0)]);
+                //peer.send(current_data.uri, 0, [255,7,35,0,0,Buffer.alloc(0)], [1,0,255,0]);
+            }
+
+            if (converter) {
+                function decode(value){
+                    converter.appendRawData(value);
+                }
+                decode(pckg[5]);
+                converter.play();
+            } else {
+                if (ts > 0) {
+                    dts = streampckg[0] - ts;
+                    console.log("Framerate = ", 1000/dts);
+                    converter = new VideoConverter.default(element, 1000/dts, 6);
+                }
+                ts = streampckg[0];
             }
-            decode(pckg[5]);
-            converter.play();
         };
     })
 
     // Start the transaction
-    peer.send("get_stream", (current_data.uri, 30, 0, current_data.uri));
+    //peer.send("get_stream", (current_data.uri, 30, 0, current_data.uri));
+
+    peer.rpc("find_stream", (res) => {
+        peer.send(current_data.uri, 0, [1,0,255,0],[255,7,35,0,0,Buffer.alloc(0)]);
+        //peer.send(current_data.uri, [255,7,35,0,0,Buffer.alloc(0)], [1,0,255,0]);
+    }, current_data.uri);
 }
 
 closeStream = () => {
@@ -194,7 +220,8 @@ saveConfigs = async () => {
     });
     const content = await rawResp.json();
 }
-},{"../../server/src/peer":36,"./lib/dist/video-converter":9}],2:[function(require,module,exports){
+}).call(this,require("buffer").Buffer)
+},{"../../server/src/peer":36,"./lib/dist/video-converter":9,"buffer":44}],2:[function(require,module,exports){
 "use strict";
 Object.defineProperty(exports, "__esModule", { value: true });
 var bit_stream_1 = require("./util/bit-stream");
@@ -1914,7 +1941,7 @@ BufferList.prototype._match = function(offset, search) {
 
 module.exports = BufferList
 
-},{"readable-stream":28,"safe-buffer":29,"util":53}],11:[function(require,module,exports){
+},{"readable-stream":27,"safe-buffer":28,"util":53}],11:[function(require,module,exports){
 (function (Buffer){
 // Copyright Joyent, Inc. and other Node contributors.
 //
@@ -2144,7 +2171,7 @@ function msgpack (options) {
 
 module.exports = msgpack
 
-},{"./lib/decoder":15,"./lib/encoder":16,"./lib/streams":17,"assert":38,"bl":10,"safe-buffer":29}],15:[function(require,module,exports){
+},{"./lib/decoder":15,"./lib/encoder":16,"./lib/streams":17,"assert":38,"bl":10,"safe-buffer":28}],15:[function(require,module,exports){
 'use strict'
 
 var bl = require('bl')
@@ -2927,7 +2954,7 @@ function encodeFloat (obj, forceFloat64) {
   return buf
 }
 
-},{"bl":10,"safe-buffer":29}],17:[function(require,module,exports){
+},{"bl":10,"safe-buffer":28}],17:[function(require,module,exports){
 'use strict'
 
 var Transform = require('readable-stream').Transform
@@ -3019,7 +3046,7 @@ Decoder.prototype._transform = function (buf, enc, done) {
 module.exports.decoder = Decoder
 module.exports.encoder = Encoder
 
-},{"bl":10,"inherits":12,"readable-stream":28}],18:[function(require,module,exports){
+},{"bl":10,"inherits":12,"readable-stream":27}],18:[function(require,module,exports){
 (function (process){
 'use strict';
 
@@ -3114,7 +3141,7 @@ var objectKeys = Object.keys || function (obj) {
 module.exports = Duplex;
 
 /*<replacement>*/
-var util = require('core-util-is');
+var util = Object.create(require('core-util-is'));
 util.inherits = require('inherits');
 /*</replacement>*/
 
@@ -3233,7 +3260,7 @@ module.exports = PassThrough;
 var Transform = require('./_stream_transform');
 
 /*<replacement>*/
-var util = require('core-util-is');
+var util = Object.create(require('core-util-is'));
 util.inherits = require('inherits');
 /*</replacement>*/
 
@@ -3316,7 +3343,7 @@ function _isUint8Array(obj) {
 /*</replacement>*/
 
 /*<replacement>*/
-var util = require('core-util-is');
+var util = Object.create(require('core-util-is'));
 util.inherits = require('inherits');
 /*</replacement>*/
 
@@ -4270,7 +4297,7 @@ function indexOf(xs, x) {
   return -1;
 }
 }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
-},{"./_stream_duplex":19,"./internal/streams/BufferList":24,"./internal/streams/destroy":25,"./internal/streams/stream":26,"_process":49,"core-util-is":11,"events":45,"inherits":12,"isarray":13,"process-nextick-args":18,"safe-buffer":29,"string_decoder/":27,"util":43}],22:[function(require,module,exports){
+},{"./_stream_duplex":19,"./internal/streams/BufferList":24,"./internal/streams/destroy":25,"./internal/streams/stream":26,"_process":49,"core-util-is":11,"events":45,"inherits":12,"isarray":13,"process-nextick-args":18,"safe-buffer":28,"string_decoder/":29,"util":43}],22:[function(require,module,exports){
 // Copyright Joyent, Inc. and other Node contributors.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a
@@ -4341,7 +4368,7 @@ module.exports = Transform;
 var Duplex = require('./_stream_duplex');
 
 /*<replacement>*/
-var util = require('core-util-is');
+var util = Object.create(require('core-util-is'));
 util.inherits = require('inherits');
 /*</replacement>*/
 
@@ -4553,7 +4580,7 @@ var Duplex;
 Writable.WritableState = WritableState;
 
 /*<replacement>*/
-var util = require('core-util-is');
+var util = Object.create(require('core-util-is'));
 util.inherits = require('inherits');
 /*</replacement>*/
 
@@ -5175,7 +5202,7 @@ Writable.prototype._destroy = function (err, cb) {
   cb(err);
 };
 }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("timers").setImmediate)
-},{"./_stream_duplex":19,"./internal/streams/destroy":25,"./internal/streams/stream":26,"_process":49,"core-util-is":11,"inherits":12,"process-nextick-args":18,"safe-buffer":29,"timers":50,"util-deprecate":30}],24:[function(require,module,exports){
+},{"./_stream_duplex":19,"./internal/streams/destroy":25,"./internal/streams/stream":26,"_process":49,"core-util-is":11,"inherits":12,"process-nextick-args":18,"safe-buffer":28,"timers":50,"util-deprecate":30}],24:[function(require,module,exports){
 'use strict';
 
 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
@@ -5255,7 +5282,7 @@ if (util && util.inspect && util.inspect.custom) {
     return this.constructor.name + ' ' + obj;
   };
 }
-},{"safe-buffer":29,"util":43}],25:[function(require,module,exports){
+},{"safe-buffer":28,"util":43}],25:[function(require,module,exports){
 'use strict';
 
 /*<replacement>*/
@@ -5334,6 +5361,79 @@ module.exports = {
 module.exports = require('events').EventEmitter;
 
 },{"events":45}],27:[function(require,module,exports){
+exports = module.exports = require('./lib/_stream_readable.js');
+exports.Stream = exports;
+exports.Readable = exports;
+exports.Writable = require('./lib/_stream_writable.js');
+exports.Duplex = require('./lib/_stream_duplex.js');
+exports.Transform = require('./lib/_stream_transform.js');
+exports.PassThrough = require('./lib/_stream_passthrough.js');
+
+},{"./lib/_stream_duplex.js":19,"./lib/_stream_passthrough.js":20,"./lib/_stream_readable.js":21,"./lib/_stream_transform.js":22,"./lib/_stream_writable.js":23}],28:[function(require,module,exports){
+/* eslint-disable node/no-deprecated-api */
+var buffer = require('buffer')
+var Buffer = buffer.Buffer
+
+// alternative to using Object.keys for old browsers
+function copyProps (src, dst) {
+  for (var key in src) {
+    dst[key] = src[key]
+  }
+}
+if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) {
+  module.exports = buffer
+} else {
+  // Copy properties from require('buffer')
+  copyProps(buffer, exports)
+  exports.Buffer = SafeBuffer
+}
+
+function SafeBuffer (arg, encodingOrOffset, length) {
+  return Buffer(arg, encodingOrOffset, length)
+}
+
+// Copy static methods from Buffer
+copyProps(Buffer, SafeBuffer)
+
+SafeBuffer.from = function (arg, encodingOrOffset, length) {
+  if (typeof arg === 'number') {
+    throw new TypeError('Argument must not be a number')
+  }
+  return Buffer(arg, encodingOrOffset, length)
+}
+
+SafeBuffer.alloc = function (size, fill, encoding) {
+  if (typeof size !== 'number') {
+    throw new TypeError('Argument must be a number')
+  }
+  var buf = Buffer(size)
+  if (fill !== undefined) {
+    if (typeof encoding === 'string') {
+      buf.fill(fill, encoding)
+    } else {
+      buf.fill(fill)
+    }
+  } else {
+    buf.fill(0)
+  }
+  return buf
+}
+
+SafeBuffer.allocUnsafe = function (size) {
+  if (typeof size !== 'number') {
+    throw new TypeError('Argument must be a number')
+  }
+  return Buffer(size)
+}
+
+SafeBuffer.allocUnsafeSlow = function (size) {
+  if (typeof size !== 'number') {
+    throw new TypeError('Argument must be a number')
+  }
+  return buffer.SlowBuffer(size)
+}
+
+},{"buffer":44}],29:[function(require,module,exports){
 // Copyright Joyent, Inc. and other Node contributors.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a
@@ -5630,80 +5730,7 @@ function simpleWrite(buf) {
 function simpleEnd(buf) {
   return buf && buf.length ? this.write(buf) : '';
 }
-},{"safe-buffer":29}],28:[function(require,module,exports){
-exports = module.exports = require('./lib/_stream_readable.js');
-exports.Stream = exports;
-exports.Readable = exports;
-exports.Writable = require('./lib/_stream_writable.js');
-exports.Duplex = require('./lib/_stream_duplex.js');
-exports.Transform = require('./lib/_stream_transform.js');
-exports.PassThrough = require('./lib/_stream_passthrough.js');
-
-},{"./lib/_stream_duplex.js":19,"./lib/_stream_passthrough.js":20,"./lib/_stream_readable.js":21,"./lib/_stream_transform.js":22,"./lib/_stream_writable.js":23}],29:[function(require,module,exports){
-/* eslint-disable node/no-deprecated-api */
-var buffer = require('buffer')
-var Buffer = buffer.Buffer
-
-// alternative to using Object.keys for old browsers
-function copyProps (src, dst) {
-  for (var key in src) {
-    dst[key] = src[key]
-  }
-}
-if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) {
-  module.exports = buffer
-} else {
-  // Copy properties from require('buffer')
-  copyProps(buffer, exports)
-  exports.Buffer = SafeBuffer
-}
-
-function SafeBuffer (arg, encodingOrOffset, length) {
-  return Buffer(arg, encodingOrOffset, length)
-}
-
-// Copy static methods from Buffer
-copyProps(Buffer, SafeBuffer)
-
-SafeBuffer.from = function (arg, encodingOrOffset, length) {
-  if (typeof arg === 'number') {
-    throw new TypeError('Argument must not be a number')
-  }
-  return Buffer(arg, encodingOrOffset, length)
-}
-
-SafeBuffer.alloc = function (size, fill, encoding) {
-  if (typeof size !== 'number') {
-    throw new TypeError('Argument must be a number')
-  }
-  var buf = Buffer(size)
-  if (fill !== undefined) {
-    if (typeof encoding === 'string') {
-      buf.fill(fill, encoding)
-    } else {
-      buf.fill(fill)
-    }
-  } else {
-    buf.fill(0)
-  }
-  return buf
-}
-
-SafeBuffer.allocUnsafe = function (size) {
-  if (typeof size !== 'number') {
-    throw new TypeError('Argument must be a number')
-  }
-  return Buffer(size)
-}
-
-SafeBuffer.allocUnsafeSlow = function (size) {
-  if (typeof size !== 'number') {
-    throw new TypeError('Argument must be a number')
-  }
-  return buffer.SlowBuffer(size)
-}
-
-},{"buffer":44}],30:[function(require,module,exports){
+},{"safe-buffer":28}],30:[function(require,module,exports){
 (function (global){
 
 /**
@@ -5798,14 +5825,16 @@ function bytesToUuid(buf, offset) {
   var i = offset || 0;
   var bth = byteToHex;
   // join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4
-  return ([bth[buf[i++]], bth[buf[i++]], 
-	bth[buf[i++]], bth[buf[i++]], '-',
-	bth[buf[i++]], bth[buf[i++]], '-',
-	bth[buf[i++]], bth[buf[i++]], '-',
-	bth[buf[i++]], bth[buf[i++]], '-',
-	bth[buf[i++]], bth[buf[i++]],
-	bth[buf[i++]], bth[buf[i++]],
-	bth[buf[i++]], bth[buf[i++]]]).join('');
+  return ([
+    bth[buf[i++]], bth[buf[i++]],
+    bth[buf[i++]], bth[buf[i++]], '-',
+    bth[buf[i++]], bth[buf[i++]], '-',
+    bth[buf[i++]], bth[buf[i++]], '-',
+    bth[buf[i++]], bth[buf[i++]], '-',
+    bth[buf[i++]], bth[buf[i++]],
+    bth[buf[i++]], bth[buf[i++]],
+    bth[buf[i++]], bth[buf[i++]]
+  ]).join('');
 }
 
 module.exports = bytesToUuid;
@@ -5862,7 +5891,7 @@ var _clockseq;
 var _lastMSecs = 0;
 var _lastNSecs = 0;
 
-// See https://github.com/broofa/node-uuid for API details
+// See https://github.com/uuidjs/uuid for API details
 function v1(options, buf, offset) {
   var i = buf && offset || 0;
   var b = buf || [];
@@ -6032,7 +6061,6 @@ function Peer(ws) {
 	this.master = false;
 
 	let message = (raw) => {
-		// console.log(raw)
 		//Gets right data for client
 		if(this.sock.on === undefined){
 			raw = raw.data;
@@ -6177,6 +6205,10 @@ Peer.prototype.bind = function(name, f) {
 	}
 }
 
+Peer.prototype.isBound = function(name) {
+	return this.bindings.hasOwnProperty(name) || this.proxies.hasOwnProperty(name);
+}
+
 /**
  * Allow an RPC call to pass through to another machine with minimal local
  * processing.
diff --git a/web-service/public/js/index.js b/web-service/public/js/index.js
index 8ea42a108eefed4a1bcf9a3bddddb2a6c4fa628b..b48e75333e3cc10afc4830bc2349a8f0ca65e74c 100644
--- a/web-service/public/js/index.js
+++ b/web-service/public/js/index.js
@@ -107,8 +107,8 @@ createCard = (url, viewers) => {
 
 createPeer = () => {
     // FOR PRODUCTION
-    const ws = new WebSocket("ws://" + location.host + ":" + (location.port == "" ? "80" : location.port) + location.pathname);
-    // const ws = new WebSocket("ws://localhost:8080")
+    //const ws = new WebSocket("ws://" + location.host + ":" + (location.port == "" ? "80" : location.port) + location.pathname);
+    const ws = new WebSocket("ws://localhost:8080")
     ws.binaryType = "arraybuffer";
     peer = new Peer(ws)
 }
@@ -120,20 +120,45 @@ webSocketTest = () => {
 
 connectToStream = () => {
     const element = document.getElementById('ftlab-stream-video');
-    const converter = new VideoConverter.default(element, 20, 6);
+    let converter = null;
+
+    let rxcount = 0;
+    let ts = 0;
+    let dts = 0;
 
     peer.bind(current_data.uri, (latency, streampckg, pckg) => {
         if(pckg[0] === 2){
-            function decode(value){
-                converter.appendRawData(value);
+            rxcount++;
+            if (rxcount >= 25) {
+                rxcount = 0;
+                peer.send(current_data.uri, 0, [1,0,255,0],[255,7,35,0,0,Buffer.alloc(0)]);
+                //peer.send(current_data.uri, 0, [255,7,35,0,0,Buffer.alloc(0)], [1,0,255,0]);
+            }
+
+            if (converter) {
+                function decode(value){
+                    converter.appendRawData(value);
+                }
+                decode(pckg[5]);
+                converter.play();
+            } else {
+                if (ts > 0) {
+                    dts = streampckg[0] - ts;
+                    console.log("Framerate = ", 1000/dts);
+                    converter = new VideoConverter.default(element, 1000/dts, 6);
+                }
+                ts = streampckg[0];
             }
-            decode(pckg[5]);
-            converter.play();
         };
     })
 
     // Start the transaction
-    peer.send("get_stream", (current_data.uri, 30, 0, current_data.uri));
+    //peer.send("get_stream", (current_data.uri, 30, 0, current_data.uri));
+
+    peer.rpc("find_stream", (res) => {
+        peer.send(current_data.uri, 0, [1,0,255,0],[255,7,35,0,0,Buffer.alloc(0)]);
+        //peer.send(current_data.uri, [255,7,35,0,0,Buffer.alloc(0)], [1,0,255,0]);
+    }, current_data.uri);
 }
 
 closeStream = () => {
diff --git a/web-service/server/src/index.js b/web-service/server/src/index.js
index 339be2b6862219f605c50fa6200709db1fbf76f9..37a0683d2015eef0cdb256f16f5fbcea95dacb42 100644
--- a/web-service/server/src/index.js
+++ b/web-service/server/src/index.js
@@ -114,7 +114,7 @@ RGBDStream.prototype.subscribe = function() {
 	//console.log("Subscribe to ", this.uri);
 	// TODO: Don't hard code 9 here, instead use 9 for thumbnails and 0 for
 	// the video...
-	this.peer.send("get_stream", this.uri, 10, 0, [Peer.uuid], this.uri);
+	//this.peer.send("get_stream", this.uri, 10, 0, [Peer.uuid], this.uri);
 }
 
 RGBDStream.prototype.pushFrames = function(latency, spacket, packet) {
@@ -158,7 +158,7 @@ app.get('/streams', (req, res) => {
 app.get('/stream/rgb', (req, res) => {
 	let uri = req.query.uri;
 	if (uri_data.hasOwnProperty(uri)) {
-		uri_data[uri].peer.send("get_stream", uri, 3, 9, [Peer.uuid], uri);
+		//uri_data[uri].peer.send("get_stream", uri, 3, 9, [Peer.uuid], uri);
 		res.writeHead(200, {'Content-Type': 'image/jpeg'});
 		res.end(uri_data[uri].rgb);
 	}
@@ -298,8 +298,9 @@ app.ws('/', (ws, req) => {
 		if (puris) {
 			for (let i=0; i<puris.length; i++) {
 				console.log("Removing stream: ", puris[i]);
-				//delete uri_to_peer[puris[i]];
+				delete uri_to_peer[puris[i]];
 				delete uri_data[puris[i]];
+				//p.unbind(pu)
 			}
 			delete peer_uris[peer.string_id];
 		}
@@ -331,11 +332,15 @@ app.ws('/', (ws, req) => {
 			if (!p.isBound(uri)) {
 				console.log("Adding local stream binding");
 				p.bind(uri, (ttimeoff, spkt, pkt) => {
-					console.log("STREAM: ", spkt);
+					console.log("STREAM: ", ttimeoff, spkt, pkt);
 					let speer = uri_to_peer[parsedURI];
 					if (speer) {
+						try {
 						uri_data[parsedURI].addClient(p);
 						speer.send(parsedURI, ttimeoff, spkt, pkt);
+						} catch(e) {
+							console.error("EXCEPTION", e);
+						}
 					} else if (speer) console.log("Stream response");
 					else console.error("No stream peer");
 				});
@@ -393,7 +398,7 @@ app.ws('/', (ws, req) => {
 	});
 
 	// Request from frames from a source
-	p.bind("get_stream", (uri, N, rate, /*pid,*/ dest) => {
+	/*p.bind("get_stream", (uri, N, rate, dest) => {
 		console.log(uri)
 		const parsedURI = stringSplitter(uri);
 		if(uri_data[uri]){
@@ -409,7 +414,7 @@ app.ws('/', (ws, req) => {
 			console.log("Couldn't get stream for ", uri)
 			return "{}";
 		}
-	});
+	});*/
 
 	/**
 	 * Get JSON values for stream configuration