diff --git a/DasherVR.uplugin b/DasherVR.uplugin
index 5697244bb824b2d5ab7d1092a8b3ac431876bb83..b36b042eda04d4b521a7fd80db5a18e0e7f11ad7 100644
--- a/DasherVR.uplugin
+++ b/DasherVR.uplugin
@@ -20,5 +20,15 @@
 			"Type": "Runtime",
 			"LoadingPhase": "PreDefault"
 		}
+	],
+	"Plugins": [
+		{
+			"Name": "nDisplay",
+			"Enabled": true
+		},
+		{
+			"Name": "RWTHVRToolkit",
+			"Enabled": true
+		}
 	]
 }
diff --git a/Source/DasherVR/DasherVR.Build.cs b/Source/DasherVR/DasherVR.Build.cs
index b1cd040debb4fc13a1c73e8acbbf980e8b524e4f..bf038bb08991a927c5ae61bfe0a2ecadc83a9817 100644
--- a/Source/DasherVR/DasherVR.Build.cs
+++ b/Source/DasherVR/DasherVR.Build.cs
@@ -42,7 +42,9 @@ public class DasherVR : ModuleRules
 				"Slate",
 				"SlateCore",
 				"UMG",
-				"RenderCore"
+				"RenderCore",
+				"DisplayCluster",
+				"RWTHVRToolkit"
 				// ... add private dependencies that you statically link with here ...	
 			}
 			);
diff --git a/Source/DasherVR/Private/SDasherWidget.cpp b/Source/DasherVR/Private/SDasherWidget.cpp
index 255d07b34c576cde7e033bac5136d116a26118ae..25c546ec9feacecd0eb39f40d1ca0d4a4fff468d 100644
--- a/Source/DasherVR/Private/SDasherWidget.cpp
+++ b/Source/DasherVR/Private/SDasherWidget.cpp
@@ -5,8 +5,15 @@
 #include "Rendering/DrawElements.h"
 #include "DasherInterface.h"
 
+#include "IDisplayCluster.h"
+#include "Cluster/DisplayClusterClusterEvent.h"
+#include "Cluster/IDisplayClusterClusterManager.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
 
@@ -17,16 +24,18 @@ BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
 //Set the state if we're in the editor or not.
 void SDasherWidget::SetEditor(bool EditorState)
 {
-  IsEditor = EditorState;
+    IsEditor = EditorState;
 }
 //Pause the input
 void SDasherWidget::PauseInput()
 {
+    if (!URWTHVRUtilities::IsPrimaryNode()) return;
     InputPaused = true;
 }
 //Unpause the input
 void SDasherWidget::UnpauseInput()
 {
+    if (!URWTHVRUtilities::IsPrimaryNode()) return;
     InputPaused = false;
 }
 
@@ -45,17 +54,17 @@ FReply SDasherWidget::HandleMouseMoveEvent(const FGeometry& Geometry, const FPoi
 
 FReply SDasherWidget::HandleMouseDownEvent(const FGeometry& Geometry, const FPointerEvent& MouseEvent)
 {
-  if (!InputPaused)
-  {
-    if (CurrentlyUsingVectorInput)
+    if (!InputPaused)
     {
-        CurrentlyUsingVectorInput = false;
-        return FReply::Handled().LockMouseToWidget(AsShared());
+        if (CurrentlyUsingVectorInput)
+        {
+            CurrentlyUsingVectorInput = false;
+            return FReply::Handled().LockMouseToWidget(AsShared());
+        }
+        DasherMainInterface->KeyDown(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Primary_Input);
+        MouseDownListeners.ExecuteIfBound();
     }
-    DasherMainInterface->KeyDown(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Primary_Input);
-    MouseDownListeners.ExecuteIfBound();
-  }
-  return FReply::Handled().LockMouseToWidget(AsShared());
+    return FReply::Handled().LockMouseToWidget(AsShared());
 }
 
 FReply SDasherWidget::HandleMouseUpEvent(const FGeometry& Geometry, const FPointerEvent& MouseEvent)
@@ -82,28 +91,29 @@ FReply SDasherWidget::HandleMouseDoubleClickEvent(const FGeometry& Geometry, con
         }
         DasherMainInterface->KeyUp(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Primary_Input);
     }
