Expose artboard instances directly on File
diff --git a/include/rive/artboard.hpp b/include/rive/artboard.hpp
index 0315a82..ad19de3 100644
--- a/include/rive/artboard.hpp
+++ b/include/rive/artboard.hpp
@@ -24,6 +24,7 @@
     class NestedArtboard;
     class ArtboardInstance;
     class LinearAnimationInstance;
+    class StateMachineInstance;
 
     class Artboard : public ArtboardBase, public CoreContext, public ShapePaintContainer {
         friend class File;
@@ -123,18 +124,23 @@
             return nullptr;
         }
 
+        size_t animationCount() const { return m_Animations.size(); }
+        std::string animationNameAt(size_t index) const;
+
+        size_t stateMachineCount() const { return m_StateMachines.size(); }
+        std::string stateMachineNameAt(size_t index) const;
+
         LinearAnimation* firstAnimation() const;
         LinearAnimation* animation(std::string name) const;
         LinearAnimation* animation(size_t index) const;
-        size_t animationCount() const { return m_Animations.size(); }
 
         StateMachine* firstStateMachine() const;
         StateMachine* stateMachine(std::string name) const;
         StateMachine* stateMachine(size_t index) const;
-        size_t stateMachineCount() const { return m_StateMachines.size(); }
 
         /// Make an instance of this artboard, must be explictly deleted when no
         /// longer needed.
+        // Deprecated...
         std::unique_ptr<ArtboardInstance> instance() const;
 
         /// Returns true if the artboard is an instance of another
@@ -157,6 +163,11 @@
 
     class ArtboardInstance : public Artboard {
     public:
+        std::unique_ptr<LinearAnimationInstance> animationAt(size_t index);
+        std::unique_ptr<LinearAnimationInstance> animationNamed(std::string name);
+
+        std::unique_ptr<StateMachineInstance> stateMachineAt(size_t index);
+        std::unique_ptr<StateMachineInstance> stateMachineNamed(std::string name);
     };
 } // namespace rive
 
diff --git a/include/rive/file.hpp b/include/rive/file.hpp
index 29f28e2..ab85e86 100644
--- a/include/rive/file.hpp
+++ b/include/rive/file.hpp
@@ -66,8 +66,15 @@
         /// @returns the file's backboard. All files have exactly one backboard.
         Backboard* backboard() const { return m_Backboard.get(); }
 
-        /// @returns the default artboard. This is typically the first artboard
-        /// found in the file's artboard list.
+        /// @returns the number of artboards in the file.
+        size_t artboardCount() const { return m_Artboards.size(); }
+        std::string artboardNameAt(size_t index) const;
+
+        // Instances
+        std::unique_ptr<ArtboardInstance> artboardDefault() const;
+        std::unique_ptr<ArtboardInstance> artboardAt(size_t index) const;
+        std::unique_ptr<ArtboardInstance> artboardNamed(std::string name) const;
+
         Artboard* artboard() const;
 
         /// @returns the named artboard. If no artboard is found with that name,
@@ -78,9 +85,6 @@
         /// index is out of range.
         Artboard* artboard(size_t index) const;
 
-        /// @returns the number of artboards in the file.
-        size_t artboardCount() const { return m_Artboards.size(); }
-
     private:
         ImportResult read(BinaryReader& reader, const RuntimeHeader& header);
     };
diff --git a/skia/thumbnail_generator/src/main.cpp b/skia/thumbnail_generator/src/main.cpp
index 226b2ed..e17b9f1 100644
--- a/skia/thumbnail_generator/src/main.cpp
+++ b/skia/thumbnail_generator/src/main.cpp
@@ -55,7 +55,7 @@
         fprintf(stderr, "Failed to read rive file.\n");
         return 1;
     }
-    auto artboard = file->artboard();
+    auto artboard = file->artboardDefault();
     artboard->advance(0.0f);
 
     delete[] bytes;
