Put input instances into std::vector
diff --git a/include/rive/animation/animation_state_instance.hpp b/include/rive/animation/animation_state_instance.hpp
index 1f03f3f..fe7da61 100644
--- a/include/rive/animation/animation_state_instance.hpp
+++ b/include/rive/animation/animation_state_instance.hpp
@@ -17,7 +17,7 @@
     public:
         AnimationStateInstance(const AnimationState* animationState, ArtboardInstance* instance);
 
-        void advance(float seconds, SMIInput** inputs) override;
+        void advance(float seconds, Span<SMIInput*>) override;
         void apply(float mix) override;
 
         bool keepGoing() const override;
diff --git a/include/rive/animation/blend_state_1d_instance.hpp b/include/rive/animation/blend_state_1d_instance.hpp
index a386074..97cd755 100644
--- a/include/rive/animation/blend_state_1d_instance.hpp
+++ b/include/rive/animation/blend_state_1d_instance.hpp
@@ -14,7 +14,7 @@
 
     public:
         BlendState1DInstance(const BlendState1D* blendState, ArtboardInstance* instance);
-        void advance(float seconds, SMIInput** inputs) override;
+        void advance(float seconds, Span<SMIInput*> inputs) override;
     };
 } // namespace rive
 #endif
\ No newline at end of file
diff --git a/include/rive/animation/blend_state_direct_instance.hpp b/include/rive/animation/blend_state_direct_instance.hpp
index 03b6a54..c4af5dd 100644
--- a/include/rive/animation/blend_state_direct_instance.hpp
+++ b/include/rive/animation/blend_state_direct_instance.hpp
@@ -10,7 +10,7 @@
         : public BlendStateInstance<BlendStateDirect, BlendAnimationDirect> {
     public:
         BlendStateDirectInstance(const BlendStateDirect* blendState, ArtboardInstance* instance);
-        void advance(float seconds, SMIInput** inputs) override;
+        void advance(float seconds, Span<SMIInput*> inputs) override;
     };
 } // namespace rive
 #endif
\ No newline at end of file
diff --git a/include/rive/animation/blend_state_instance.hpp b/include/rive/animation/blend_state_instance.hpp
index 6847d0a..9b1a120 100644
--- a/include/rive/animation/blend_state_instance.hpp
+++ b/include/rive/animation/blend_state_instance.hpp
@@ -46,7 +46,7 @@
 
         bool keepGoing() const override { return m_KeepGoing; }
 