-        return FReply::Handled();
+    return FReply::Handled();
 }
 
 //Set in the HandleMouseMoveEvent or via Set InputVector
 //See tick for geometry things that could be use to modify this.
 FVector2D SDasherWidget::GetCursorPosition()
 {
-  return CursorPosition;
+    return CursorPosition;
 }
 
 bool SDasherWidget::GetScreenCoords(screenint& iX, screenint& iY, Dasher::CDasherView* pView)
 {
-  const FVector2D Position = GetCursorPosition();
-  
-  iX = Position.X;
-  iY = Position.Y;
+    const FVector2D Position = GetCursorPosition();
+
+    iX = Position.X;
+    iY = Position.Y;
 
-  return true;
+    return true;
 }
 
 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)
@@ -113,6 +123,7 @@ void SDasherWidget::InputVector(const FVector2D InputVector)
 
 void SDasherWidget::InputButton(bool Pressed)
 {
+    if (!URWTHVRUtilities::IsPrimaryNode()) return;
     if (!InputPaused) {
         CurrentlyUsingVectorInput = true;
         if (Pressed)
@@ -125,219 +136,273 @@ 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)
-{	
-  //Initial resize, needed for setup
-  Width = InArgs._width;
-  Height = InArgs._height;
-  resize(Width, Height);
-
-  //initialize the font measuring service.
-  FontMeasureService = FSlateApplication::Get().GetRenderer()->GetFontMeasureService();
-
-  //Setting up Dasher
-  static Dasher::XMLErrorDisplay display;
-  Dasher::XmlSettingsStore* Settings = new Dasher::XmlSettingsStore("Settings.xml", &display); //Gets deleted somewhere else
-  Settings->Load();
-  Settings->Save();
-  DasherMainInterface = MakeShared<Dasher::DasherInterface>(Settings);
-  DasherMainInterface->GetModuleManager()->RegisterInputDeviceModule(this, true);
-
-  DasherMainInterface->SetScreen(this);
-  DasherMainInterface->SetBuffer(0);
-
-  DasherMainInterface->SetCharEnteredCallback([this](FString Char, FString Buffer) {CharacterEnteredFlag = true; AlteredChar = Char; });
-  DasherMainInterface->SetCharDeletedCallback([this](FString Char, FString Buffer) {CharacterDeletedFlag = true; AlteredChar = Char; });
-
-  //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.
-    //You could add Slate Code here for child slots etc.
+{
+    //Initial resize, needed for setup
+    Width = InArgs._width;
+    Height = InArgs._height;
+    resize(Width, Height);
+
+    //initialize the font measuring service.
+    FontMeasureService = FSlateApplication::Get().GetRenderer()->GetFontMeasureService();
+
+    //Setting up Dasher
+    static Dasher::XMLErrorDisplay display;
+    Dasher::XmlSettingsStore* Settings = new Dasher::XmlSettingsStore("Settings.xml", &display); //Gets deleted somewhere else
+    Settings->Load();
+    Settings->Save();
+    DasherMainInterface = MakeShared<Dasher::DasherInterface>(Settings);
+    DasherMainInterface->GetModuleManager()->RegisterInputDeviceModule(this, true);
+
+    DasherMainInterface->SetScreen(this);
+    DasherMainInterface->SetBuffer(0);
+
+    DasherMainInterface->SetCharEnteredCallback([this](FString Char, FString Buffer) {CharacterEnteredFlag = true; AlteredChar = Char; });
+    DasherMainInterface->SetCharDeletedCallback([this](FString Char, FString Buffer) {CharacterDeletedFlag = true; AlteredChar = Char; });
+
+    // Bind the cluster events that manage the door state.
+    IDisplayClusterClusterManager* ClusterManager = IDisplayCluster::Get().GetClusterMgr();
+    if (ClusterManager && !ClusterEvent.IsBound())
+    {
+        ClusterEvent =
+            FOnClusterEventBinaryListener::CreateSP(this, &SDasherWidget::HandleClusterEvent);
+        ClusterManager->AddClusterEventBinaryListener(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.
+    }
 }
 
-void SDasherWidget::SetParameter(FString ParameterName, bool Value) {
-    for (auto param = Dasher::Settings::parameter_defaults.begin(); param != Dasher::Settings::parameter_defaults.end(); ++param)
-        if (param->second.name == TCHAR_TO_UTF8(*ParameterName)) {
-            DasherMainInterface->SetBoolParameter(param->first, Value);
-            return;
-        }						
+void SDasherWidget::SetParameter(FString& ParameterName, bool Value) {
+    DasherMainInterface->SetBoolParameter(Dasher::Settings::GetParameter(TCHAR_TO_UTF8(*ParameterName)).first, Value);
 }
 
-void SDasherWidget::SetParameter(FString ParameterName, int64 Value) {
-    for (auto param = Dasher::Settings::parameter_defaults.begin(); param != Dasher::Settings::parameter_defaults.end(); ++param)
-        if (param->second.name == TCHAR_TO_UTF8(*ParameterName)) {
-            DasherMainInterface->SetLongParameter(param->first, Value);
-            return;
-        }
+void SDasherWidget::SetParameter(FString& ParameterName, int64 Value) {
+    DasherMainInterface->SetLongParameter(Dasher::Settings::GetParameter(TCHAR_TO_UTF8(*ParameterName)).first, Value);
 }
 
-void SDasherWidget::SetParameter(FString ParameterName, FString Value) {
-    for (auto param = Dasher::Settings::parameter_defaults.begin(); param != Dasher::Settings::parameter_defaults.end(); ++param)
-        if (param->second.name == TCHAR_TO_UTF8(*ParameterName)) {
-            DasherMainInterface->SetStringParameter(param->first, TCHAR_TO_UTF8(*Value));
-            return;
-        }
+void SDasherWidget::SetParameter(FString& ParameterName, FString Value) {
+    DasherMainInterface->SetStringParameter(Dasher::Settings::GetParameter(TCHAR_TO_UTF8(*ParameterName)).first, TCHAR_TO_UTF8(*Value));
 }
 
 //paints our stuff, then lets compoundwidget (super::) draw its stuff
 //This draws from bottom to top rectangles->lines->labels, this is enough for our use, but for more complex scenarios a proper layering system might have to be implemented
 int32 SDasherWidget::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
 {
-  //this doesn't draw anything red, we just need a brush. Could probably find a better "empty" brush
-  auto MyBrush = FSlateColorBrush(FColor::Red);
-
-  FFilledRect* RectObject;
-  FWriting* WritingObject;
-  FPolyLine* LineObject;
-  FVector2D Size;
-  FString Text;
-  FSlateFontInfo Font;
-
-  TArray<TUniquePtr<DasherDrawGeometry>>& GeometryBuffer = *FrontBuffer;
-  for(TUniquePtr<DasherDrawGeometry>& GeneralObject : GeometryBuffer) {
-    switch(GeneralObject->Type)
-    {
-      case Rectangle:
-        RectObject = static_cast<FFilledRect*>(GeneralObject.Get());
-        Size = { FMath::Abs(RectObject->top.X - RectObject->bottom.X), FMath::Abs(RectObject->top.Y - RectObject->bottom.Y) };
-        FSlateDrawElement::MakeBox(OutDrawElements, LayerId++, AllottedGeometry.ToPaintGeometry(Size, FSlateLayoutTransform(1, RectObject->top)), &MyBrush, ESlateDrawEffect::None, RectObject->color); //actually adds the box to the geometry
-    	break;
-      case Writing:
-        WritingObject = static_cast<FWriting*>(GeneralObject.Get());
-        Text = FString(UTF8_TO_TCHAR(WritingObject->label->m_strText.c_str()));
+    //this doesn't draw anything red, we just need a brush. Could probably find a better "empty" brush
+    auto MyBrush = FSlateColorBrush(FColor::Red);
+
+    FFilledRect* RectObject;
+    FWriting* WritingObject;
+    FPolyLine* LineObject;
+    FVector2D Size;
+    FString Text;
+    FSlateFontInfo Font;
+
+    TArray<TUniquePtr<DasherDrawGeometry>>& GeometryBuffer = *FrontBuffer;
+    for (TUniquePtr<DasherDrawGeometry>& GeneralObject : GeometryBuffer) {
+        switch (GeneralObject->Type)
+        {
+        case GeometryType::Rectangle:
+            RectObject = static_cast<FFilledRect*>(GeneralObject.Get());
+            Size = { FMath::Abs(RectObject->top.X - RectObject->bottom.X), FMath::Abs(RectObject->top.Y - RectObject->bottom.Y) };
+            FSlateDrawElement::MakeBox(OutDrawElements, LayerId++, AllottedGeometry.ToPaintGeometry(Size, FSlateLayoutTransform(1, RectObject->top)), &MyBrush, ESlateDrawEffect::None, RectObject->color); //actually adds the box to the geometry
+            break;
+        case GeometryType::Writing:
+            WritingObject = static_cast<FWriting*>(GeneralObject.Get());
 
             Font = FCoreStyle::GetDefaultFontStyle("Roboto", WritingObject->size, FFontOutlineSettings::NoOutline); //get the font
-        FSlateDrawElement::MakeText(OutDrawElements, LayerId++, AllottedGeometry.ToPaintGeometry(FSlateLayoutTransform(1, FVector2D(WritingObject->pos.X, WritingObject->pos.Y))), Text , Font, ESlateDrawEffect::None, WritingObject->color);
-        break;
-      case PolyLine:
-        LineObject = static_cast<FPolyLine*>(GeneralObject.Get());
-        FSlateDrawElement::MakeLines(OutDrawElements, LayerId++, AllottedGeometry.ToPaintGeometry(), LineObject->points, ESlateDrawEffect::None, LineObject->color, LineObject->AntiAliasing, LineObject->linewidth);
-    	break;
-      default: break;
+            FSlateDrawElement::MakeText(OutDrawElements, LayerId++, AllottedGeometry.ToPaintGeometry(FSlateLayoutTransform(1, FVector2D(WritingObject->pos.X, WritingObject->pos.Y))), WritingObject->label, Font, ESlateDrawEffect::None, WritingObject->color);
+            break;
+        case GeometryType::PolyLine:
+            LineObject = static_cast<FPolyLine*>(GeneralObject.Get());
+            FSlateDrawElement::MakeLines(OutDrawElements, LayerId++, AllottedGeometry.ToPaintGeometry(), LineObject->points, ESlateDrawEffect::None, LineObject->color, LineObject->AntiAliasing, LineObject->linewidth);
+            break;
+        default: break;
+        }
     }
-  }
 
-  return SCompoundWidget::OnPaint(Args, AllottedGeometry, MyClippingRect, OutDrawElements, LayerId, InWidgetStyle,  bParentEnabled); //call the parent onPaint
+    return SCompoundWidget::OnPaint(Args, AllottedGeometry, MyClippingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled); //call the parent onPaint
 }
 
 //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) {
-  //don't tick in the editor
-  if (!IsEditor && !InputPaused) {
-    SCompoundWidget::Tick(AllotedGeometry, InCurrentTime, InDeltaTime);
+    //don't tick in the editor
+    if (!IsEditor && !InputPaused && URWTHVRUtilities::IsPrimaryNode()) {
+        SCompoundWidget::Tick(AllotedGeometry, InCurrentTime, InDeltaTime);
 
-    DasherMainInterface->Tick(static_cast<unsigned long>(InCurrentTime * 1000.0)); //we need to provide ticks in milliseconds
+        DasherMainInterface->Tick(static_cast<unsigned long>(InCurrentTime * 1000.0)); //we need to provide ticks in milliseconds
 
 
-    //This probably doesn't have to be done every tick, but it does not seem to have a huge hit on performance
+        //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);
-    };
-  }
+        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);
+        }
+    }
 }
 
 std::pair<SDasherWidget::screenint, SDasherWidget::screenint> SDasherWidget::TextSize(CDasherScreen::Label* Label, unsigned Size)
 {
-  const FSlateFontInfo Font = FCoreStyle::GetDefaultFontStyle("Roboto", Size, FFontOutlineSettings::NoOutline); //get the font
-  const FVector2D TextSize = FontMeasureService->Measure(FString(UTF8_TO_TCHAR(Label->m_strText.c_str())), Font, 1); //get the real size of the text, using the fontmeasuring service
-  return {static_cast<screenint>(TextSize.X), static_cast<screenint>(TextSize.Y)};
+    const FSlateFontInfo Font = FCoreStyle::GetDefaultFontStyle("Roboto", Size, FFontOutlineSettings::NoOutline); //get the font
+    const FVector2D TextSize = FontMeasureService->Measure(FString(UTF8_TO_TCHAR(Label->m_strText.c_str())), Font, 1); //get the real size of the text, using the fontmeasuring service
+    return { static_cast<screenint>(TextSize.X), static_cast<screenint>(TextSize.Y) };
 }
 
 //Double Buffers are rotated here.
 void SDasherWidget::Display() {
-  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());
-  }
-
-  CharacterEnteredFlag = false;
-  CharacterDeletedFlag = false;
+
+	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());
+	    }
+
+	    CharacterEnteredFlag = false;
+	    CharacterDeletedFlag = false;
+    }
 }
 
 //Functions for Drawing
 void SDasherWidget::DrawRectangle(Dasher::screenint x1, Dasher::screenint y1, Dasher::screenint x2, Dasher::screenint y2, const Dasher::ColorPalette::Color& color, const Dasher::ColorPalette::Color& outlineColor, int iThickness)
 {
-    if(outlineColor == Dasher::ColorPalette::noColor) iThickness = 0; // Draw till brim if no outline color is given
+    if (outlineColor == Dasher::ColorPalette::noColor) iThickness = 0; // Draw till brim if no outline color is given
 
-	if(color != Dasher::ColorPalette::noColor && !color.isFullyTransparent())
-	{
-		BackBuffer->Add(MakeUnique<FFilledRect>(FVector2D(x1 + iThickness, y1 + iThickness), FVector2D(x2 - iThickness, y2 - iThickness), FLinearColor(color.Red / 255.0f, color.Green / 255.0f, color.Blue / 255.0f, color.Alpha / 255.0f)));
-	}
+    if (color != Dasher::ColorPalette::noColor && !color.isFullyTransparent())
+    {
+        BackBuffer->Add(MakeUnique<FFilledRect>(FVector2D(x1 + iThickness, y1 + iThickness), FVector2D(x2 - iThickness, y2 - iThickness), FLinearColor(color.Red / 255.0f, color.Green / 255.0f, color.Blue / 255.0f, color.Alpha / 255.0f)));
+    }
 
-	if (iThickness && outlineColor != Dasher::ColorPalette::noColor && !outlineColor.isFullyTransparent())
-	{
+    if (iThickness && outlineColor != Dasher::ColorPalette::noColor && !outlineColor.isFullyTransparent())
+    {
         const float hThickness = iThickness / 2.0f;
-		const FVector2D CornerMin = FVector2D(x1 + hThickness, y1 + hThickness);
-		const FVector2D CornerMax = FVector2D(x2 - hThickness, y2 - hThickness);
+        const FVector2D CornerMin = FVector2D(x1 + hThickness, y1 + hThickness);
+        const FVector2D CornerMax = FVector2D(x2 - hThickness, y2 - hThickness);
 
         const FLinearColor oColor = FLinearColor(outlineColor.Red / 255.0, outlineColor.Green / 255.0, outlineColor.Blue / 255.0, outlineColor.Alpha / 255.0f);
-        BackBuffer->Add(MakeUnique<FPolyLine>(TArray({FVector2D(CornerMin.X, CornerMin.Y - hThickness),FVector2D(CornerMin.X, CornerMax.Y + hThickness)}), static_cast<float>(iThickness), oColor, false));
-        BackBuffer->Add(MakeUnique<FPolyLine>(TArray({FVector2D(CornerMin.X - hThickness, CornerMax.Y),FVector2D(CornerMax.X + hThickness, CornerMax.Y)}), static_cast<float>(iThickness), oColor, false));
-        BackBuffer->Add(MakeUnique<FPolyLine>(TArray({FVector2D(CornerMax.X, CornerMax.Y + hThickness),FVector2D(CornerMax.X, CornerMin.Y - hThickness)}), static_cast<float>(iThickness), oColor, false));
-        BackBuffer->Add(MakeUnique<FPolyLine>(TArray({FVector2D(CornerMax.X + hThickness, CornerMin.Y),FVector2D(CornerMin.X - hThickness, CornerMin.Y)}), static_cast<float>(iThickness), oColor, false));
-	}
+        BackBuffer->Add(MakeUnique<FPolyLine>(TArray({ FVector2D(CornerMin.X, CornerMin.Y - hThickness),FVector2D(CornerMin.X, CornerMax.Y + hThickness) }), static_cast<float>(iThickness), oColor, false));
+        BackBuffer->Add(MakeUnique<FPolyLine>(TArray({ FVector2D(CornerMin.X - hThickness, CornerMax.Y),FVector2D(CornerMax.X + hThickness, CornerMax.Y) }), static_cast<float>(iThickness), oColor, false));
+        BackBuffer->Add(MakeUnique<FPolyLine>(TArray({ FVector2D(CornerMax.X, CornerMax.Y + hThickness),FVector2D(CornerMax.X, CornerMin.Y - hThickness) }), static_cast<float>(iThickness), oColor, false));
+        BackBuffer->Add(MakeUnique<FPolyLine>(TArray({ FVector2D(CornerMax.X + hThickness, CornerMin.Y),FVector2D(CornerMin.X - hThickness, CornerMin.Y) }), static_cast<float>(iThickness), oColor, false));
+    }
 }
 
 void SDasherWidget::DrawString(CDasherScreen::Label* lab, screenint x1, screenint y1, unsigned int iSize, const Dasher::ColorPalette::Color& color) {
-    BackBuffer->Add(MakeUnique<FWriting>(lab, FVector2D(x1, y1), static_cast<int>(iSize), FLinearColor(color.Red / 255.0, color.Green / 255.0, color.Blue / 255.0, color.Alpha / 255.0f)));
+    BackBuffer->Add(MakeUnique<FWriting>(UTF8_TO_TCHAR(lab->m_strText.c_str()), FVector2D(x1, y1), static_cast<int>(iSize), FLinearColor(color.Red / 255.0, color.Green / 255.0, color.Blue / 255.0, color.Alpha / 255.0f)));
 }
 
 void SDasherWidget::Polyline(CDasherScreen::point* points, int number, int iwidth, const Dasher::ColorPalette::Color& color) {
-  TArray<FVector2D> PointArray;
-  for (int i = 0; i < number; i++) {
-    FVector2D Point(points[i].x, points[i].y);
-    PointArray.Add(Point);
-  }
-  
-  BackBuffer->Add(MakeUnique<FPolyLine>(PointArray, static_cast<float>(iwidth), FLinearColor(color.Red / 255.0, color.Green / 255.0, color.Blue / 255.0, color.Alpha / 255.0f), true));
+    TArray<FVector2D> PointArray;
+    for (int i = 0; i < number; i++) {
+        FVector2D Point(points[i].x, points[i].y);
+        PointArray.Add(Point);
+    }
+
+    BackBuffer->Add(MakeUnique<FPolyLine>(PointArray, static_cast<float>(iwidth), FLinearColor(color.Red / 255.0, color.Green / 255.0, color.Blue / 255.0, color.Alpha / 255.0f), true));
 }
 
 //techincally polygons are just multiple polylines. Dasher doesn't actually draw polygons in our case.
 void SDasherWidget::Polygon(CDasherScreen::point* points, int number, const Dasher::ColorPalette::Color& fillcolor, const Dasher::ColorPalette::Color& outlinecolor, int iwidth) {
-  TArray<FVector2D> PointArray;
-  for (int i = 0; i < number; i++) {
-    FVector2D Point(points[i].x, points[i].y);
-    PointArray.Add(Point);
-  }
-  PointArray.Add(FVector2D(points[0].x, points[0].y));
-
-  BackBuffer->Add(MakeUnique<FPolyLine>(PointArray, static_cast<float>(iwidth), FLinearColor(outlinecolor.Red / 255.0, outlinecolor.Green / 255.0, outlinecolor.Blue / 255.0, outlinecolor.Alpha / 255.0), true));
+    TArray<FVector2D> PointArray;
+    for (int i = 0; i < number; i++) {
+        FVector2D Point(points[i].x, points[i].y);
+        PointArray.Add(Point);
+    }
+    PointArray.Add(FVector2D(points[0].x, points[0].y));
+
+    BackBuffer->Add(MakeUnique<FPolyLine>(PointArray, static_cast<float>(iwidth), FLinearColor(outlinecolor.Red / 255.0, outlinecolor.Green / 255.0, outlinecolor.Blue / 255.0, outlinecolor.Alpha / 255.0), true));
 }
 
 //We pass through the contents of the dasher buffer
 FString SDasherWidget::GetBuffer() const
 {
-  return DasherMainInterface->GetBuffer();
+    return (URWTHVRUtilities::IsPrimaryNode()) ? DasherMainInterface->GetBuffer() : NDisplayBuffer;
 }
 
 void SDasherWidget::ResetBuffer()
 {
-  DasherMainInterface->ResetBuffer();
+    DasherMainInterface->ResetBuffer();
+    NDisplayBuffer = "";
 }
 
 void SDasherWidget::StartTraining(FString PathToTextFile)
 {
-  DasherMainInterface->ImportTrainingFile(TCHAR_TO_UTF8(*FPaths::ConvertRelativePathToFull(PathToTextFile)));
+    DasherMainInterface->ImportTrainingFile(TCHAR_TO_UTF8(*FPaths::ConvertRelativePathToFull(PathToTextFile)));
 }
 
