diff --git a/Source/DasherVR/Private/SDasherWidget.cpp b/Source/DasherVR/Private/SDasherWidget.cpp
index 12cc35696b2fee1695b8accfb4d2ab9687a288df..6d993fc67e49d6c65ce0e2ac73101d7c74e213dc 100644
--- a/Source/DasherVR/Private/SDasherWidget.cpp
+++ b/Source/DasherVR/Private/SDasherWidget.cpp
@@ -4,16 +4,14 @@
 #include "Brushes/SlateColorBrush.h"
 #include "Rendering/DrawElements.h"
 #include "DasherInterface.h"
-
 #include "IDisplayCluster.h"
 #include "Cluster/DisplayClusterClusterEvent.h"
 #include "Cluster/IDisplayClusterClusterManager.h"
 
+#include "Utility/RWTHVRUtilities.h"
+
 #include "Components/SlateWrapperTypes.h"
 #include "Framework/Application/SlateApplication.h"
-#include "Serialization/BufferArchive.h"
-#include "Serialization/BufferWriter.h"
-#include "Utility/RWTHVRUtilities.h"
 
 BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
 
@@ -48,6 +46,18 @@ FReply SDasherWidget::HandleMouseMoveEvent(const FGeometry& Geometry, const FPoi
         if (CurrentlyUsingVectorInput) return FReply::Unhandled();
         //The mouse event only contains the Screen Space Position
         CursorPosition = Geometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition());
+
+        if(URWTHVRUtilities::IsRoomMountedMode())
+	    {
+		    FDisplayClusterClusterEventJson ClusterEvent;
+	        ClusterEvent.Category = "Dasher";
+	        ClusterEvent.Type = "Input";
+	        ClusterEvent.Name = "MouseMove";
+            ClusterEvent.Parameters.Add("X", FString::SanitizeFloat(CursorPosition.X));
+            ClusterEvent.Parameters.Add("Y", FString::SanitizeFloat(CursorPosition.Y));
+		    IDisplayCluster::Get().GetClusterMgr()->EmitClusterEventJson(ClusterEvent, false);
+	        return FReply::Handled().ReleaseMouseLock();
+	    }
     }
     return FReply::Handled();
 }