-        void advance(float seconds, SMIInput** inputs) override {
+        void advance(float seconds, Span<SMIInput*>) override {
             m_KeepGoing = false;
             for (auto& animation : m_AnimationInstances) {
                 if (animation.m_AnimationInstance.advance(seconds)) {
diff --git a/include/rive/animation/state_instance.hpp b/include/rive/animation/state_instance.hpp
index f2d1dca..81cbaea 100644
--- a/include/rive/animation/state_instance.hpp
+++ b/include/rive/animation/state_instance.hpp
@@ -3,6 +3,8 @@
 
 #include <string>
 #include <stddef.h>
+#include "rive/rive_types.hpp"
+#include "rive/span.hpp"
 
 namespace rive {
     class LayerState;
@@ -17,7 +19,7 @@
     public:
         StateInstance(const LayerState* layerState);
         virtual ~StateInstance();
-        virtual void advance(float seconds, SMIInput** inputs) = 0;
+        virtual void advance(float seconds, Span<SMIInput*> inputs) = 0;
         virtual void apply(float mix) = 0;
 
         /// Returns true when the State Machine needs to keep advancing this
diff --git a/include/rive/animation/state_machine_instance.hpp b/include/rive/animation/state_machine_instance.hpp
index 1c7ecf2..712b6d7 100644
--- a/include/rive/animation/state_machine_instance.hpp
+++ b/include/rive/animation/state_machine_instance.hpp
@@ -27,17 +27,19 @@
         ArtboardInstance* m_ArtboardInstance;
         bool m_NeedsAdvance = false;
 
-        size_t m_InputCount;
-        SMIInput** m_InputInstances;
+        std::vector<SMIInput*> m_InputInstances;    // we own each pointer
         size_t m_LayerCount;
         StateMachineLayerInstance* m_Layers;
 
         void markNeedsAdvance();
 
-        std::vector<HitShape*> m_HitShapes;
+        std::vector<std::unique_ptr<HitShape>> m_HitShapes;
         /// Provide a hitEvent if you want to process a down or an up for the pointer position too.
         void processEvent(Vec2D position, EventType hitEvent = EventType::updateHover);
 
+        template <typename SMType, typename InstType>
+        InstType* getNamedInput(std::string name) const;
+
     public:
         StateMachineInstance(const StateMachine* machine, ArtboardInstance* instance);
         ~StateMachineInstance();
@@ -52,7 +54,7 @@
         // Returns a pointer to the instance's stateMachine
         const StateMachine* stateMachine() const { return m_Machine; }
 
-        size_t inputCount() const { return m_InputCount; }
+        size_t inputCount() const { return m_InputInstances.size(); }
         SMIInput* input(size_t index) const;
 
         SMIBool* getBool(std::string name) const;
diff --git a/include/rive/animation/state_transition.hpp b/include/rive/animation/state_transition.hpp
index 8c800a5..90fa07f 100644
--- a/include/rive/animation/state_transition.hpp
+++ b/include/rive/animation/state_transition.hpp
@@ -47,7 +47,7 @@
         /// Returns AllowTransition::yes when this transition can be taken from
         /// stateFrom with the given inputs.
         AllowTransition
-        allowed(StateInstance* stateFrom, SMIInput** inputs, bool ignoreTriggers) const;
+        allowed(StateInstance* stateFrom, Span<SMIInput*> inputs, bool ignoreTriggers) const;
 
         /// Whether the animation is held at exit or if it keeps advancing
         /// during mixing.
diff --git a/include/rive/animation/system_state_instance.hpp b/include/rive/animation/system_state_instance.hpp
index c0f76a3..afaa2a9 100644
--- a/include/rive/animation/system_state_instance.hpp
+++ b/include/rive/animation/system_state_instance.hpp
@@ -12,7 +12,7 @@
     public:
         SystemStateInstance(const LayerState* layerState, ArtboardInstance* instance);
 
-        void advance(float seconds, SMIInput** inputs) override;
+        void advance(float seconds, Span<SMIInput*> inputs) override;
         void apply(float mix) override;
 
         bool keepGoing() const override;
diff --git a/src/animation/animation_state_instance.cpp b/src/animation/animation_state_instance.cpp
index d3a7c25..3cd7b15 100644
--- a/src/animation/animation_state_instance.cpp
+++ b/src/animation/animation_state_instance.cpp
@@ -10,7 +10,7 @@
     m_KeepGoing(true)
 {}
 
-void AnimationStateInstance::advance(float seconds, SMIInput** inputs) {
+void AnimationStateInstance::advance(float seconds, Span<SMIInput*>) {
     m_KeepGoing = m_AnimationInstance.advance(seconds);
 }
 
diff --git a/src/animation/blend_state_1d_instance.cpp b/src/animation/blend_state_1d_instance.cpp
index 2f6227b..ce5b7c4 100644
--- a/src/animation/blend_state_1d_instance.cpp
+++ b/src/animation/blend_state_1d_instance.cpp
@@ -30,7 +30,7 @@
     return idx;
 }
 
-void BlendState1DInstance::advance(float seconds, SMIInput** inputs) {
+void BlendState1DInstance::advance(float seconds, Span<SMIInput*> inputs) {
     BlendStateInstance<BlendState1D, BlendAnimation1D>::advance(seconds, inputs);
 
     auto blendState = state()->as<BlendState1D>();
diff --git a/src/animation/blend_state_direct_instance.cpp b/src/animation/blend_state_direct_instance.cpp
index 04dd036..3a46138 100644
--- a/src/animation/blend_state_direct_instance.cpp
+++ b/src/animation/blend_state_direct_instance.cpp
@@ -6,7 +6,7 @@
 BlendStateDirectInstance::BlendStateDirectInstance(const BlendStateDirect* blendState, ArtboardInstance* instance) :
     BlendStateInstance<BlendStateDirect, BlendAnimationDirect>(blendState, instance) {}
 
-void BlendStateDirectInstance::advance(float seconds, SMIInput** inputs) {
+void BlendStateDirectInstance::advance(float seconds, Span<SMIInput*> inputs) {
     BlendStateInstance<BlendStateDirect, BlendAnimationDirect>::advance(seconds, inputs);
     for (auto& animation : m_AnimationInstances) {
         auto inputInstance = inputs[animation.blendAnimation()->inputId()];
diff --git a/src/animation/state_machine_instance.cpp b/src/animation/state_machine_instance.cpp
index 2a5a80b..48a076a 100644
--- a/src/animation/state_machine_instance.cpp
+++ b/src/animation/state_machine_instance.cpp
@@ -74,7 +74,7 @@
             }
         }
 
-        bool advance(/*Artboard* artboard, */ float seconds, SMIInput** inputs, size_t inputCount) {
+        bool advance(/*Artboard* artboard, */float seconds, Span<SMIInput*> inputs) {
             m_StateChangedOnAdvance = false;
 
             if (m_CurrentState != nullptr) {
@@ -109,7 +109,7 @@
                    m_Transition->duration() != 0 && m_Mix < 1.0f;
         }
 
-        bool updateState(SMIInput** inputs, bool ignoreTriggers) {
+        bool updateState(Span<SMIInput*> inputs, bool ignoreTriggers) {
             // Don't allow changing state while a transition is taking place
             // (we're mixing one state onto another).
             if (isTransitioning()) {
@@ -135,7 +135,7 @@
         }
 
         bool
-        tryChangeState(StateInstance* stateFromInstance, SMIInput** inputs, bool ignoreTriggers) {
+        tryChangeState(StateInstance* stateFromInstance, Span<SMIInput*> inputs, bool ignoreTriggers) {
             if (stateFromInstance == nullptr) {
                 return false;
             }
@@ -243,7 +243,7 @@
                         position.y() + hitRadius)
                        .round();
 
-    for (auto hitShape : m_HitShapes) {
+    for (const auto& hitShape : m_HitShapes) {
 
         // TODO: quick reject.
 
@@ -287,13 +287,11 @@
                                            ArtboardInstance* instance) :
     m_Machine(machine), m_ArtboardInstance(instance) {
     assert(instance->isInstance());
-
-    m_InputCount = machine->inputCount();
-    m_InputInstances = new SMIInput*[m_InputCount];
-    for (size_t i = 0; i < m_InputCount; i++) {
+    const auto count = machine->inputCount();
+    m_InputInstances.resize(count);
+    for (size_t i = 0; i < count; i++) {
         auto input = machine->input(i);
         if (input == nullptr) {
-            m_InputInstances[i] = nullptr;
             continue;
         }
         switch (input->coreType()) {
@@ -308,7 +306,6 @@
                 break;
             default:
                 // Sanity check.
-                m_InputInstances[i] = nullptr;
                 break;
         }
     }
@@ -334,8 +331,9 @@
             if (itr == hitShapeLookup.end()) {
                 auto shape = m_ArtboardInstance->resolve(id);
                 if (shape != nullptr && shape->is<Shape>()) {
-                    hitShapeLookup[id] = hitShape = new HitShape(shape->as<Shape>());
-                    m_HitShapes.push_back(hitShape);
+                    auto hs = std::make_unique<HitShape>(shape->as<Shape>());
+                    hitShapeLookup[id] = hitShape = hs.get();
+                    m_HitShapes.push_back(std::move(hs));
                 } else {
                     // No object or not a shape...
                     continue;
@@ -349,27 +347,22 @@
 }
 
 StateMachineInstance::~StateMachineInstance() {
-    for (auto hitShape : m_HitShapes) {
-        delete hitShape;
+    for (auto inst : m_InputInstances) {
+        delete inst;
     }
-    // TODO: can this and others be rewritten as for (auto inst : m_InputInstances) ?
-    for (size_t i = 0; i < m_InputCount; i++) {
-        delete m_InputInstances[i];
-    }
-    delete[] m_InputInstances;
     delete[] m_Layers;
 }
 
 bool StateMachineInstance::advance(float seconds) {
     m_NeedsAdvance = false;
     for (size_t i = 0; i < m_LayerCount; i++) {
-        if (m_Layers[i].advance(seconds, m_InputInstances, m_InputCount)) {
+        if (m_Layers[i].advance(seconds, toSpan(m_InputInstances))) {
             m_NeedsAdvance = true;
         }
     }
 
-    for (size_t i = 0; i < m_InputCount; i++) {
-        m_InputInstances[i]->advanced();
+    for (auto inst : m_InputInstances) {
+        inst->advanced();
     }
 
     return m_NeedsAdvance;
@@ -381,39 +374,31 @@
 std::string StateMachineInstance::name() const { return m_Machine->name(); }
 
 SMIInput* StateMachineInstance::input(size_t index) const {
-    if (index < m_InputCount) {
+    if (index < m_InputInstances.size()) {
         return m_InputInstances[index];
     }
     return nullptr;
 }
 
-SMIBool* StateMachineInstance::getBool(std::string name) const {
-    for (size_t i = 0; i < m_InputCount; i++) {
-        auto input = m_InputInstances[i]->input();
-        if (input->is<StateMachineBool>() && input->name() == name) {
-            return static_cast<SMIBool*>(m_InputInstances[i]);
+template <typename SMType, typename InstType>
+InstType* StateMachineInstance::getNamedInput(std::string name) const {
+    for (const auto inst : m_InputInstances) {
+        auto input = inst->input();
+        if (input->is<SMType>() && input->name() == name) {
+            return static_cast<InstType*>(inst);
         }
     }
     return nullptr;
 }
 
+SMIBool* StateMachineInstance::getBool(std::string name) const {
+    return getNamedInput<StateMachineBool, SMIBool>(name);
+}
 SMINumber* StateMachineInstance::getNumber(std::string name) const {
-    for (size_t i = 0; i < m_InputCount; i++) {
-        auto input = m_InputInstances[i]->input();
-        if (input->is<StateMachineNumber>() && input->name() == name) {
-            return static_cast<SMINumber*>(m_InputInstances[i]);
-        }
-    }
-    return nullptr;
+    return getNamedInput<StateMachineNumber, SMINumber>(name);
 }
 SMITrigger* StateMachineInstance::getTrigger(std::string name) const {
-    for (size_t i = 0; i < m_InputCount; i++) {
-        auto input = m_InputInstances[i]->input();
-        if (input->is<StateMachineTrigger>() && input->name() == name) {
-            return static_cast<SMITrigger*>(m_InputInstances[i]);
-        }
-    }
-    return nullptr;
+    return getNamedInput<StateMachineTrigger, SMITrigger>(name);
 }
 
 size_t StateMachineInstance::stateChangedCount() const {
diff --git a/src/animation/state_transition.cpp b/src/animation/state_transition.cpp
index 2a442ca..24c4b3a 100644
--- a/src/animation/state_transition.cpp
+++ b/src/animation/state_transition.cpp
@@ -102,7 +102,7 @@
 }
 
 AllowTransition
-StateTransition::allowed(StateInstance* stateFrom, SMIInput** inputs, bool ignoreTriggers) const {
+StateTransition::allowed(StateInstance* stateFrom, Span<SMIInput*> inputs, bool ignoreTriggers) const {
     if (isDisabled()) {
         return AllowTransition::no;
     }
diff --git a/src/animation/system_state_instance.cpp b/src/animation/system_state_instance.cpp
index 50906a5..3b6da97 100644
--- a/src/animation/system_state_instance.cpp
+++ b/src/animation/system_state_instance.cpp
@@ -4,7 +4,7 @@
 SystemStateInstance::SystemStateInstance(const LayerState* layerState, ArtboardInstance* instance) :
     StateInstance(layerState) {}
 
-void SystemStateInstance::advance(float seconds, SMIInput** inputs) {}
+void SystemStateInstance::advance(float seconds, Span<SMIInput*>) {}
 void SystemStateInstance::apply(float mix) {}
 
 bool SystemStateInstance::keepGoing() const { return false; }
\ No newline at end of file