-
 // ++ We need to undefine this namespace after we finish creating the Slate widget
 #undef LOCTEXT_NAMESPACE
 
diff --git a/Source/DasherVR/Public/SDasherWidget.h b/Source/DasherVR/Public/SDasherWidget.h
index ead5b120b45b995fbc2ea9b333dcb94f63164ebd..765919e21de9c86df68f60ae8d861ae5d4a6ab46 100644
--- a/Source/DasherVR/Public/SDasherWidget.h
+++ b/Source/DasherVR/Public/SDasherWidget.h
@@ -5,21 +5,22 @@
 #include <utility>
 
 #include "DasherInterface.h"
+#include "Cluster/DisplayClusterClusterEvent.h"
 #include "Math/Vector2D.h"
 #include "Fonts/FontMeasure.h"
 #include "Widgets/DeclarativeSyntaxSupport.h"
 
-//using namespace Dasher;
-
+#include "Cluster/IDisplayClusterClusterManager.h"
 
+//using namespace Dasher;
 
 //Structs to hold the elements making up the UI
 
-enum GeometryType
+enum class GeometryType : uint8
 {
-	Rectangle,
-	Writing,
-	PolyLine
+	Rectangle = 0,
+	Writing = 1,
+	PolyLine = 2
 };
 
 struct DasherDrawGeometry