diff --git a/skia/viewer/src/main.cpp b/skia/viewer/src/main.cpp
index 6136e5f..d7cf24c 100644
--- a/skia/viewer/src/main.cpp
+++ b/skia/viewer/src/main.cpp
@@ -36,6 +36,13 @@
 std::unique_ptr<rive::ArtboardInstance> artboardInstance;
 std::unique_ptr<rive::StateMachineInstance> stateMachineInstance;
 std::unique_ptr<rive::LinearAnimationInstance> animationInstance;
+
+// ImGui wants raw pointers to names, but our public API returns
+// names as strings (by value), so we cache these names each time we
+// load a file
+std::vector<std::string> animationNames;
+std::vector<std::string> stateMachineNames;
+
 // We hold onto the file's bytes for the lifetime of the file, in case we want
 // to change animations or state-machines, we just rebuild the rive::File from
 // it.
@@ -44,6 +51,19 @@
 int animationIndex = 0;
 int stateMachineIndex = -1;
 
+static void loadNames(const rive::Artboard* ab) {
+    animationNames.clear();
+    stateMachineNames.clear();
+    if (ab) {
+        for (size_t i = 0; i < ab->animationCount(); ++i) {
+            animationNames.push_back(ab->animationNameAt(i));
+        }
+        for (size_t i = 0; i < ab->stateMachineCount(); ++i) {
+            stateMachineNames.push_back(ab->stateMachineNameAt(i));
+        }
+    }
+}
+
 void initStateMachine(int index) {
     stateMachineIndex = index;
     animationIndex = -1;
@@ -60,11 +80,12 @@
     artboardInstance = nullptr;
 
     currentFile = std::move(file);
-    artboardInstance = currentFile->artboard()->instance();
+    artboardInstance = currentFile->artboardDefault();
     artboardInstance->advance(0.0f);
+    loadNames(artboardInstance.get());
 
     if (index >= 0 && index < artboardInstance->stateMachineCount()) {
-        stateMachineInstance = std::make_unique<rive::StateMachineInstance>(artboardInstance->stateMachine(index), artboardInstance.get());
+        stateMachineInstance = artboardInstance->stateMachineAt(index);
     }
 }
 
@@ -84,11 +105,12 @@
     artboardInstance = nullptr;
 
     currentFile = std::move(file);
-    artboardInstance = currentFile->artboard()->instance();
+    artboardInstance = currentFile->artboardDefault();
     artboardInstance->advance(0.0f);
+    loadNames(artboardInstance.get());
 
     if (index >= 0 && index < artboardInstance->animationCount()) {
-        animationInstance = std::make_unique<rive::LinearAnimationInstance>(artboardInstance->animation(index), artboardInstance.get());
+        animationInstance = artboardInstance->animationAt(index);
     }
 }
 
@@ -298,12 +320,11 @@
                     "Animations",
                     &animationIndex,
                     [](void* data, int index, const char** name) {
-                        const char* animationName = artboardInstance->animation(index)->name().c_str();
-                        *name = animationName;
+                        *name = animationNames[index].c_str();
                         return true;
                     },
                     artboardInstance.get(),
-                    artboardInstance->animationCount(),
+                    animationNames.size(),
                     4))
             {
                 stateMachineIndex = -1;
@@ -313,12 +334,11 @@
                     "State Machines",
                     &stateMachineIndex,
                     [](void* data, int index, const char** name) {
-                        const char* machineName = artboardInstance->stateMachine(index)->name().c_str();
-                        *name = machineName;
+                        *name = stateMachineNames[index].c_str();
                         return true;
                     },
                     artboardInstance.get(),
-                    artboardInstance->stateMachineCount(),
+                    stateMachineNames.size(),
                     4))
             {
                 animationIndex = -1;
diff --git a/src/artboard.cpp b/src/artboard.cpp
index 77d2122..7402623 100644
--- a/src/artboard.cpp
+++ b/src/artboard.cpp
@@ -450,6 +450,16 @@
     return this->isTranslucent(inst->animation());
 }
 
