diff --git a/assets/games/liminal/example-scene.xml b/assets/games/liminal/example-scene.xml
index 69c5c707..5d3fdab1 100644
--- a/assets/games/liminal/example-scene.xml
+++ b/assets/games/liminal/example-scene.xml
@@ -16,7 +16,7 @@
     <set property="craig->material->color.a" value="0" />
 
     <!-- Fade out the black overlay -->
-    <animate item="square->material->color.a" from="1" to="0" time="1" curve="easeOut" />
+    <set property="square->material->color.a" from="1" to="0" duration="1" curve="easeOut" />
     
     <!-- Example Text, also showing how in future we can support multiple languages -->
     <text character="eth">
@@ -24,7 +24,7 @@
     </text>
     
     <!-- Fade in craig -->
-    <animate item="craig->material->color.a" from="0" to="1" time="1" curve="easeOut" />
+    <set item="craig->material->color.a" from="0" to="1" duration="1" curve="easeOut" />
     <text character="craig">
       <string lang="en">Hi, I'm Craig.</string>
     </text>
@@ -35,12 +35,12 @@
       at the same time. This will make craig exit screen left.
     -->
     <parallel>
-      <animate item="craig->material->color.a" from="1" to="0" time="1" curve="easeOut" />
-      <position item="craig" x="-2" time="1" curve="easeOut" />
+      <set item="craig->material->color.a" from="1" to="0" duration="1" curve="easeOut" />
+      <position item="craig" x="-2" duration="1" curve="easeOut" />
     </parallel>
 
     <!-- Now Craig is gone, so sad, let's now fade the screen out and present some choices -->
-    <animate item="square->material->color.a" from="0" to="1" time="1" curve="easeOut" />
+    <set item="square->material->color.a" from="0" to="1" duration="1" curve="easeOut" />
 
     <!-- Here I am creating a marker -->
     <marker name="craigCool" />
@@ -90,7 +90,7 @@
     <choice-set key="isCraigCool" value="yes" />
 
     <!-- We can also wait some time -->
-    <wait time="1" />
+    <wait duration="1" />
 
     <!--
       Now change scenes. Changing scenes is handled in-engine to stop the game
diff --git a/src/dawn/games/vn/components/VNManager.hpp b/src/dawn/games/vn/components/VNManager.hpp
index 7f5f1748..5f6d5842 100644
--- a/src/dawn/games/vn/components/VNManager.hpp
+++ b/src/dawn/games/vn/components/VNManager.hpp
@@ -32,7 +32,7 @@ namespace Dawn {
         this->events.push_back(event);
         return event;
       }