@@ -27,23 +28,60 @@ struct DasherDrawGeometry
 	GeometryType Type;
 	DasherDrawGeometry(GeometryType Type) : Type(Type) {}
 	virtual ~DasherDrawGeometry() {};
+
+	virtual void Serialize(FMemoryWriter& ar) = 0;
 };
 
-struct FFilledRect : DasherDrawGeometry{
+struct FFilledRect : DasherDrawGeometry {
 	FVector2D top;
 	FVector2D bottom;
 	FLinearColor color;
-
-	FFilledRect(FVector2D Top, FVector2D Bottom, FLinearColor Color) : DasherDrawGeometry(Rectangle), top(Top), bottom(Bottom), color(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{
-	Dasher::CDasherScreen::Label *label;
+	FString label;
 	FVector2D pos;
 	int size;
 	FLinearColor color;
 
-	FWriting(Dasher::CDasherScreen::Label *Label, FVector2D Pos, int Size, FLinearColor Color) : DasherDrawGeometry(Writing), label(Label), pos(Pos), size(Size), color(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{
@@ -52,7 +90,26 @@ struct FPolyLine : DasherDrawGeometry{
 	bool AntiAliasing;
 	FLinearColor color;
 
-	FPolyLine(TArray<FVector2D> Points, float LineWidth, FLinearColor Color, bool AntiAliasing): DasherDrawGeometry(PolyLine), points(Points), linewidth(LineWidth), AntiAliasing(AntiAliasing), color(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);
@@ -79,9 +136,9 @@ public:
 
 	// Constructs this widget with InArgs. Needed for every widget. Builds this widget and any of its children
 	void Construct(const FArguments& InArgs);
-	void SetParameter(FString ParameterName, bool Value);
-	void SetParameter(FString ParameterName, int64 Value);
-	void SetParameter(FString ParameterName, FString Value);
+	void SetParameter(FString& ParameterName, bool Value);
+	void SetParameter(FString& ParameterName, int64 Value);
+	void SetParameter(FString& ParameterName, FString Value);
 
 	virtual int32 OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override;
 
@@ -129,6 +186,7 @@ public:
 
 	void InputVector(FVector2D InputVector);
 	void InputButton(bool Pressed);
+	void HandleClusterEvent(const FDisplayClusterClusterEventBinary& Event);
 	FVector2D GetCursorPosition();
 
 	//Allows to Pause Input
@@ -166,6 +224,10 @@ private:
 	//set up the font measure service to ... measure fonts.
 	TSharedPtr<FSlateFontMeasure> FontMeasureService;
 
+private:
+	FString NDisplayBuffer = "";
+	FOnClusterEventBinaryListener ClusterEvent;
+
 protected:
 	// stores color information 
 	TSharedPtr<Dasher::DasherInterface> DasherMainInterface;
diff --git a/Source/Thirdparty/CMakeLists.txt b/Source/Thirdparty/CMakeLists.txt
index 4c001d32bb46711687653f3ba04b89fd51750962..4d2f8bc7d3a4fadaa50b4a6126eb3a809e1390cf 100644
--- a/Source/Thirdparty/CMakeLists.txt
+++ b/Source/Thirdparty/CMakeLists.txt
@@ -18,7 +18,7 @@ project("DasherLib")
 ############################################################################################################
 # Set this to your libc++ path included with Unreal
 ############################################################################################################
-set(LIBC_PATH "/home/unreal/UE4_Build_426/Engine/Source/ThirdParty/Linux/LibCxx/")
+set(LIBC_PATH "/home/ue4/UnrealEngine/Engine/Source/ThirdParty/Unix/LibCxx/")
 
 
 if (UNIX)
@@ -28,9 +28,9 @@ if (UNIX)
 
 	set(CMAKE_CXX_STANDARD 17)
 	set(CMAKE_CXX_STANDARD_REQUIRED ON)
-	add_compile_definitions(HAVE_ROUND XML_STATIC _CRT_SECURE_NO_WARNINGS HAVE_OWN_FILEUTILS HAVE_OWN_FILELOGGER)
+	add_compile_definitions(HAVE_ROUND XML_STATIC _CRT_SECURE_NO_WARNINGS HAVE_OWN_FILEUTILS HAVE_OWN_FILELOGGER PUGIXML_NO_EXCEPTIONS)
 	set(CMAKE_SUPPRESS_REGENERATION true)
-	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -std=c++17 -stdlib=libc++ -nostdinc++ -I${LIBC_PATH}include/c++/v1 -L${LIBC_PATH}lib -Wl -rpath ${LIBC_PATH}lib")
+	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -std=c++17 -nostdinc++ -I${LIBC_PATH}include/c++/v1 -L${LIBC_PATH}lib ${LIBC_PATH}lib")
 	set(CMAKE_EXE_LINKER_FLAGS "-stdlib=libc++")
 
 
diff --git a/Source/Thirdparty/Dasher/DasherCore b/Source/Thirdparty/Dasher/DasherCore
index a4df257624722cae45b8d9dd5d14dea8ce1dc4a5..0f0d1953f3ff1d4c20a1387d4bde2ede3b86f254 160000
--- a/Source/Thirdparty/Dasher/DasherCore
+++ b/Source/Thirdparty/Dasher/DasherCore
@@ -1 +1 @@
-Subproject commit a4df257624722cae45b8d9dd5d14dea8ce1dc4a5
+Subproject commit 0f0d1953f3ff1d4c20a1387d4bde2ede3b86f254
diff --git a/Source/Thirdparty/Dasher/Lib/DasherCore.a b/Source/Thirdparty/Dasher/Lib/DasherCore.a
new file mode 100644
index 0000000000000000000000000000000000000000..00410eed404cdbd6929169e1a7c3eedba2c9a311
Binary files /dev/null and b/Source/Thirdparty/Dasher/Lib/DasherCore.a differ