+std::string Artboard::animationNameAt(size_t index) const {
+    auto la = this->animation(index);
+    return la ? la->name() : nullptr;
+}
+
+std::string Artboard::stateMachineNameAt(size_t index) const {
+    auto sm = this->stateMachine(index);
+    return sm ? sm->name() : nullptr;
+}
+
 LinearAnimation* Artboard::firstAnimation() const {
     if (m_Animations.empty()) {
         return nullptr;
@@ -609,3 +619,32 @@
         return true;
     }
 }
+
+////////// ArtboardInstance
+
+#include "rive/animation/linear_animation_instance.hpp"
+#include "rive/animation/state_machine_instance.hpp"
+
+std::unique_ptr<LinearAnimationInstance>
+ArtboardInstance::animationAt(size_t index) {
+    auto la = this->animation(index);
+    return la ? std::make_unique<LinearAnimationInstance>(la, this) : nullptr;
+}
+
+std::unique_ptr<LinearAnimationInstance>
+ArtboardInstance::animationNamed(std::string name) {
+    auto la = this->animation(name);
+    return la ? std::make_unique<LinearAnimationInstance>(la, this) : nullptr;
+}
+
+std::unique_ptr<StateMachineInstance>
+ArtboardInstance::stateMachineAt(size_t index) {
+    auto sm = this->stateMachine(index);
+    return sm ? std::make_unique<StateMachineInstance>(sm, this) : nullptr;
+}
+
+std::unique_ptr<StateMachineInstance>
+ArtboardInstance::stateMachineNamed(std::string name) {
+    auto sm = this->stateMachine(name);
+    return sm ? std::make_unique<StateMachineInstance>(sm, this) : nullptr;
+}
diff --git a/src/file.cpp b/src/file.cpp
index eb4577d..bf5bd04 100644
--- a/src/file.cpp
+++ b/src/file.cpp
@@ -257,3 +257,23 @@
     }
     return m_Artboards[index].get();
 }
+
+std::string File::artboardNameAt(size_t index) const {
+    auto ab = this->artboard(index);
+    return ab ? ab->name() : "";
+}
+
+std::unique_ptr<ArtboardInstance> File::artboardDefault() const {
+    auto ab = this->artboard();
+    return ab ? ab->instance() : nullptr;
+}
+
+std::unique_ptr<ArtboardInstance> File::artboardAt(size_t index) const {
+    auto ab = this->artboard(index);
+    return ab ? ab->instance() : nullptr;
+}
+
+std::unique_ptr<ArtboardInstance> File::artboardNamed(std::string name) const {
+    auto ab = this->artboard(name);
+    return ab ? ab->instance() : nullptr;
+}
diff --git a/test/image_mesh_test.cpp b/test/image_mesh_test.cpp
index d1575f2..6b817ed 100644
--- a/test/image_mesh_test.cpp
+++ b/test/image_mesh_test.cpp
@@ -31,9 +31,9 @@
     RiveFileReader reader("../../test/assets/tape.riv");
     auto file = reader.file();
 
-    auto instance1 = file->artboard()->instance();
-    auto instance2 = file->artboard()->instance();
-    auto instance3 = file->artboard()->instance();
+    auto instance1 = file->artboardDefault();
+    auto instance2 = file->artboardDefault();
+    auto instance3 = file->artboardDefault();
 
     auto node1 = instance1->find("Tape body.png");
     auto node2 = instance2->find("Tape body.png");
diff --git a/test/instancing_test.cpp b/test/instancing_test.cpp
index 40fe2a5..2d8672a 100644
--- a/test/instancing_test.cpp
+++ b/test/instancing_test.cpp
@@ -50,7 +50,7 @@
     REQUIRE(file->artboard() != nullptr);
     REQUIRE(!file->artboard()->isInstance());
 
-    auto artboard = file->artboard()->instance();
+    auto artboard = file->artboardDefault();
 
     REQUIRE(artboard->isInstance());
 
@@ -86,7 +86,7 @@
     REQUIRE(file != nullptr);
     REQUIRE(file->artboard() != nullptr);
 
-    auto artboard = file->artboard()->instance();
+    auto artboard = file->artboardDefault();
 
     REQUIRE(file->artboard()->animationCount() == artboard->animationCount());
     REQUIRE(file->artboard()->firstAnimation() == artboard->firstAnimation());