-
+      
       /**
        * Sets the currently active visual novel event. This is assumed to be 
        * the only way to handle events (no multiples currently).
diff --git a/src/dawn/games/vn/events/VNAnimateEvent.hpp b/src/dawn/games/vn/events/VNAnimateEvent.hpp
new file mode 100644
index 00000000..565a2430
--- /dev/null
+++ b/src/dawn/games/vn/events/VNAnimateEvent.hpp
@@ -0,0 +1,49 @@
+// Copyright (c) 2023 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#pragma once
+#include "VNEvent.hpp"
+#include "display/animation/SimplerCallbackAnimation.hpp"
+
+namespace Dawn {
+  template<typename T>
+  class VNAnimateEvent : public VNEvent {
+    public:
+      T from;
+      T to;
+      float_t duration;
+
+    protected:
+      SimplerCallbackAnimation<T> animation;
+
+      void onStart() override {
+        if(duration > 0) {
+          animation.clear();
+
+          animation.addKeyframe(0, from);
+          animation.addKeyframe(duration, to);
+
+          
+          animation.callback = [&](T v){
+            this->setValue(v);
+          };
+
+          // On-end
+          useEvent([&]() {
+            this->next();
+          }, animation.event2AnimationEnd); 
+
+          useEvent([&](float_t delta) {
+            animation.tick(delta);
+          }, getScene()->eventSceneUpdate);
+        } else {
+          this->setValue(to);
+          this->next();
+        }
+      }
+
+      virtual void setValue(T value) = 0;
+  };
+}
\ No newline at end of file
diff --git a/src/dawn/games/vn/events/VNEvent.hpp b/src/dawn/games/vn/events/VNEvent.hpp
index 6dd4ae3e..50840513 100644
--- a/src/dawn/games/vn/events/VNEvent.hpp
+++ b/src/dawn/games/vn/events/VNEvent.hpp
@@ -5,6 +5,7 @@
 
 #pragma once
 #include "games/vn/components/VNManager.hpp"
+#include "scene/SceneItem.hpp"
 
 namespace Dawn {
   class VNEvent : public StateOwner {
diff --git a/src/dawn/games/vn/events/VNPositionEvent.hpp b/src/dawn/games/vn/events/VNPositionEvent.hpp
index c3d359fe..7595e5a6 100644
--- a/src/dawn/games/vn/events/VNPositionEvent.hpp
+++ b/src/dawn/games/vn/events/VNPositionEvent.hpp
@@ -4,20 +4,27 @@
 // https://opensource.org/licenses/MIT
 
 #pragma once
-#include "VNEvent.hpp"
-#include "scene/SceneItem.hpp"
-#include "display/animation/SimplerCallbackAnimation.hpp"
+#include "VNAnimateEvent.hpp"
 
-#define VN_POSITION_EVENT_VALUE_UNCHANGED -123456789
+#define VN_POSITION_EVENT_VALUE_UNCHANGED std::numeric_limits<float>::min()
 
 namespace Dawn {
-  class VNPositionEvent : public VNEvent {
+  class VNPositionEvent : public VNAnimateEvent<glm::vec3> {
     public:
-      float_t x = VN_POSITION_EVENT_VALUE_UNCHANGED;
-      float_t y = VN_POSITION_EVENT_VALUE_UNCHANGED;
-      float_t z = VN_POSITION_EVENT_VALUE_UNCHANGED;
       SceneItem *item = nullptr;
-      float_t duration;
+
+      VNPositionEvent() {
+        from = glm::vec3(
+          VN_POSITION_EVENT_VALUE_UNCHANGED,
+          VN_POSITION_EVENT_VALUE_UNCHANGED,
+          VN_POSITION_EVENT_VALUE_UNCHANGED
+        );
+        to = glm::vec3(
+          VN_POSITION_EVENT_VALUE_UNCHANGED,
+          VN_POSITION_EVENT_VALUE_UNCHANGED,
+          VN_POSITION_EVENT_VALUE_UNCHANGED
+        );
+      }
 
     protected:
       SimplerCallbackAnimation<glm::vec3> animation;
@@ -26,33 +33,19 @@ namespace Dawn {
         assertNotNull(item);
 
         auto start = item->transform.getLocalPosition();
-        auto destination = glm::vec3(
-          x == VN_POSITION_EVENT_VALUE_UNCHANGED ? start.x : x,
-          y == VN_POSITION_EVENT_VALUE_UNCHANGED ? start.y : y,
-          z == VN_POSITION_EVENT_VALUE_UNCHANGED ? start.z : z
-        );
+        if(from.x == VN_POSITION_EVENT_VALUE_UNCHANGED) start.x = from.x;
+        if(from.y == VN_POSITION_EVENT_VALUE_UNCHANGED) start.y = from.y;
+        if(from.z == VN_POSITION_EVENT_VALUE_UNCHANGED) start.z = from.z;
 
-        if(duration > 0) {
-          animation.clear();
+        if(to.x == VN_POSITION_EVENT_VALUE_UNCHANGED) to.x = start.x;
+        if(to.y == VN_POSITION_EVENT_VALUE_UNCHANGED) to.y = start.y;
+        if(to.z == VN_POSITION_EVENT_VALUE_UNCHANGED) to.z = start.z;
+        
+        VNAnimateEvent::onStart();
+      }
 
-          animation.addKeyframe(0, start);
-          animation.addKeyframe(duration, destination);
-
-          animation.callback = [&](glm::vec3 v){
-            this->item->transform.setLocalPosition(v);
-          };
-
-          useEvent([&]() {
-            this->next();
-          }, animation.event2AnimationEnd); 
-
-          useEvent([&](float_t delta) {
-            animation.tick(delta);
-          }, getScene()->eventSceneUpdate);
-        } else {
-          this->item->transform.setLocalPosition(destination);
-          this->next();
-        }
+      void setValue(glm::vec3 value) override {
+        this->item->transform.setLocalPosition(value);
       }
   };
 }
\ No newline at end of file
diff --git a/src/dawn/games/vn/events/VNSetEvent.hpp b/src/dawn/games/vn/events/VNSetEvent.hpp
new file mode 100644
index 00000000..bd9b1e50
--- /dev/null
+++ b/src/dawn/games/vn/events/VNSetEvent.hpp
@@ -0,0 +1,27 @@
+// Copyright (c) 2023 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#pragma once
+#include "VNAnimateEvent.hpp"
+
+namespace Dawn {
+  template<typename T>
+  class VNSetEvent : public VNAnimateEvent<T> {
+    public:
+      T *modifies = nullptr;
+
+    protected:
+      void onStart() override {
+        assertNotNull(this->modifies);
+        this->from = *modifies;
+        
+        VNAnimateEvent<T>::onStart();
+      }
+
+      void setValue(T value) override {
+        *modifies = value;
+      }
+  };
+}
\ No newline at end of file
diff --git a/src/dawn/games/vn/events/VNTextEvent.hpp b/src/dawn/games/vn/events/VNTextEvent.hpp
new file mode 100644
index 00000000..9171986c
--- /dev/null
+++ b/src/dawn/games/vn/events/VNTextEvent.hpp
@@ -0,0 +1,20 @@
+// Copyright (c) 2023 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#pragma once
+#include "VNEvent.hpp"
+
+namespace Dawn {
+  class VNTextEvent : public VNEvent {
+    public:
+      std::string text;
+
+    protected:
+      void onStart() override {
+        std::cout << "TEXT: " << text << std::endl;
+        this->next();
+      }
+  };
+}
\ No newline at end of file
diff --git a/src/dawn/games/vn/events/VNWaitEvent.hpp b/src/dawn/games/vn/events/VNWaitEvent.hpp
new file mode 100644
index 00000000..e03f2302
--- /dev/null
+++ b/src/dawn/games/vn/events/VNWaitEvent.hpp
@@ -0,0 +1,18 @@
+// Copyright (c) 2023 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#pragma once
+#include "VNAnimateEvent.hpp"
+
+namespace Dawn {
+  class VNWaitEvent : public VNAnimateEvent<float_t> {
+    public:
+
+    protected:
+      void setValue(T value) override {
+        // Do nothing
+      }
+  };
+}
\ No newline at end of file
diff --git a/src/dawnliminal/scenes/HelloWorldScene.hpp b/src/dawnliminal/scenes/HelloWorldScene.hpp
index a62c843d..56000366 100644
--- a/src/dawnliminal/scenes/HelloWorldScene.hpp
+++ b/src/dawnliminal/scenes/HelloWorldScene.hpp
@@ -9,7 +9,9 @@
 #include "prefabs/SimpleSpinningCubePrefab.hpp"
 #include "games/vn/components/VNManager.hpp"
 #include "games/vn/events/VNDummyEvent.hpp"
+#include "games/vn/events/VNTextEvent.hpp"
 #include "games/vn/events/VNPositionEvent.hpp"
+#include "games/vn/events/VNSetEvent.hpp"
 
 namespace Dawn {
   class HelloWorldScene : public Scene {
@@ -17,6 +19,8 @@ namespace Dawn {
       Camera *camera;
       UICanvas *canvas;
 
+      int32_t test = 0;
+
       void stage() override {
         canvas = UICanvas::create(this);
         
@@ -32,14 +36,21 @@ namespace Dawn {
         auto eventTest = vnManager->createEvent<VNDummyEvent>();
 
         auto positionEvent = vnManager->createEvent<VNPositionEvent>();
-        positionEvent->x = 2.0f;
+        positionEvent->to.x = 2.0f;
         positionEvent->item = cube;
         positionEvent->duration = 3.0f;
 
+        auto vnTextEvent = vnManager->createEvent<VNTextEvent>();
+        vnTextEvent->text = "Hello World!";
+
+        auto setPropertyEvent = vnManager->createEvent<VNSetEvent<int32_t>>();
+        setPropertyEvent->modifies = &test;
+        setPropertyEvent->to = 10;
+
         eventTest
           ->then(positionEvent)
-          ->then(vnManager->createEvent<VNDummyEvent>())
-          ->then(vnManager->createEvent<VNDummyEvent>())
+          ->then(vnTextEvent)
+          ->then(setPropertyEvent)
         ;
         vnManager->setEvent(eventTest);
       }