Event triggering

Explores an API for triggering events and querying them at runtime.

For Flutter we expose an onEvent callback that you can add a callback to on the StateMachineController:
```dart
final controller = StateMachineController.fromArtboard(artboard, 'bumpy', onEvent: {);
controller.onEvent = (event) {
  // Do something with event. Like:
  if(event is OpenURLEvent) {
    launchUrl(event.url);
  }
};
artboard.addController(controller!);
```

Note that I haven't piped onEvent to the Flutter runtime yet but you'll see it in the StateMachineController in core.

In C++:
```c++
auto count = stateMachineInstance->firedEventCount();
for(auto i = 0; i < count; i++) {
  auto event = stateMachineInstance->firedEventAt(i);
  if(event->is<OpenURLEvent>()) {
    // Do something with the url.
    auto url = event->as<OpenURLEvent>()->url();
  }
}
```

You can see some of this in action in the state_machine_event_test.cpp.

You can also see the events in the console in the editor:
<img width="717" alt="CleanShot 2023-08-10 at 18 03 22@2x" src="https://github.com/rive-app/rive/assets/454182/af21902a-424d-435b-b5b0-2a43701fe186">

In a follow up PR:
- Ability to trigger events from State (in/out) and Transition (start/end).
- Add custom properties to Events C++ API (currently they are loaded but not tracked against their respective events).

Diffs=
8caa7d377 Event triggering (#5793)

Co-authored-by: Luigi Rosso <luigi-rosso@users.noreply.github.com>
diff --git a/.rive_head b/.rive_head
index 4b1e608..f69ef3f 100644
--- a/.rive_head
+++ b/.rive_head
@@ -1 +1 @@
-e71ae68ba09767f246bf3d779f38bb731cf7da9a
+8caa7d377f368af8c667ca209a2ce7aef8679be2
diff --git a/dev/defs/animation/listener_fire_event.json b/dev/defs/animation/listener_fire_event.json
new file mode 100644
index 0000000..d5baa7d
--- /dev/null
+++ b/dev/defs/animation/listener_fire_event.json
@@ -0,0 +1,21 @@
+{
+  "name": "ListenerFireEvent",
+  "key": {
+    "int": 168,
+    "string": "listenerfireevent"
+  },
+  "extends": "animation/listener_action.json",
+  "properties": {
+    "eventId": {
+      "type": "Id",
+      "typeRuntime": "uint",
+      "initialValue": "Core.missingId",
+      "initialValueRuntime": "-1",
+      "key": {
+        "int": 389,
+        "string": "eventid"
+      },
+      "description": "Id of the Event referenced."
+    }
+  }
+}
\ No newline at end of file
diff --git a/dev/defs/animation/state_machine_component.json b/dev/defs/animation/state_machine_component.json
index b3272fb..2266155 100644
--- a/dev/defs/animation/state_machine_component.json
+++ b/dev/defs/animation/state_machine_component.json
@@ -34,6 +34,16 @@
       },
       "description": "Order of this component in the state machine. Exported in order for runtime",
       "runtime": false
+    },
+    "folderId": {
+      "type": "Id",
+      "initialValue": "Core.missingId",
+      "key": {
+        "int": 311,
+        "string": "folderid"
+      },
+      "description": "Id of the folder this state machine component belongs to",
+      "runtime": false
     }
   }
-}
+}
\ No newline at end of file
diff --git a/dev/defs/animation/state_machine_component_folder.json b/dev/defs/animation/state_machine_component_folder.json
new file mode 100644
index 0000000..ab05187
--- /dev/null
+++ b/dev/defs/animation/state_machine_component_folder.json
@@ -0,0 +1,9 @@
+{
+  "name": "StateMachineComponentFolder",
+  "key": {
+    "int": 151,
+    "string": "statemachinecomponentfolder"
+  },
+  "extends": "animation/state_machine_component.json",
+  "runtime": false
+}
\ No newline at end of file
diff --git a/dev/defs/animation/state_machine_input_folder.json b/dev/defs/animation/state_machine_input_folder.json
new file mode 100644
index 0000000..86e874f
--- /dev/null
+++ b/dev/defs/animation/state_machine_input_folder.json
@@ -0,0 +1,9 @@
+{
+  "name": "StateMachineInputFolder",
+  "key": {
+    "int": 149,
+    "string": "statemachineinputfolder"
+  },
+  "extends": "animation/state_machine_component_folder.json",
+  "runtime": false
+}
\ No newline at end of file
diff --git a/dev/defs/animation/state_machine_listener_folder.json b/dev/defs/animation/state_machine_listener_folder.json
new file mode 100644
index 0000000..9c59ae2
--- /dev/null
+++ b/dev/defs/animation/state_machine_listener_folder.json
@@ -0,0 +1,9 @@
+{
+  "name": "StateMachineListenerFolder",
+  "key": {
+    "int": 150,
+    "string": "statemachinelistenerfolder"
+  },
+  "extends": "animation/state_machine_component_folder.json",
+  "runtime": false
+}
\ No newline at end of file
diff --git a/dev/defs/artboard.json b/dev/defs/artboard.json
index 5319043..628ce9b 100644
--- a/dev/defs/artboard.json
+++ b/dev/defs/artboard.json
@@ -102,14 +102,14 @@
       "runtime": false,
       "coop": false
     },
-    "expandedAnimationFolders": {
+    "expandedFolders": {
       "type": "List<Id>",
       "initialValue": "[]",
       "key": {
         "int": 283,
         "string": "expandedanimationfolders"
       },
-      "description": "List of expanded animation folders",
+      "description": "List of expanded folders",
       "runtime": false,
       "coop": false
     },
@@ -125,4 +125,4 @@
       "coop": false
     }
   }
-}
+}
\ No newline at end of file
diff --git a/dev/defs/assets/font_asset.json b/dev/defs/assets/font_asset.json
index f10a790..01a5058 100644
--- a/dev/defs/assets/font_asset.json
+++ b/dev/defs/assets/font_asset.json
@@ -4,5 +4,37 @@
     "int": 141,
     "string": "fontasset"
   },
-  "extends": "assets/file_asset.json"
-}
+  "extends": "assets/file_asset.json",
+  "properties": {
+    "isCustom": {
+      "type": "bool",
+      "initialValue": "true",
+      "key": {
+        "int": 309,
+        "string": "iscustom"
+      },
+      "description": "True if the font is a custom font, i.e the user has uploaded it",
+      "runtime": false
+    },
+    "includeTypeValue": {
+      "type": "uint",
+      "initialValue": "0",
+      "key": {
+        "int": 360,
+        "string": "includetypevalue"
+      },
+      "description": "Which characters of the font to include for export: all, used or custom.",
+      "runtime": false
+    },
+    "includedUnicodes": {
+      "type": "String",
+      "initialValue": "''",
+      "key": {
+        "int": 361,
+        "string": "includedunicodes"
+      },
+      "description": "Included unicodes when exporting, in U+0040-0080, format.",
+      "runtime": false
+    }
+  }
+}
\ No newline at end of file
diff --git a/dev/defs/backboard.json b/dev/defs/backboard.json
index b194d68..a57597d 100644
--- a/dev/defs/backboard.json
+++ b/dev/defs/backboard.json
@@ -36,6 +36,17 @@
       "description": "The background color.",
       "runtime": false
     },
+    "animateColorValue": {
+      "type": "Color",
+      "initialValue": "0xFF1D1D1D",
+      "key": {
+        "int": 354,
+        "string": "animatecolorvalue"
+      },
+      "description": "The background color while in Animate mode",
+      "runtime": false,
+      "coop": false
+    },
     "editorMode": {
       "type": "uint",
       "initialValue": "0",
@@ -146,6 +157,61 @@
       "runtime": false,
       "coop": false
     },