@@ -56,6 +66,16 @@ FReply SDasherWidget::HandleMouseDownEvent(const FGeometry& Geometry, const FPoi
 {
     if (!InputPaused)
     {
+        if(URWTHVRUtilities::IsRoomMountedMode())
+	    {
+		    FDisplayClusterClusterEventJson ClusterEvent;
+	        ClusterEvent.Category = "Dasher";
+	        ClusterEvent.Type = "Input";
+	        ClusterEvent.Name = "MouseDown";
+		    IDisplayCluster::Get().GetClusterMgr()->EmitClusterEventJson(ClusterEvent, false);
+	        return FReply::Handled().ReleaseMouseLock();
+	    }
+
         if (CurrentlyUsingVectorInput)
         {
             CurrentlyUsingVectorInput = false;
@@ -69,6 +89,16 @@ FReply SDasherWidget::HandleMouseDownEvent(const FGeometry& Geometry, const FPoi
 
 FReply SDasherWidget::HandleMouseUpEvent(const FGeometry& Geometry, const FPointerEvent& MouseEvent)
 {
+    if(URWTHVRUtilities::IsRoomMountedMode())
+    {
+	    FDisplayClusterClusterEventJson ClusterEvent;
+        ClusterEvent.Category = "Dasher";
+        ClusterEvent.Type = "Input";
+        ClusterEvent.Name = "MouseUp";
+	    IDisplayCluster::Get().GetClusterMgr()->EmitClusterEventJson(ClusterEvent, false);
+        return FReply::Handled().ReleaseMouseLock();
+    }
+
     if (CurrentlyUsingVectorInput)
     {
         CurrentlyUsingVectorInput = false;
@@ -82,8 +112,19 @@ FReply SDasherWidget::HandleMouseUpEvent(const FGeometry& Geometry, const FPoint
 
 FReply SDasherWidget::HandleMouseDoubleClickEvent(const FGeometry& Geometry, const FPointerEvent& MouseEvent)
 {
+
     if (!InputPaused)
     {
+	    if(URWTHVRUtilities::IsRoomMountedMode())
+	    {
+		    FDisplayClusterClusterEventJson ClusterEvent;
+	        ClusterEvent.Category = "Dasher";
+	        ClusterEvent.Type = "Input";
+	        ClusterEvent.Name = "MouseDown";
+		    IDisplayCluster::Get().GetClusterMgr()->EmitClusterEventJson(ClusterEvent, false);
+	        return FReply::Handled().ReleaseMouseLock();
+	    }
+
         if (CurrentlyUsingVectorInput)
         {
             CurrentlyUsingVectorInput = false;
@@ -111,9 +152,36 @@ bool SDasherWidget::GetScreenCoords(screenint& iX, screenint& iY, Dasher::CDashe
     return true;
 }
 
+void SDasherWidget::HandleClusterEvent(const FDisplayClusterClusterEventJson& Event)
+{
+    if(Event.Category != "Dasher") return;
+    if(Event.Type == "Input")
+    {
+	    if(Event.Name == "MouseUp")
+	    {
+			DasherMainInterface->KeyUp(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Primary_Input);
+			MouseUpListeners.ExecuteIfBound();   
+	    } else
+        if(Event.Name == "MouseDown")
+	    {
+		    DasherMainInterface->KeyDown(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Primary_Input);
+			MouseDownListeners.ExecuteIfBound();
+	    } else
+        if(Event.Name == "MouseMove")
+	    {
+            CursorPosition.X = FCString::Atod(*Event.Parameters["X"]);
+            CursorPosition.Y = FCString::Atod(*Event.Parameters["Y"]);
+	    }
+    } else
+	if(Event.Type == "Tick")
+	{
+		double currentTime = FCString::Atod(*Event.Parameters["Time"]);
+        DasherMainInterface->Tick(static_cast<unsigned long>(currentTime * 1000.0));
+	}
+}
+
 void SDasherWidget::InputVector(const FVector2D InputVector)
 {
-    if (!URWTHVRUtilities::IsPrimaryNode()) return;
     if (!InputPaused) {
         if (!CurrentlyUsingVectorInput) return;
         CursorPosition = DasherMainInterface->ConvertDasher2Screen(Dasher::CDasherModel::ORIGIN_X, Dasher::CDasherModel::ORIGIN_Y)
@@ -123,7 +191,6 @@ void SDasherWidget::InputVector(const FVector2D InputVector)
 
 void SDasherWidget::InputButton(bool Pressed)
 {
-    if (!URWTHVRUtilities::IsPrimaryNode()) return;
     if (!InputPaused) {
         CurrentlyUsingVectorInput = true;
         if (Pressed)
@@ -136,42 +203,6 @@ void SDasherWidget::InputButton(bool Pressed)
     }
 }
 
-void SDasherWidget::HandleClusterEvent(const FDisplayClusterClusterEventBinary& Event)
-{
-    if(!URWTHVRUtilities::IsPrimaryNode()) UE_LOG(LogTemp, Log, TEXT("Non Primary Receive"));
-
-    FMemoryReader r(Event.EventData);
-
-    BackBuffer->Empty();
-
-    r << NDisplayBuffer;
-
-	GeometryType t;
-    bool readError = false;
-    while(!r.AtEnd() && !readError)
-    {
-        r << t;
-        switch (t)
-		{
-		case GeometryType::Rectangle:
-			BackBuffer->Add(FFilledRect::Deserialize(r));
-            break;
-		case GeometryType::Writing:
-			BackBuffer->Add(FWriting::Deserialize(r));
-            break;
-		case GeometryType::PolyLine:
-			BackBuffer->Add(FPolyLine::Deserialize(r));
-            break;
-        default:
-            readError = true;
-			break;
-		}
-    }
-
-    std::swap(FrontBuffer, BackBuffer);
-    BackBuffer->Empty();
-}
-
 //The construction of the Dasher Widget, Dasher parameters are set here, a function to set them outside is not provided, but can be easily implemented
 void SDasherWidget::Construct(const FArguments& InArgs)
 {
@@ -202,17 +233,17 @@ void SDasherWidget::Construct(const FArguments& InArgs)
     if (ClusterManager && !ClusterEvent.IsBound())
     {
         ClusterEvent =
-            FOnClusterEventBinaryListener::CreateSP(this, &SDasherWidget::HandleClusterEvent);
-        ClusterManager->AddClusterEventBinaryListener(ClusterEvent);
+            FOnClusterEventJsonListener::CreateSP(this, &SDasherWidget::HandleClusterEvent);
+        ClusterManager->AddClusterEventJsonListener(ClusterEvent);
     }
 
-    //Set up the Event Handlers for Mouse Movement etc.
     if (URWTHVRUtilities::IsPrimaryNode())
     {
-        SetOnMouseMove(FPointerEventHandler::CreateLambda([this](const FGeometry& Geometry, const FPointerEvent& MouseEvent) {return HandleMouseMoveEvent(Geometry, MouseEvent); }));
-        SetOnMouseButtonDown(FPointerEventHandler::CreateLambda([this](const FGeometry& Geometry, const FPointerEvent& MouseEvent) {return HandleMouseDownEvent(Geometry, MouseEvent); }));
-        SetOnMouseButtonUp(FPointerEventHandler::CreateLambda([this](const FGeometry& Geometry, const FPointerEvent& MouseEvent) {return HandleMouseUpEvent(Geometry, MouseEvent); }));
-        SetOnMouseDoubleClick(FPointerEventHandler::CreateLambda([this](const FGeometry& Geometry, const FPointerEvent& MouseEvent) {return HandleMouseDoubleClickEvent(Geometry, MouseEvent); })); //We treat a double click of the mouse as a lift of the mouse button.
+	    //Set up the Event Handlers for Mouse Movement etc.
+	    SetOnMouseMove(FPointerEventHandler::CreateLambda([this](const FGeometry& Geometry, const FPointerEvent& MouseEvent) {return HandleMouseMoveEvent(Geometry, MouseEvent); }));
+	    SetOnMouseButtonDown(FPointerEventHandler::CreateLambda([this](const FGeometry& Geometry, const FPointerEvent& MouseEvent) {return HandleMouseDownEvent(Geometry, MouseEvent); }));
+	    SetOnMouseButtonUp(FPointerEventHandler::CreateLambda([this](const FGeometry& Geometry, const FPointerEvent& MouseEvent) {return HandleMouseUpEvent(Geometry, MouseEvent); }));
+	    SetOnMouseDoubleClick(FPointerEventHandler::CreateLambda([this](const FGeometry& Geometry, const FPointerEvent& MouseEvent) {return HandleMouseDoubleClickEvent(Geometry, MouseEvent); })); //We treat a double click of the mouse as a lift of the mouse button.
     }
 }
 
@@ -269,23 +300,32 @@ int32 SDasherWidget::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGe
 
 //The tick function, we tick dasher in it and update the screen size for dasher
 void SDasherWidget::Tick(const FGeometry& AllotedGeometry, const double InCurrentTime, const float InDeltaTime) {
+    SCompoundWidget::Tick(AllotedGeometry, InCurrentTime, InDeltaTime);
+
     //don't tick in the editor
-    if (!IsEditor && !InputPaused && URWTHVRUtilities::IsPrimaryNode()) {
-        SCompoundWidget::Tick(AllotedGeometry, InCurrentTime, InDeltaTime);
+    if (!IsEditor && !InputPaused && !URWTHVRUtilities::IsPrimaryNode()) return;
 
+    if(!URWTHVRUtilities::IsRoomMountedMode()){
         DasherMainInterface->Tick(static_cast<unsigned long>(InCurrentTime * 1000.0)); //we need to provide ticks in milliseconds
+    }
+	else
+    {
+        FDisplayClusterClusterEventJson ClusterEvent;
+        ClusterEvent.Category = "Dasher";
+        ClusterEvent.Type = "Tick";
+        ClusterEvent.Parameters.Add("Time", FString::SanitizeFloat(InCurrentTime));
+	    IDisplayCluster::Get().GetClusterMgr()->EmitClusterEventJson(ClusterEvent, false);
+    }
 
-
-        //This probably doesn't have to be done every tick, but it does not seem to have a huge hit on performance
-        const FGeometry Geometry = GetTickSpaceGeometry();
-        const FIntPoint AbsoluteSize = Geometry.Size.IntPoint();
-        if (Width != AbsoluteSize.X || Height != AbsoluteSize.Y) {
-            Width = AbsoluteSize.X;
-            Height = AbsoluteSize.Y;
-            resize(Width, Height);
-            //tell dasher we resized the screen, but only if there's actually a screen
-            if (Width && Height) DasherMainInterface->ScreenResized(this);
-        }
+    //This probably doesn't have to be done every tick, but it does not seem to have a huge hit on performance
+    const FGeometry Geometry = GetTickSpaceGeometry();
+    const FIntPoint AbsoluteSize = Geometry.Size.IntPoint();
+    if (Width != AbsoluteSize.X || Height != AbsoluteSize.Y) {
+        Width = AbsoluteSize.X;
+        Height = AbsoluteSize.Y;
+        resize(Width, Height);
+        //tell dasher we resized the screen, but only if there's actually a screen
+        if (Width && Height) DasherMainInterface->ScreenResized(this);
     }
 }
 
@@ -298,41 +338,21 @@ std::pair<SDasherWidget::screenint, SDasherWidget::screenint> SDasherWidget::Tex
 
 //Double Buffers are rotated here.
 void SDasherWidget::Display() {
+    std::swap(FrontBuffer, BackBuffer);
+    BackBuffer->Empty();
 
-	// if (URWTHVRUtilities::IsRoomMountedMode())
-	// {
-		// auto start = std::chrono::high_resolution_clock::now();
-		// FDisplayClusterClusterEventBinary BinaryClusterEvent;
-		// BinaryClusterEvent.EventData.Reserve(100000); // 100kb of data
-		// FMemoryWriter m(BinaryClusterEvent.EventData);
-
-		// m << NDisplayBuffer;
-
-		// for(auto& e : *BackBuffer)
-		// {
-		    // e->Serialize(m);
-		// }
-
-		// IDisplayCluster::Get().GetClusterMgr()->EmitClusterEventBinary(BinaryClusterEvent, false);
-    // }
-	// else
-    // {
-	    std::swap(FrontBuffer, BackBuffer);
-	    BackBuffer->Empty();
-
-	    if (CharacterEnteredFlag && CharacterDeletedFlag) {
-	        CharacterSwitched.ExecuteIfBound(AlteredChar, GetBuffer());
-	    }
-	    else if (CharacterEnteredFlag) {
-	        CharacterEntered.ExecuteIfBound(AlteredChar, GetBuffer());
-	    }
-	    else if (CharacterDeletedFlag) {
-	        CharacterDeleted.ExecuteIfBound(AlteredChar, GetBuffer());
-	    }
+    if (CharacterEnteredFlag && CharacterDeletedFlag) {
+        CharacterSwitched.ExecuteIfBound(AlteredChar, GetBuffer());
+    }
+    else if (CharacterEnteredFlag) {
+        CharacterEntered.ExecuteIfBound(AlteredChar, GetBuffer());
+    }
+    else if (CharacterDeletedFlag) {
+        CharacterDeleted.ExecuteIfBound(AlteredChar, GetBuffer());
+    }
 
-	    CharacterEnteredFlag = false;
-	    CharacterDeletedFlag = false;
-    // }
+    CharacterEnteredFlag = false;
+    CharacterDeletedFlag = false;
 }
 
 //Functions for Drawing
@@ -388,13 +408,12 @@ void SDasherWidget::Polygon(CDasherScreen::point* points, int number, const Dash
 //We pass through the contents of the dasher buffer
 FString SDasherWidget::GetBuffer() const
 {
-    return (URWTHVRUtilities::IsPrimaryNode()) ? DasherMainInterface->GetBuffer() : NDisplayBuffer;
+    return DasherMainInterface->GetBuffer();
 }
 
 void SDasherWidget::ResetBuffer()
 {
     DasherMainInterface->ResetBuffer();
-    NDisplayBuffer = "";
 }
 
 void SDasherWidget::StartTraining(FString PathToTextFile)
diff --git a/Source/DasherVR/Public/SDasherWidget.h b/Source/DasherVR/Public/SDasherWidget.h
index 765919e21de9c86df68f60ae8d861ae5d4a6ab46..b65371c2c0698f0bb48f8785919fb754b0b95440 100644
--- a/Source/DasherVR/Public/SDasherWidget.h
+++ b/Source/DasherVR/Public/SDasherWidget.h
@@ -5,11 +5,9 @@
 #include <utility>
 
 #include "DasherInterface.h"
-#include "Cluster/DisplayClusterClusterEvent.h"
 #include "Math/Vector2D.h"
 #include "Fonts/FontMeasure.h"
 #include "Widgets/DeclarativeSyntaxSupport.h"
-
 #include "Cluster/IDisplayClusterClusterManager.h"
 
 //using namespace Dasher;
@@ -36,24 +34,8 @@ struct FFilledRect : DasherDrawGeometry {
 	FVector2D top;
 	FVector2D bottom;
 	FLinearColor color;
-	void Serialize(FMemoryWriter& ar)
-	{
-		ar << Type;
-		ar << top;
-		ar << bottom;
-		ar << color;
-	}
-	static TUniquePtr<DasherDrawGeometry> Deserialize(FMemoryReader& ar)
-	{
-		TUniquePtr<FFilledRect> g = MakeUnique<FFilledRect>();
-		ar << g->top;
-		ar << g->bottom;
-		ar << g->color;
-		return g;
-	}
 
 	FFilledRect(FVector2D Top, FVector2D Bottom, FLinearColor Color) : DasherDrawGeometry(GeometryType::Rectangle), top(Top), bottom(Bottom), color(Color) {}
-	FFilledRect() : DasherDrawGeometry(GeometryType::Rectangle) {}
 };
 
 struct FWriting : DasherDrawGeometry{
@@ -62,26 +44,7 @@ struct FWriting : DasherDrawGeometry{
 	int size;
 	FLinearColor color;
 
-	void Serialize(FMemoryWriter& ar)
-	{
-		ar << Type;
-		ar << pos;
-		ar << size;
-		ar << color;
-		ar << label;
-	}
-	static TUniquePtr<DasherDrawGeometry> Deserialize(FMemoryReader& ar)
-	{
-		TUniquePtr<FWriting> g = MakeUnique<FWriting>();
-		ar << g->pos;
-		ar << g->size;
-		ar << g->color;
-		ar << g->label;
-		return g;
-	}
-
 	FWriting(FString Label, FVector2D Pos, int Size, FLinearColor Color) : DasherDrawGeometry(GeometryType::Writing), label(Label), pos(Pos), size(Size), color(Color)  {}
-	FWriting() : DasherDrawGeometry(GeometryType::Writing) {}
 };
 
 struct FPolyLine : DasherDrawGeometry{
@@ -90,26 +53,7 @@ struct FPolyLine : DasherDrawGeometry{
 	bool AntiAliasing;
 	FLinearColor color;
 
-	void Serialize(FMemoryWriter& ar)
-	{
-		ar << Type;
-		ar << points;
-		ar << linewidth;
-		ar << AntiAliasing;
-		ar << color;
-	}
-	static TUniquePtr<DasherDrawGeometry> Deserialize(FMemoryReader& ar)
-	{
-		TUniquePtr<FPolyLine> g = MakeUnique<FPolyLine>();
-		ar << g->points;
-		ar << g->linewidth;
-		ar << g->AntiAliasing;
-		ar << g->color;
-		return g;
-	}
-
 	FPolyLine(TArray<FVector2D> Points, float LineWidth, FLinearColor Color, bool AntiAliasing): DasherDrawGeometry(GeometryType::PolyLine), points(Points), linewidth(LineWidth), AntiAliasing(AntiAliasing), color(Color)  {}
-	FPolyLine(): DasherDrawGeometry(GeometryType::PolyLine) {}
 };
 
 DECLARE_DELEGATE(FDasherMouseUpDelegate);
@@ -158,8 +102,6 @@ public:
 
 	virtual void Display() override;
 
-	virtual void SendMarker(int imarker) override {}
-
 	virtual bool IsPointVisible(screenint x, screenint y) override { return true; }
 
 	//Pass-me-down returning Buffer
@@ -184,9 +126,10 @@ public:
 
 	virtual bool GetScreenCoords(screenint& iX, screenint& iY, Dasher::CDasherView* pView) override;
 
+	void HandleClusterEvent(const FDisplayClusterClusterEventJson& Event);
+
 	void InputVector(FVector2D InputVector);
 	void InputButton(bool Pressed);
-	void HandleClusterEvent(const FDisplayClusterClusterEventBinary& Event);
 	FVector2D GetCursorPosition();
 
 	//Allows to Pause Input
@@ -224,9 +167,8 @@ private:
 	//set up the font measure service to ... measure fonts.
 	TSharedPtr<FSlateFontMeasure> FontMeasureService;
 
-private:
-	FString NDisplayBuffer = "";
-	FOnClusterEventBinaryListener ClusterEvent;
+	//CAVE Sync
+	FOnClusterEventJsonListener ClusterEvent;
 
 protected:
 	// stores color information 
diff --git a/Source/Thirdparty/Dasher/DasherCore b/Source/Thirdparty/Dasher/DasherCore
index 015b8c82fab747de4ff98de27ac177fcac6ddbfc..f6211ff1ad2e6aaf3a0ecf7ecb58aec610893b52 160000
--- a/Source/Thirdparty/Dasher/DasherCore
+++ b/Source/Thirdparty/Dasher/DasherCore
@@ -1 +1 @@
-Subproject commit 015b8c82fab747de4ff98de27ac177fcac6ddbfc
+Subproject commit f6211ff1ad2e6aaf3a0ecf7ecb58aec610893b52