+    "targetsEnabled": {
+      "type": "bool",
+      "initialValue": "true",
+      "key": {
+        "int": 379,
+        "string": "targetsenabled"
+      },
+      "description": "If targets and empty groups are enabled for this file",
+      "runtime": false,
+      "coop": false
+    },
+    "motionPathsEnabled": {
+      "type": "bool",
+      "initialValue": "true",
+      "key": {
+        "int": 351,
+        "string": "motionpathsenabled"
+      },
+      "description": "If motion paths are enabled for this file",
+      "runtime": false,
+      "coop": false
+    },
+    "showFinalPlaybackEnabled": {
+      "type": "bool",
+      "initialValue": "true",
+      "key": {
+        "int": 355,
+        "string": "showfinalplaybackenabled"
+      },
+      "description": "If final playback (hiding gizmos during playback) are enabled for this file",
+      "runtime": false,
+      "coop": false
+    },
+    "showModifierRange": {
+      "type": "bool",
+      "initialValue": "true",
+      "key": {
+        "int": 374,
+        "string": "showmodifierrange"
+      },
+      "description": "Whether text modifier ranges show on the stage",
+      "runtime": false,
+      "coop": false
+    },
+    "showModifierRangeValues": {
+      "type": "bool",
+      "initialValue": "false",
+      "key": {
+        "int": 375,
+        "string": "showmodifierrangevalues"
+      },
+      "description": "Whether text modifier range values show on the stage",
+      "runtime": false,
+      "coop": false
+    },
     "toolbarOutputAction": {
       "type": "uint",
       "initialValue": "0",
@@ -156,6 +222,96 @@
       "description": "Primary output action in the toolbar (share, publish, download)",
       "runtime": false,
       "coop": false
+    },
+    "assetsPanelState": {
+      "type": "uint",
+      "initialValue": "0",
+      "key": {
+        "int": 315,
+        "string": "assetspanelstate"
+      },
+      "runtime": false,
+      "coop": false
+    },
+    "defaultFontFamily": {
+      "type": "String",
+      "initialValue": "'Inter'",
+      "key": {
+        "int": 343,
+        "string": "defaultfontfamily"
+      },
+      "runtime": false,
+      "coop": false
+    },
+    "isDefaultFontFamilyCustom": {
+      "type": "bool",
+      "initialValue": "false",
+      "key": {
+        "int": 352,
+        "string": "isdefaultfontfamilycustom"
+      },
+      "runtime": false,
+      "coop": false
+    },
+    "defaultFontStyle": {
+      "type": "String",
+      "initialValue": "'Regular'",
+      "key": {
+        "int": 344,
+        "string": "defaultfontstyle"
+      },
+      "runtime": false,
+      "coop": false
+    },
+    "defaultFontSize": {
+      "type": "double",
+      "initialValue": "20.0",
+      "key": {
+        "int": 345,
+        "string": "defaultfontsize"
+      },
+      "runtime": false,
+      "coop": false
+    },
+    "defaultTextAlign": {
+      "type": "uint",
+      "initialValue": "0",
+      "key": {
+        "int": 346,
+        "string": "defaulttextalign"
+      },
+      "runtime": false,
+      "coop": false
+    },
+    "defaultTextOverflow": {
+      "type": "uint",
+      "initialValue": "0",
+      "key": {
+        "int": 347,
+        "string": "defaulttextoverflow"
+      },
+      "runtime": false,
+      "coop": false
+    },
+    "defaultTextHighlightTextRuns": {
+      "type": "bool",
+      "initialValue": "false",
+      "key": {
+        "int": 348,
+        "string": "defaulttexthighlighttextruns"
+      },
+      "runtime": false,
+      "coop": false
+    },
+    "defaultCollapseTags": {
+      "type": "bool",
+      "initialValue": "false",
+      "key": {
+        "int": 387,
+        "string": "defaultcollapsetags"
+      },
+      "runtime": false,
+      "coop": false
     }
   }
-}
+}
\ No newline at end of file
diff --git a/dev/defs/clipboard_header.json b/dev/defs/clipboard_header.json
new file mode 100644
index 0000000..22ad63b
--- /dev/null
+++ b/dev/defs/clipboard_header.json
@@ -0,0 +1,40 @@
+{
+  "name": "ClipboardHeader",
+  "key": {
+    "int": 146,
+    "string": "clipboardheader"
+  },
+  "runtime": false,
+  "properties": {
+    "fileId": {
+      "type": "uint",
+      "initialValue": "0",
+      "key": {
+        "int": 293,
+        "string": "fileid"
+      },
+      "description": "File clipboard contents has come from.",
+      "runtime": false
+    },
+    "projectId": {
+      "type": "uint",
+      "initialValue": "0",
+      "key": {
+        "int": 294,
+        "string": "projectid"
+      },
+      "description": "Project clipboard contents has come from.",
+      "runtime": false
+    },
+    "clipboardTypeValue": {
+      "type": "uint",
+      "initialValue": "0",
+      "key": {
+        "int": 295,
+        "string": "clipboardtypevalue"
+      },
+      "description": "Type of clipboard used to read these bytes",
+      "runtime": false
+    }
+  }
+}
\ No newline at end of file
diff --git a/dev/defs/component.json b/dev/defs/component.json
index 4d590a6..75fd1e8 100644
--- a/dev/defs/component.json
+++ b/dev/defs/component.json
@@ -36,6 +36,16 @@
       },
       "description": "Identifier used to track parent ContainerComponent."
     },
+    "tagIds": {
+      "type": "List<Id>",
+      "initialValue": "[]",
+      "key": {
+        "int": 382,
+        "string": "tagids"
+      },
+      "description": "Assigned tag ids.",
+      "runtime": false
+    },
     "childOrder": {
       "type": "FractionalIndex",
       "initialValue": "FractionalIndex.invalid",
diff --git a/dev/defs/custom_property.json b/dev/defs/custom_property.json
new file mode 100644
index 0000000..98c3684
--- /dev/null
+++ b/dev/defs/custom_property.json
@@ -0,0 +1,9 @@
+{
+  "name": "CustomProperty",
+  "key": {
+    "int": 167,
+    "string": "customproperty"
+  },
+  "abstract": true,
+  "extends": "component.json"
+}
\ No newline at end of file
diff --git a/dev/defs/custom_property_boolean.json b/dev/defs/custom_property_boolean.json
new file mode 100644
index 0000000..d114ca6
--- /dev/null
+++ b/dev/defs/custom_property_boolean.json
@@ -0,0 +1,19 @@
+{
+  "name": "CustomPropertyBoolean",
+  "key": {
+    "int": 129,
+    "string": "custompropertyboolean"
+  },
+  "extends": "custom_property.json",
+  "properties": {
+    "propertyValue": {
+      "type": "bool",
+      "initialValue": "false",
+      "animates": true,
+      "key": {
+        "int": 245,
+        "string": "value"
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/dev/defs/custom_property_number.json b/dev/defs/custom_property_number.json
new file mode 100644
index 0000000..3271b79
--- /dev/null
+++ b/dev/defs/custom_property_number.json
@@ -0,0 +1,19 @@
+{
+  "name": "CustomPropertyNumber",
+  "key": {
+    "int": 127,
+    "string": "custompropertynumber"
+  },
+  "extends": "custom_property.json",
+  "properties": {
+    "propertyValue": {
+      "type": "double",
+      "initialValue": "0",
+      "animates": true,
+      "key": {
+        "int": 243,
+        "string": "value"
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/dev/defs/custom_property_string.json b/dev/defs/custom_property_string.json
new file mode 100644
index 0000000..6f60ac9
--- /dev/null
+++ b/dev/defs/custom_property_string.json
@@ -0,0 +1,19 @@
+{
+  "name": "CustomPropertyString",
+  "key": {
+    "int": 130,
+    "string": "custompropertystring"
+  },
+  "extends": "custom_property.json",
+  "properties": {
+    "propertyValue": {
+      "type": "String",
+      "initialValue": "''",
+      "animates": true,
+      "key": {
+        "int": 246,
+        "string": "value"
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/dev/defs/event.json b/dev/defs/event.json
new file mode 100644
index 0000000..900d71e
--- /dev/null
+++ b/dev/defs/event.json
@@ -0,0 +1,8 @@
+{
+  "name": "Event",
+  "key": {
+    "int": 128,
+    "string": "event"
+  },
+  "extends": "container_component.json"
+}
\ No newline at end of file
diff --git a/dev/defs/guide.json b/dev/defs/guide.json
new file mode 100644
index 0000000..87f6d18
--- /dev/null
+++ b/dev/defs/guide.json
@@ -0,0 +1,32 @@
+{
+  "name": "Guide",
+  "key": {
+    "int": 140,
+    "string": "guide"
+  },
+  "extends": "component.json",
+  "runtime": false,
+  "properties": {
+    "axisPosition": {
+      "type": "double",
+      "initialValue": "0",
+      "overrideGet": true,
+      "group": "axisposition",
+      "key": {
+        "int": 276,
+        "string": "axisposition"
+      },
+      "runtime": false
+    },
+    "axisValue": {
+      "type": "uint",
+      "initialValue": "0",
+      "key": {
+        "int": 277,
+        "string": "axisvalue"
+      },
+      "description": "Axis the guide position relates to. 0 for x, 1 for y.",
+      "runtime": false
+    }
+  }
+}
\ No newline at end of file
diff --git a/dev/defs/joystick.json b/dev/defs/joystick.json
index 461454e..f356b79 100644
--- a/dev/defs/joystick.json
+++ b/dev/defs/joystick.json
@@ -106,6 +106,17 @@
         "string": "joystickflags"
       }
     },
+    "joystickOrder": {
+      "type": "uint",
+      "initialValue": "0",
+      "key": {
+        "int": 314,
+        "string": "joystickorder"
+      },
+      "runtime": false,
+      "coop": false,
+      "journal": false
+    },
     "handleSourceId": {
       "type": "Id",
       "typeRuntime": "uint",
@@ -118,4 +129,4 @@
       "description": "Identifier used to track the custom handle source of the joystick."
     }
   }
-}
+}
\ No newline at end of file
diff --git a/dev/defs/open_url_event.json b/dev/defs/open_url_event.json
new file mode 100644
index 0000000..77b57f9
--- /dev/null
+++ b/dev/defs/open_url_event.json
@@ -0,0 +1,28 @@
+{
+  "name": "OpenUrlEvent",
+  "key": {
+    "int": 131,
+    "string": "openurlevent"
+  },
+  "extends": "event.json",
+  "properties": {
+    "url": {
+      "type": "String",
+      "initialValue": "''",
+      "key": {
+        "int": 248,
+        "string": "url"
+      },
+      "description": "URL to open."
+    },
+    "targetValue": {
+      "type": "uint",
+      "initialValue": "0",
+      "key": {
+        "int": 249,
+        "string": "targetvalue"
+      },
+      "description": "Backing value for the target enum."
+    }
+  }
+}
\ No newline at end of file
diff --git a/dev/defs/tag.json b/dev/defs/tag.json
new file mode 100644
index 0000000..58c6ebf
--- /dev/null
+++ b/dev/defs/tag.json
@@ -0,0 +1,57 @@
+{
+  "name": "Tag",
+  "key": {
+    "int": 166,
+    "string": "tag"
+  },
+  "runtime": false,
+  "properties": {
+    "name": {
+      "type": "String",
+      "initialValue": "''",
+      "key": {
+        "int": 383,
+        "string": "name"
+      },
+      "runtime": false
+    },
+    "colorValue": {
+      "type": "Color",
+      "initialValue": "0xFF57A5E0",
+      "key": {
+        "int": 384,
+        "string": "colorvalue"
+      },
+      "runtime": false
+    },
+    "flags": {
+      "type": "uint",
+      "initialValue": "0",
+      "key": {
+        "int": 385,
+        "string": "flags"
+      },
+      "runtime": false
+    },
+    "order": {
+      "type": "FractionalIndex",
+      "initialValue": "FractionalIndex.invalid",
+      "key": {
+        "int": 388,
+        "string": "order"
+      },
+      "description": "Order this tag shows up in the tags list.",
+      "runtime": false
+    },
+    "dependentIds": {
+      "type": "List<Id>",
+      "initialValue": "[]",
+      "key": {
+        "int": 386,
+        "string": "dependentids"
+      },
+      "description": "Object ids assigned to this tag.",
+      "runtime": false
+    }
+  }
+}
\ No newline at end of file
diff --git a/dev/defs/text/text.json b/dev/defs/text/text.json
index 8bf0efd..7316395 100644
--- a/dev/defs/text/text.json
+++ b/dev/defs/text/text.json
@@ -51,6 +51,17 @@
       },
       "description": "Height of the text object."
     },
+    "highlightTextRuns": {
+      "type": "bool",
+      "initialValue": "true",
+      "key": {
+        "int": 310,
+        "string": "highlighttextruns"
+      },
+      "description": "Whether to draw colored underlines to identify runs.",
+      "runtime": false,
+      "coop": false
+    },
     "originX": {
       "type": "double",
       "initialValue": "0.0",
@@ -71,17 +82,6 @@
       },
       "description": "Origin y in normalized coordinates (0.5 = center, 0 = top, 1 = bottom)."
     },
-    "highlightTextRuns": {
-      "type": "bool",
-      "initialValue": "true",
-      "key": {
-        "int": 310,
-        "string": "highlighttextruns"
-      },
-      "description": "Whether to draw colored underlines to identify runs.",
-      "runtime": false,
-      "coop": false
-    },
     "paragraphSpacing": {
       "type": "double",
       "initialValue": "0.0",
diff --git a/dev/defs/text/text_value_run.json b/dev/defs/text/text_value_run.json
index 0dae240..41b2ac0 100644
--- a/dev/defs/text/text_value_run.json
+++ b/dev/defs/text/text_value_run.json
@@ -29,4 +29,4 @@
       "description": "The text string value."
     }
   }
-}
+}
\ No newline at end of file
diff --git a/include/rive/animation/listener_fire_event.hpp b/include/rive/animation/listener_fire_event.hpp
new file mode 100644
index 0000000..66c4850
--- /dev/null
+++ b/include/rive/animation/listener_fire_event.hpp
@@ -0,0 +1,14 @@
+#ifndef _RIVE_LISTENER_FIRE_EVENT_HPP_
+#define _RIVE_LISTENER_FIRE_EVENT_HPP_
+#include "rive/generated/animation/listener_fire_event_base.hpp"
+
+namespace rive
+{
+class ListenerFireEvent : public ListenerFireEventBase
+{
+public:
+    void perform(StateMachineInstance* stateMachineInstance, Vec2D position) const override;
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/animation/state_machine_instance.hpp b/include/rive/animation/state_machine_instance.hpp
index 5013b34..8bd9877 100644
--- a/include/rive/animation/state_machine_instance.hpp
+++ b/include/rive/animation/state_machine_instance.hpp
@@ -21,6 +21,7 @@
 class StateMachineLayerInstance;
 class HitShape;
 class NestedArtboard;
+class Event;
 
 class StateMachineInstance : public Scene
 {
@@ -72,7 +73,7 @@
 
     // The number of state changes that occurred across all layers on the
     // previous advance.
-    size_t stateChangedCount() const;
+    std::size_t stateChangedCount() const;
 
     // Returns the state name for states that changed in layers on the
     // previously called advance. If the index of out of range, it returns
@@ -92,6 +93,18 @@
     /// Allow anything referencing a concrete StateMachineInstace access to
     /// the backing artboard (explicitly not allowed on Scenes).
     Artboard* artboard() { return m_ArtboardInstance; }
+
+    /// Tracks an event that fired, will be cleared at the end of the next advance.
+    void fireEvent(Event* event);
+
+    /// Gets the number of events that fired since the last advance.
+    std::size_t firedEventCount() const;
+
+    /// Gets a fired event at an index < firedEventCount().
+    const Event* firedEventAt(std::size_t index) const;
+
+private:
+    std::vector<Event*> m_firedEvents;
 };
 } // namespace rive
 #endif
diff --git a/include/rive/custom_property.hpp b/include/rive/custom_property.hpp
new file mode 100644
index 0000000..2863962
--- /dev/null
+++ b/include/rive/custom_property.hpp
@@ -0,0 +1,13 @@
+#ifndef _RIVE_CUSTOM_PROPERTY_HPP_
+#define _RIVE_CUSTOM_PROPERTY_HPP_
+#include "rive/generated/custom_property_base.hpp"
+#include <stdio.h>
+namespace rive
+{
+class CustomProperty : public CustomPropertyBase
+{
+public:
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/custom_property_boolean.hpp b/include/rive/custom_property_boolean.hpp
new file mode 100644
index 0000000..260bc0f
--- /dev/null
+++ b/include/rive/custom_property_boolean.hpp
@@ -0,0 +1,13 @@
+#ifndef _RIVE_CUSTOM_PROPERTY_BOOLEAN_HPP_
+#define _RIVE_CUSTOM_PROPERTY_BOOLEAN_HPP_
+#include "rive/generated/custom_property_boolean_base.hpp"
+#include <stdio.h>
+namespace rive
+{
+class CustomPropertyBoolean : public CustomPropertyBooleanBase
+{
+public:
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/custom_property_number.hpp b/include/rive/custom_property_number.hpp
new file mode 100644
index 0000000..192351b
--- /dev/null
+++ b/include/rive/custom_property_number.hpp
@@ -0,0 +1,13 @@
+#ifndef _RIVE_CUSTOM_PROPERTY_NUMBER_HPP_
+#define _RIVE_CUSTOM_PROPERTY_NUMBER_HPP_
+#include "rive/generated/custom_property_number_base.hpp"
+#include <stdio.h>
+namespace rive
+{
+class CustomPropertyNumber : public CustomPropertyNumberBase
+{
+public:
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/custom_property_string.hpp b/include/rive/custom_property_string.hpp
new file mode 100644
index 0000000..7efc20e
--- /dev/null
+++ b/include/rive/custom_property_string.hpp
@@ -0,0 +1,13 @@
+#ifndef _RIVE_CUSTOM_PROPERTY_STRING_HPP_
+#define _RIVE_CUSTOM_PROPERTY_STRING_HPP_
+#include "rive/generated/custom_property_string_base.hpp"
+#include <stdio.h>
+namespace rive
+{
+class CustomPropertyString : public CustomPropertyStringBase
+{
+public:
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/event.hpp b/include/rive/event.hpp
new file mode 100644
index 0000000..faa5518
--- /dev/null
+++ b/include/rive/event.hpp
@@ -0,0 +1,13 @@
+#ifndef _RIVE_EVENT_HPP_
+#define _RIVE_EVENT_HPP_
+#include "rive/generated/event_base.hpp"
+#include <stdio.h>
+namespace rive
+{
+class Event : public EventBase
+{
+public:
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/generated/animation/listener_fire_event_base.hpp b/include/rive/generated/animation/listener_fire_event_base.hpp
new file mode 100644
index 0000000..33280cd
--- /dev/null
+++ b/include/rive/generated/animation/listener_fire_event_base.hpp
@@ -0,0 +1,71 @@
+#ifndef _RIVE_LISTENER_FIRE_EVENT_BASE_HPP_
+#define _RIVE_LISTENER_FIRE_EVENT_BASE_HPP_
+#include "rive/animation/listener_action.hpp"
+#include "rive/core/field_types/core_uint_type.hpp"
+namespace rive
+{
+class ListenerFireEventBase : public ListenerAction
+{
+protected:
+    typedef ListenerAction Super;
+
+public:
+    static const uint16_t typeKey = 168;
+
+    /// Helper to quickly determine if a core object extends another without RTTI
+    /// at runtime.
+    bool isTypeOf(uint16_t typeKey) const override
+    {
+        switch (typeKey)
+        {
+            case ListenerFireEventBase::typeKey:
+            case ListenerActionBase::typeKey:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    uint16_t coreType() const override { return typeKey; }
+
+    static const uint16_t eventIdPropertyKey = 389;
+
+private:
+    uint32_t m_EventId = -1;
+
+public:
+    inline uint32_t eventId() const { return m_EventId; }
+    void eventId(uint32_t value)
+    {
+        if (m_EventId == value)
+        {
+            return;
+        }
+        m_EventId = value;
+        eventIdChanged();
+    }
+
+    Core* clone() const override;
+    void copy(const ListenerFireEventBase& object)
+    {
+        m_EventId = object.m_EventId;
+        ListenerAction::copy(object);
+    }
+
+    bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
+    {
+        switch (propertyKey)
+        {
+            case eventIdPropertyKey:
+                m_EventId = CoreUintType::deserialize(reader);
+                return true;
+        }
+        return ListenerAction::deserialize(propertyKey, reader);
+    }
+
+protected:
+    virtual void eventIdChanged() {}
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/generated/core_registry.hpp b/include/rive/generated/core_registry.hpp
index efc7388..bf09d27 100644
--- a/include/rive/generated/core_registry.hpp
+++ b/include/rive/generated/core_registry.hpp
@@ -30,6 +30,7 @@
 #include "rive/animation/listener_action.hpp"
 #include "rive/animation/listener_align_target.hpp"
 #include "rive/animation/listener_bool_change.hpp"
+#include "rive/animation/listener_fire_event.hpp"
 #include "rive/animation/listener_input_change.hpp"
 #include "rive/animation/listener_number_change.hpp"
 #include "rive/animation/listener_trigger_change.hpp"
@@ -86,13 +87,19 @@
 #include "rive/constraints/transform_space_constraint.hpp"
 #include "rive/constraints/translation_constraint.hpp"
 #include "rive/container_component.hpp"
+#include "rive/custom_property.hpp"
+#include "rive/custom_property_boolean.hpp"
+#include "rive/custom_property_number.hpp"
+#include "rive/custom_property_string.hpp"
 #include "rive/draw_rules.hpp"
 #include "rive/draw_target.hpp"
 #include "rive/drawable.hpp"
+#include "rive/event.hpp"
 #include "rive/joystick.hpp"
 #include "rive/nested_animation.hpp"
 #include "rive/nested_artboard.hpp"
 #include "rive/node.hpp"
+#include "rive/open_url_event.hpp"
 #include "rive/shapes/clipping_shape.hpp"
 #include "rive/shapes/contour_mesh_vertex.hpp"
 #include "rive/shapes/cubic_asymmetric_vertex.hpp"
@@ -146,6 +153,8 @@
         {
             case DrawTargetBase::typeKey:
                 return new DrawTarget();
+            case CustomPropertyNumberBase::typeKey:
+                return new CustomPropertyNumber();
             case DistanceConstraintBase::typeKey:
                 return new DistanceConstraint();
             case IKConstraintBase::typeKey:
@@ -166,6 +175,8 @@
                 return new NestedArtboard();
             case SoloBase::typeKey:
                 return new Solo();
+            case ListenerFireEventBase::typeKey:
+                return new ListenerFireEvent();
             case NestedSimpleAnimationBase::typeKey:
                 return new NestedSimpleAnimation();
             case AnimationStateBase::typeKey:
@@ -294,14 +305,20 @@
                 return new Image();
             case CubicDetachedVertexBase::typeKey:
                 return new CubicDetachedVertex();
+            case EventBase::typeKey:
+                return new Event();
             case DrawRulesBase::typeKey:
                 return new DrawRules();
+            case CustomPropertyBooleanBase::typeKey:
+                return new CustomPropertyBoolean();
             case ArtboardBase::typeKey:
                 return new Artboard();
             case JoystickBase::typeKey:
                 return new Joystick();
             case BackboardBase::typeKey:
                 return new Backboard();
+            case OpenUrlEventBase::typeKey:
+                return new OpenUrlEvent();
             case WeightBase::typeKey:
                 return new Weight();
             case BoneBase::typeKey:
@@ -330,6 +347,8 @@
                 return new Text();
             case TextValueRunBase::typeKey:
                 return new TextValueRun();
+            case CustomPropertyStringBase::typeKey:
+                return new CustomPropertyString();
             case FolderBase::typeKey:
                 return new Folder();
             case ImageAssetBase::typeKey:
@@ -357,9 +376,15 @@
             case KeyFrameStringBase::valuePropertyKey:
                 object->as<KeyFrameStringBase>()->value(value);
                 break;
+            case OpenUrlEventBase::urlPropertyKey:
+                object->as<OpenUrlEventBase>()->url(value);
+                break;
             case TextValueRunBase::textPropertyKey:
                 object->as<TextValueRunBase>()->text(value);
                 break;
+            case CustomPropertyStringBase::propertyValuePropertyKey:
+                object->as<CustomPropertyStringBase>()->propertyValue(value);
+                break;
             case AssetBase::namePropertyKey:
                 object->as<AssetBase>()->name(value);
                 break;
@@ -411,6 +436,9 @@
             case SoloBase::activeComponentIdPropertyKey:
                 object->as<SoloBase>()->activeComponentId(value);
                 break;
+            case ListenerFireEventBase::eventIdPropertyKey:
+                object->as<ListenerFireEventBase>()->eventId(value);
+                break;
             case ListenerInputChangeBase::inputIdPropertyKey:
                 object->as<ListenerInputChangeBase>()->inputId(value);
                 break;
@@ -549,6 +577,9 @@
             case JoystickBase::handleSourceIdPropertyKey:
                 object->as<JoystickBase>()->handleSourceId(value);
                 break;
+            case OpenUrlEventBase::targetValuePropertyKey:
+                object->as<OpenUrlEventBase>()->targetValue(value);
+                break;
             case WeightBase::valuesPropertyKey:
                 object->as<WeightBase>()->values(value);
                 break;
@@ -624,6 +655,9 @@
     {
         switch (propertyKey)
         {
+            case CustomPropertyNumberBase::propertyValuePropertyKey:
+                object->as<CustomPropertyNumberBase>()->propertyValue(value);
+                break;
             case ConstraintBase::strengthPropertyKey:
                 object->as<ConstraintBase>()->strength(value);
                 break;
@@ -1077,6 +1111,9 @@
             case ClippingShapeBase::isVisiblePropertyKey:
                 object->as<ClippingShapeBase>()->isVisible(value);
                 break;
+            case CustomPropertyBooleanBase::propertyValuePropertyKey:
+                object->as<CustomPropertyBooleanBase>()->propertyValue(value);
+                break;
             case ArtboardBase::clipPropertyKey:
                 object->as<ArtboardBase>()->clip(value);
                 break;
@@ -1112,8 +1149,12 @@
                 return object->as<StateMachineComponentBase>()->name();
             case KeyFrameStringBase::valuePropertyKey:
                 return object->as<KeyFrameStringBase>()->value();
+            case OpenUrlEventBase::urlPropertyKey:
+                return object->as<OpenUrlEventBase>()->url();
             case TextValueRunBase::textPropertyKey:
                 return object->as<TextValueRunBase>()->text();
+            case CustomPropertyStringBase::propertyValuePropertyKey:
+                return object->as<CustomPropertyStringBase>()->propertyValue();
             case AssetBase::namePropertyKey:
                 return object->as<AssetBase>()->name();
         }
@@ -1151,6 +1192,8 @@
                 return object->as<NestedAnimationBase>()->animationId();
             case SoloBase::activeComponentIdPropertyKey:
                 return object->as<SoloBase>()->activeComponentId();
+            case ListenerFireEventBase::eventIdPropertyKey:
+                return object->as<ListenerFireEventBase>()->eventId();
             case ListenerInputChangeBase::inputIdPropertyKey:
                 return object->as<ListenerInputChangeBase>()->inputId();
             case AnimationStateBase::animationIdPropertyKey:
@@ -1243,6 +1286,8 @@
                 return object->as<JoystickBase>()->joystickFlags();
             case JoystickBase::handleSourceIdPropertyKey:
                 return object->as<JoystickBase>()->handleSourceId();
+            case OpenUrlEventBase::targetValuePropertyKey:
+                return object->as<OpenUrlEventBase>()->targetValue();
             case WeightBase::valuesPropertyKey:
                 return object->as<WeightBase>()->values();
             case WeightBase::indicesPropertyKey:
@@ -1296,6 +1341,8 @@
     {
         switch (propertyKey)
         {
+            case CustomPropertyNumberBase::propertyValuePropertyKey:
+                return object->as<CustomPropertyNumberBase>()->propertyValue();
             case ConstraintBase::strengthPropertyKey:
                 return object->as<ConstraintBase>()->strength();
             case DistanceConstraintBase::distancePropertyKey:
@@ -1601,6 +1648,8 @@
                 return object->as<RectangleBase>()->linkCornerRadius();
             case ClippingShapeBase::isVisiblePropertyKey:
                 return object->as<ClippingShapeBase>()->isVisible();
+            case CustomPropertyBooleanBase::propertyValuePropertyKey:
+                return object->as<CustomPropertyBooleanBase>()->propertyValue();
             case ArtboardBase::clipPropertyKey:
                 return object->as<ArtboardBase>()->clip();
             case TextModifierRangeBase::clampPropertyKey:
@@ -1629,7 +1678,9 @@
             case AnimationBase::namePropertyKey:
             case StateMachineComponentBase::namePropertyKey:
             case KeyFrameStringBase::valuePropertyKey:
+            case OpenUrlEventBase::urlPropertyKey:
             case TextValueRunBase::textPropertyKey:
+            case CustomPropertyStringBase::propertyValuePropertyKey:
             case AssetBase::namePropertyKey:
                 return CoreStringType::id;
             case ComponentBase::parentIdPropertyKey:
@@ -1646,6 +1697,7 @@
             case NestedArtboardBase::artboardIdPropertyKey:
             case NestedAnimationBase::animationIdPropertyKey:
             case SoloBase::activeComponentIdPropertyKey:
+            case ListenerFireEventBase::eventIdPropertyKey:
             case ListenerInputChangeBase::inputIdPropertyKey:
             case AnimationStateBase::animationIdPropertyKey:
             case NestedInputBase::inputIdPropertyKey:
@@ -1692,6 +1744,7 @@
             case JoystickBase::yIdPropertyKey:
             case JoystickBase::joystickFlagsPropertyKey:
             case JoystickBase::handleSourceIdPropertyKey:
+            case OpenUrlEventBase::targetValuePropertyKey:
             case WeightBase::valuesPropertyKey:
             case WeightBase::indicesPropertyKey:
             case TendonBase::boneIdPropertyKey:
@@ -1716,6 +1769,7 @@
             case TextValueRunBase::styleIdPropertyKey:
             case FileAssetBase::assetIdPropertyKey:
                 return CoreUintType::id;
+            case CustomPropertyNumberBase::propertyValuePropertyKey:
             case ConstraintBase::strengthPropertyKey:
             case DistanceConstraintBase::distancePropertyKey:
             case TransformComponentConstraintBase::copyFactorPropertyKey:
@@ -1866,6 +1920,7 @@
             case PointsPathBase::isClosedPropertyKey:
             case RectangleBase::linkCornerRadiusPropertyKey:
             case ClippingShapeBase::isVisiblePropertyKey:
+            case CustomPropertyBooleanBase::propertyValuePropertyKey:
             case ArtboardBase::clipPropertyKey:
             case TextModifierRangeBase::clampPropertyKey:
                 return CoreBoolType::id;
diff --git a/include/rive/generated/custom_property_base.hpp b/include/rive/generated/custom_property_base.hpp
new file mode 100644
index 0000000..1e21815
--- /dev/null
+++ b/include/rive/generated/custom_property_base.hpp
@@ -0,0 +1,34 @@
+#ifndef _RIVE_CUSTOM_PROPERTY_BASE_HPP_
+#define _RIVE_CUSTOM_PROPERTY_BASE_HPP_
+#include "rive/component.hpp"
+namespace rive
+{
+class CustomPropertyBase : public Component
+{
+protected:
+    typedef Component Super;
+
+public:
+    static const uint16_t typeKey = 167;
+
+    /// Helper to quickly determine if a core object extends another without RTTI
+    /// at runtime.
+    bool isTypeOf(uint16_t typeKey) const override
+    {
+        switch (typeKey)
+        {
+            case CustomPropertyBase::typeKey:
+            case ComponentBase::typeKey:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    uint16_t coreType() const override { return typeKey; }
+
+protected:
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/generated/custom_property_boolean_base.hpp b/include/rive/generated/custom_property_boolean_base.hpp
new file mode 100644
index 0000000..ebfe524
--- /dev/null
+++ b/include/rive/generated/custom_property_boolean_base.hpp
@@ -0,0 +1,72 @@
+#ifndef _RIVE_CUSTOM_PROPERTY_BOOLEAN_BASE_HPP_
+#define _RIVE_CUSTOM_PROPERTY_BOOLEAN_BASE_HPP_
+#include "rive/core/field_types/core_bool_type.hpp"
+#include "rive/custom_property.hpp"
+namespace rive
+{
+class CustomPropertyBooleanBase : public CustomProperty
+{
+protected:
+    typedef CustomProperty Super;
+
+public:
+    static const uint16_t typeKey = 129;
+
+    /// Helper to quickly determine if a core object extends another without RTTI
+    /// at runtime.
+    bool isTypeOf(uint16_t typeKey) const override
+    {
+        switch (typeKey)
+        {
+            case CustomPropertyBooleanBase::typeKey:
+            case CustomPropertyBase::typeKey:
+            case ComponentBase::typeKey:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    uint16_t coreType() const override { return typeKey; }
+
+    static const uint16_t propertyValuePropertyKey = 245;
+
+private:
+    bool m_PropertyValue = false;
+
+public:
+    inline bool propertyValue() const { return m_PropertyValue; }
+    void propertyValue(bool value)
+    {
+        if (m_PropertyValue == value)
+        {
+            return;
+        }
+        m_PropertyValue = value;
+        propertyValueChanged();
+    }
+
+    Core* clone() const override;
+    void copy(const CustomPropertyBooleanBase& object)
+    {
+        m_PropertyValue = object.m_PropertyValue;
+        CustomProperty::copy(object);
+    }
+
+    bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
+    {
+        switch (propertyKey)
+        {
+            case propertyValuePropertyKey:
+                m_PropertyValue = CoreBoolType::deserialize(reader);
+                return true;
+        }
+        return CustomProperty::deserialize(propertyKey, reader);
+    }
+
+protected:
+    virtual void propertyValueChanged() {}
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/generated/custom_property_number_base.hpp b/include/rive/generated/custom_property_number_base.hpp
new file mode 100644
index 0000000..57d6d2a
--- /dev/null
+++ b/include/rive/generated/custom_property_number_base.hpp
@@ -0,0 +1,72 @@
+#ifndef _RIVE_CUSTOM_PROPERTY_NUMBER_BASE_HPP_
+#define _RIVE_CUSTOM_PROPERTY_NUMBER_BASE_HPP_
+#include "rive/core/field_types/core_double_type.hpp"
+#include "rive/custom_property.hpp"
+namespace rive
+{
+class CustomPropertyNumberBase : public CustomProperty
+{
+protected:
+    typedef CustomProperty Super;
+
+public:
+    static const uint16_t typeKey = 127;
+
+    /// Helper to quickly determine if a core object extends another without RTTI
+    /// at runtime.
+    bool isTypeOf(uint16_t typeKey) const override
+    {
+        switch (typeKey)
+        {
+            case CustomPropertyNumberBase::typeKey:
+            case CustomPropertyBase::typeKey:
+            case ComponentBase::typeKey:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    uint16_t coreType() const override { return typeKey; }
+
+    static const uint16_t propertyValuePropertyKey = 243;
+
+private:
+    float m_PropertyValue = 0.0f;
+
+public:
+    inline float propertyValue() const { return m_PropertyValue; }
+    void propertyValue(float value)
+    {
+        if (m_PropertyValue == value)
+        {
+            return;
+        }
+        m_PropertyValue = value;
+        propertyValueChanged();
+    }
+
+    Core* clone() const override;
+    void copy(const CustomPropertyNumberBase& object)
+    {
+        m_PropertyValue = object.m_PropertyValue;
+        CustomProperty::copy(object);
+    }
+
+    bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
+    {
+        switch (propertyKey)
+        {
+            case propertyValuePropertyKey:
+                m_PropertyValue = CoreDoubleType::deserialize(reader);
+                return true;
+        }
+        return CustomProperty::deserialize(propertyKey, reader);
+    }
+
+protected:
+    virtual void propertyValueChanged() {}
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/generated/custom_property_string_base.hpp b/include/rive/generated/custom_property_string_base.hpp
new file mode 100644
index 0000000..40c57ca
--- /dev/null
+++ b/include/rive/generated/custom_property_string_base.hpp
@@ -0,0 +1,73 @@
+#ifndef _RIVE_CUSTOM_PROPERTY_STRING_BASE_HPP_
+#define _RIVE_CUSTOM_PROPERTY_STRING_BASE_HPP_
+#include <string>
+#include "rive/core/field_types/core_string_type.hpp"
+#include "rive/custom_property.hpp"
+namespace rive
+{
+class CustomPropertyStringBase : public CustomProperty
+{
+protected:
+    typedef CustomProperty Super;
+
+public:
+    static const uint16_t typeKey = 130;
+
+    /// Helper to quickly determine if a core object extends another without RTTI
+    /// at runtime.
+    bool isTypeOf(uint16_t typeKey) const override
+    {
+        switch (typeKey)
+        {
+            case CustomPropertyStringBase::typeKey:
+            case CustomPropertyBase::typeKey:
+            case ComponentBase::typeKey:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    uint16_t coreType() const override { return typeKey; }
+
+    static const uint16_t propertyValuePropertyKey = 246;
+
+private:
+    std::string m_PropertyValue = "";
+
+public:
+    inline const std::string& propertyValue() const { return m_PropertyValue; }
+    void propertyValue(std::string value)
+    {
+        if (m_PropertyValue == value)
+        {
+            return;
+        }
+        m_PropertyValue = value;
+        propertyValueChanged();
+    }
+
+    Core* clone() const override;
+    void copy(const CustomPropertyStringBase& object)
+    {
+        m_PropertyValue = object.m_PropertyValue;
+        CustomProperty::copy(object);
+    }
+
+    bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
+    {
+        switch (propertyKey)
+        {
+            case propertyValuePropertyKey:
+                m_PropertyValue = CoreStringType::deserialize(reader);
+                return true;
+        }
+        return CustomProperty::deserialize(propertyKey, reader);
+    }
+
+protected:
+    virtual void propertyValueChanged() {}
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/generated/event_base.hpp b/include/rive/generated/event_base.hpp
new file mode 100644
index 0000000..b2d4017
--- /dev/null
+++ b/include/rive/generated/event_base.hpp
@@ -0,0 +1,37 @@
+#ifndef _RIVE_EVENT_BASE_HPP_
+#define _RIVE_EVENT_BASE_HPP_
+#include "rive/container_component.hpp"
+namespace rive
+{
+class EventBase : public ContainerComponent
+{
+protected:
+    typedef ContainerComponent Super;
+
+public:
+    static const uint16_t typeKey = 128;
+
+    /// Helper to quickly determine if a core object extends another without RTTI
+    /// at runtime.
+    bool isTypeOf(uint16_t typeKey) const override
+    {
+        switch (typeKey)
+        {
+            case EventBase::typeKey:
+            case ContainerComponentBase::typeKey:
+            case ComponentBase::typeKey:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    uint16_t coreType() const override { return typeKey; }
+
+    Core* clone() const override;
+
+protected:
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/generated/open_url_event_base.hpp b/include/rive/generated/open_url_event_base.hpp
new file mode 100644
index 0000000..ec53ae7
--- /dev/null
+++ b/include/rive/generated/open_url_event_base.hpp
@@ -0,0 +1,93 @@
+#ifndef _RIVE_OPEN_URL_EVENT_BASE_HPP_
+#define _RIVE_OPEN_URL_EVENT_BASE_HPP_
+#include <string>
+#include "rive/core/field_types/core_string_type.hpp"
+#include "rive/core/field_types/core_uint_type.hpp"
+#include "rive/event.hpp"
+namespace rive
+{
+class OpenUrlEventBase : public Event
+{
+protected:
+    typedef Event Super;
+
+public:
+    static const uint16_t typeKey = 131;
+
+    /// Helper to quickly determine if a core object extends another without RTTI
+    /// at runtime.
+    bool isTypeOf(uint16_t typeKey) const override
+    {
+        switch (typeKey)
+        {
+            case OpenUrlEventBase::typeKey:
+            case EventBase::typeKey:
+            case ContainerComponentBase::typeKey:
+            case ComponentBase::typeKey:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    uint16_t coreType() const override { return typeKey; }
+
+    static const uint16_t urlPropertyKey = 248;
+    static const uint16_t targetValuePropertyKey = 249;
+
+private:
+    std::string m_Url = "";
+    uint32_t m_TargetValue = 0;
+
+public:
+    inline const std::string& url() const { return m_Url; }
+    void url(std::string value)
+    {
+        if (m_Url == value)
+        {
+            return;
+        }
+        m_Url = value;
+        urlChanged();
+    }
+
+    inline uint32_t targetValue() const { return m_TargetValue; }
+    void targetValue(uint32_t value)
+    {
+        if (m_TargetValue == value)
+        {
+            return;
+        }
+        m_TargetValue = value;
+        targetValueChanged();
+    }
+
+    Core* clone() const override;
+    void copy(const OpenUrlEventBase& object)
+    {
+        m_Url = object.m_Url;
+        m_TargetValue = object.m_TargetValue;
+        Event::copy(object);
+    }
+
+    bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
+    {
+        switch (propertyKey)
+        {
+            case urlPropertyKey:
+                m_Url = CoreStringType::deserialize(reader);
+                return true;
+            case targetValuePropertyKey:
+                m_TargetValue = CoreUintType::deserialize(reader);
+                return true;
+        }
+        return Event::deserialize(propertyKey, reader);
+    }
+
+protected:
+    virtual void urlChanged() {}
+    virtual void targetValueChanged() {}
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/open_url_event.hpp b/include/rive/open_url_event.hpp
new file mode 100644
index 0000000..fb8bc0e
--- /dev/null
+++ b/include/rive/open_url_event.hpp
@@ -0,0 +1,13 @@
+#ifndef _RIVE_OPEN_URL_EVENT_HPP_
+#define _RIVE_OPEN_URL_EVENT_HPP_
+#include "rive/generated/open_url_event_base.hpp"
+#include <stdio.h>
+namespace rive
+{
+class OpenUrlEvent : public OpenUrlEventBase
+{
+public:
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/src/animation/listener_fire_event.cpp b/src/animation/listener_fire_event.cpp
new file mode 100644
index 0000000..39d2fd1
--- /dev/null
+++ b/src/animation/listener_fire_event.cpp
@@ -0,0 +1,15 @@
+#include "rive/animation/listener_fire_event.hpp"
+#include "rive/animation/state_machine_instance.hpp"
+#include "rive/event.hpp"
+
+using namespace rive;
+
+void ListenerFireEvent::perform(StateMachineInstance* stateMachineInstance, Vec2D position) const
+{
+    auto coreEvent = stateMachineInstance->artboard()->resolve(eventId());
+    if (coreEvent == nullptr || !coreEvent->is<Event>())
+    {
+        return;
+    }
+    stateMachineInstance->fireEvent(coreEvent->as<Event>());
+}
\ No newline at end of file
diff --git a/src/animation/state_machine_instance.cpp b/src/animation/state_machine_instance.cpp
index 5a1e00d..cbccf6d 100644
--- a/src/animation/state_machine_instance.cpp
+++ b/src/animation/state_machine_instance.cpp
@@ -485,6 +485,7 @@
 
 bool StateMachineInstance::advance(float seconds)
 {
+    m_firedEvents.clear();
     m_NeedsAdvance = false;
     for (size_t i = 0; i < m_LayerCount; i++)
     {
@@ -609,3 +610,16 @@
     }
     return nullptr;
 }
+
+void StateMachineInstance::fireEvent(Event* event) { m_firedEvents.push_back(event); }
+
+std::size_t StateMachineInstance::firedEventCount() const { return m_firedEvents.size(); }
+
+const Event* StateMachineInstance::firedEventAt(std::size_t index) const
+{
+    if (index >= m_firedEvents.size())
+    {
+        return nullptr;
+    }
+    return m_firedEvents[index];
+}
diff --git a/src/generated/animation/listener_fire_event_base.cpp b/src/generated/animation/listener_fire_event_base.cpp
new file mode 100644
index 0000000..e7ae04f
--- /dev/null
+++ b/src/generated/animation/listener_fire_event_base.cpp
@@ -0,0 +1,11 @@
+#include "rive/generated/animation/listener_fire_event_base.hpp"
+#include "rive/animation/listener_fire_event.hpp"
+
+using namespace rive;
+
+Core* ListenerFireEventBase::clone() const
+{
+    auto cloned = new ListenerFireEvent();
+    cloned->copy(*this);
+    return cloned;
+}
diff --git a/src/generated/custom_property_boolean_base.cpp b/src/generated/custom_property_boolean_base.cpp
new file mode 100644
index 0000000..b999ee8
--- /dev/null
+++ b/src/generated/custom_property_boolean_base.cpp
@@ -0,0 +1,11 @@
+#include "rive/generated/custom_property_boolean_base.hpp"
+#include "rive/custom_property_boolean.hpp"
+
+using namespace rive;
+
+Core* CustomPropertyBooleanBase::clone() const
+{
+    auto cloned = new CustomPropertyBoolean();
+    cloned->copy(*this);
+    return cloned;
+}
diff --git a/src/generated/custom_property_number_base.cpp b/src/generated/custom_property_number_base.cpp
new file mode 100644
index 0000000..e1d0dd8
--- /dev/null
+++ b/src/generated/custom_property_number_base.cpp
@@ -0,0 +1,11 @@
+#include "rive/generated/custom_property_number_base.hpp"
+#include "rive/custom_property_number.hpp"
+
+using namespace rive;
+
+Core* CustomPropertyNumberBase::clone() const
+{
+    auto cloned = new CustomPropertyNumber();
+    cloned->copy(*this);
+    return cloned;
+}
diff --git a/src/generated/custom_property_string_base.cpp b/src/generated/custom_property_string_base.cpp
new file mode 100644
index 0000000..c7e27ec
--- /dev/null
+++ b/src/generated/custom_property_string_base.cpp
@@ -0,0 +1,11 @@
+#include "rive/generated/custom_property_string_base.hpp"
+#include "rive/custom_property_string.hpp"
+
+using namespace rive;
+
+Core* CustomPropertyStringBase::clone() const
+{
+    auto cloned = new CustomPropertyString();
+    cloned->copy(*this);
+    return cloned;
+}
diff --git a/src/generated/event_base.cpp b/src/generated/event_base.cpp
new file mode 100644
index 0000000..1829228
--- /dev/null
+++ b/src/generated/event_base.cpp
@@ -0,0 +1,11 @@
+#include "rive/generated/event_base.hpp"
+#include "rive/event.hpp"
+
+using namespace rive;
+
+Core* EventBase::clone() const
+{
+    auto cloned = new Event();
+    cloned->copy(*this);
+    return cloned;
+}
diff --git a/src/generated/open_url_event_base.cpp b/src/generated/open_url_event_base.cpp
new file mode 100644
index 0000000..6c7735d
--- /dev/null
+++ b/src/generated/open_url_event_base.cpp
@@ -0,0 +1,11 @@
+#include "rive/generated/open_url_event_base.hpp"
+#include "rive/open_url_event.hpp"
+
+using namespace rive;
+
+Core* OpenUrlEventBase::clone() const
+{
+    auto cloned = new OpenUrlEvent();
+    cloned->copy(*this);
+    return cloned;
+}
diff --git a/test/assets/event_on_listener.riv b/test/assets/event_on_listener.riv
new file mode 100644
index 0000000..6e2000d
--- /dev/null
+++ b/test/assets/event_on_listener.riv
Binary files differ
diff --git a/test/state_machine_event_test.cpp b/test/state_machine_event_test.cpp
index 00586c0..9ddab3d 100644
--- a/test/state_machine_event_test.cpp
+++ b/test/state_machine_event_test.cpp
@@ -1,19 +1,22 @@
-#include <rive/core/binary_reader.hpp>
-#include <rive/file.hpp>
-#include <rive/animation/state_machine_bool.hpp>
-#include <rive/animation/state_machine_layer.hpp>
-#include <rive/animation/state_machine_listener.hpp>
-#include <rive/animation/animation_state.hpp>
-#include <rive/animation/entry_state.hpp>
-#include <rive/animation/state_transition.hpp>
-#include <rive/animation/state_machine_instance.hpp>
-#include <rive/animation/state_machine_input_instance.hpp>
-#include <rive/animation/blend_state_1d.hpp>
-#include <rive/animation/blend_animation_1d.hpp>
-#include <rive/animation/blend_state_direct.hpp>
-#include <rive/animation/blend_state_transition.hpp>
-#include <rive/animation/listener_input_change.hpp>
-#include <rive/node.hpp>
+#include "rive/core/binary_reader.hpp"
+#include "rive/file.hpp"
+#include "rive/event.hpp"
+#include "rive/shapes/shape.hpp"
+#include "rive/animation/state_machine_bool.hpp"
+#include "rive/animation/state_machine_layer.hpp"
+#include "rive/animation/state_machine_listener.hpp"
+#include "rive/animation/animation_state.hpp"
+#include "rive/animation/entry_state.hpp"
+#include "rive/animation/state_transition.hpp"
+#include "rive/animation/state_machine_instance.hpp"
+#include "rive/animation/state_machine_input_instance.hpp"
+#include "rive/animation/blend_state_1d.hpp"
+#include "rive/animation/blend_animation_1d.hpp"
+#include "rive/animation/blend_state_direct.hpp"
+#include "rive/animation/blend_state_transition.hpp"
+#include "rive/animation/listener_input_change.hpp"
+#include "rive/animation/listener_fire_event.hpp"
+#include "rive/node.hpp"
 #include "catch.hpp"
 #include "rive_file_reader.hpp"
 #include <cstdio>
@@ -115,3 +118,49 @@
     // Got toggled back on after pressing
     REQUIRE(switchButton->value() == true);
 }
+
+TEST_CASE("events load correctly on a listener", "[events]")
+{
+    auto file = ReadRiveFile("../../test/assets/event_on_listener.riv");
+
+    auto artboard = file->artboard()->instance();
+    REQUIRE(artboard != nullptr);
+    REQUIRE(artboard->stateMachineCount() == 1);
+
+    auto stateMachineInstance = artboard->stateMachineAt(0);
+    REQUIRE(stateMachineInstance != nullptr);
+
+    artboard->advance(0.0f);
+    stateMachineInstance->advance(0.0f);
+
+    auto events = artboard->find<rive::Event>();
+    REQUIRE(events.size() == 4);
+
+    REQUIRE(stateMachineInstance->stateMachine()->listenerCount() == 1);
+    auto listener1 = stateMachineInstance->stateMachine()->listener(0);
+    auto target1 = artboard->resolve(listener1->targetId());
+    REQUIRE(target1->is<rive::Shape>());
+    REQUIRE(listener1->actionCount() == 2);
+    auto fireEvent1 = listener1->action(0);
+    REQUIRE(fireEvent1 != nullptr);
+    REQUIRE(fireEvent1->is<rive::ListenerFireEvent>());
+    REQUIRE(fireEvent1->as<rive::ListenerFireEvent>()->eventId() != 0);
+    auto event = artboard->resolve(fireEvent1->as<rive::ListenerFireEvent>()->eventId());
+    REQUIRE(event->is<rive::Event>());
+    REQUIRE(event->as<rive::Event>()->name() == "Footstep");
+
+    REQUIRE(stateMachineInstance->firedEventCount() == 0);
+    stateMachineInstance->pointerDown(rive::Vec2D(343.0f, 116.0f));
+    stateMachineInstance->pointerUp(rive::Vec2D(343.0f, 116.0f));
+
+    // There are two events on the listener.
+    REQUIRE(stateMachineInstance->firedEventCount() == 2);
+    auto firedEvent1 = stateMachineInstance->firedEventAt(0);
+    REQUIRE(firedEvent1->name() == "Footstep");
+    auto firedEvent2 = stateMachineInstance->firedEventAt(1);
+    REQUIRE(firedEvent2->name() == "Event 3");
+
+    // After advancing again the firedEventCount should return to 0.
+    stateMachineInstance->advance(0.0f);
+    REQUIRE(stateMachineInstance->firedEventCount() == 0);
+}