Try adding C wrappers
diff --git a/dev/test/premake5.lua b/dev/test/premake5.lua
index c0d716b..163155d 100644
--- a/dev/test/premake5.lua
+++ b/dev/test/premake5.lua
@@ -84,8 +84,10 @@
 
 includedirs {"./include", "../../include"}
 
-files {"../../src/**.cpp", -- the Rive runtime source
-"../../test/**.cpp" -- the tests
+files {
+    "../../src/**.cpp",  -- the Rive runtime source
+    "../../test/**.cpp", -- the tests
+    "../../include/capi/**.cpp",
 }
 
 defines {"TESTING", "ENABLE_QUERY_FLAT_VERTICES"}
@@ -97,4 +99,4 @@
 filter "system:windows"
     architecture "x64"
     defines {"_USE_MATH_DEFINES"}
-    buildoptions {WINDOWS_CLANG_CL_SUPPRESSED_WARNINGS}
\ No newline at end of file
+    buildoptions {WINDOWS_CLANG_CL_SUPPRESSED_WARNINGS}
diff --git a/include/capi/rive_api.cpp b/include/capi/rive_api.cpp
new file mode 100644
index 0000000..a3fd406
--- /dev/null
+++ b/include/capi/rive_api.cpp
@@ -0,0 +1,113 @@
+#include "capi/rive_api.h"
+
+#include "rive/file.hpp"
+#include "rive/artboard.hpp"
+#include "rive/span.hpp"
+#include "rive/scene.hpp"
+#include "rive/animation/linear_animation_instance.hpp"
+#include "rive/animation/state_machine_instance.hpp"
+#include "rive/math/aabb.hpp"
+
+static inline rive_file_t* toC(rive::File* file) { return (rive_file_t*)file; }
+static inline rive_artboard_t* toC(rive::ArtboardInstance* abi) { return (rive_artboard_t*)abi; }
+static inline rive_scene_t* toC(rive::Scene* scene) { return (rive_scene_t*)scene; }
+static inline rive_animation_t* toC(rive::LinearAnimationInstance* anim) {
+    return (rive_animation_t*)anim;
+}
+static inline rive_statemachine_t* toC(rive::StateMachineInstance* smi) {
+    return (rive_statemachine_t*)smi;
+}
+
+static inline rive::Factory* toRive(rive_factory_t* factory) { return (rive::Factory*)factory; }
+static inline rive::Renderer* toRive(rive_renderer_t* renderer) { return (rive::Renderer*)renderer; }
+
+static inline rive::File* toRive(rive_file_t* file) { return (rive::File*)file; }
+static inline rive::ArtboardInstance* toRive(rive_artboard_t* abi) { return (rive::ArtboardInstance*)abi; }
+static inline rive::Scene* toRive(rive_scene_t* scene) { return (rive::Scene*)scene; }
+
+#if 0
+static inline rive_factory_t* toC(rive::Factory* factory) { return (rive_factory_t*)factory; }
+static inline rive_renderer_t* toC(rive::Renderer* renderer) { return (rive_renderer_t*)renderer; }
+static inline rive::LinearAnimationInstance* toRive(rive_animation_t* anim) {
+    return (rive::LinearAnimationInstance*)anim;
+}
+static inline rive::StateMachineInstance* toRive(rive_statemachine_t* smi) {
+    return (rive::StateMachineInstance*)smi;
+}
+#endif
+
+//////////////////////////////////////
+
+void rive_file_delete(rive_file_t* file) { delete toRive(file); }
+void rive_artboard_delete(rive_artboard_t* abi) { delete toRive(abi); }
+void rive_scene_delete(rive_scene_t* scene) { delete toRive(scene); }
+
+rive_file_t* rive_file_import(rive_span_t cspan, rive_factory_t* factory) {
+    rive::Span<const uint8_t> span((const uint8_t*)cspan.buffer, cspan.size);
+    return toC(rive::File::import(span, toRive(factory)).release());
+}
+
+int32_t rive_file_artboard_count(rive_file_t* file) {
+    return toRive(file)->artboardCount();
+}
+
+rive_artboard_t* rive_file_artboard_default(rive_file_t* file) {
+    return toC(toRive(file)->artboardDefault().release());
+}
+
+rive_artboard_t* rive_file_artboard_at(rive_file_t* file, int32_t index) {
+    return toC(toRive(file)->artboardAt(index).release());
+}
+
+rive_artboard_t* rive_file_artboard_named(rive_file_t* file, const char name[]) {
+    return toC(toRive(file)->artboardNamed(name).release());
+}
+
+///////////////////////////
+
+int32_t rive_artboard_animation_count(rive_artboard_t* abi) {
+    return toRive(abi)->animationCount();
+}
+
+rive_animation_t* rive_artboard_animation_at(rive_artboard_t* abi, int32_t index) {
+    return toC(toRive(abi)->animationAt(index).release());
+}
+
+rive_animation_t* rive_artboard_animation_named(rive_artboard_t* abi, const char name[]) {
+    return toC(toRive(abi)->animationNamed(name).release());
+}
+
+int32_t rive_artboard_statemachine_count(rive_artboard_t* abi) {
+    return toRive(abi)->stateMachineCount();
+}
+
+rive_statemachine_t* rive_artboard_statemachine_at(rive_artboard_t* abi, int32_t index) {
+    return toC(toRive(abi)->stateMachineAt(index).release());
+}
+
+rive_statemachine_t* rive_artboard_statemachine_named(rive_artboard_t* abi, const char name[]) {
+    return toC(toRive(abi)->stateMachineNamed(name).release());
+}
+
+rive_statemachine_t* rive_artboard_statemachine_default(rive_artboard_t* abi) {
+    return toC(toRive(abi)->defaultStateMachine().release());
+}
+
+rive_scene_t* rive_artboard_scene_default(rive_artboard_t* abi) {
+    return toC(toRive(abi)->defaultScene().release());
+}
+
+////////////////////////////////
+
+rive_aabb_t rive_scene_bounds(rive_scene_t* scene) {
+    auto aabb = toRive(scene)->bounds();
+    return {aabb.left(), aabb.top(), aabb.right(), aabb.bottom()};
+}
+
+void rive_scene_draw(rive_scene_t* scene, rive_renderer_t* renderer) {
+    toRive(scene)->draw(toRive(renderer));
+}
+
+bool rive_scene_advance(rive_scene_t* scene, float seconds) {
+    return toRive(scene)->advanceAndApply(seconds);
+}
diff --git a/include/capi/rive_api.h b/include/capi/rive_api.h
new file mode 100644
index 0000000..afb8697
--- /dev/null
+++ b/include/capi/rive_api.h
@@ -0,0 +1,36 @@
+#ifndef _RIVE_FILE_H_
+#define _RIVE_FILE_H_
+
+#include "capi/rive_types.h"
+
+RIVE_C_PLUS_PLUS_BEGIN_GUARD
+
+rive_file_t* rive_file_import(rive_span_t, rive_factory_t*);
+
+int32_t rive_file_artboard_count(rive_file_t*);
+rive_artboard_t* rive_file_artboard_default(rive_file_t*);
+rive_artboard_t* rive_file_artboard_at(rive_file_t*, int32_t index);
+rive_artboard_t* rive_file_artboard_named(rive_file_t*, const char name[]);
+
+rive_statemachine_t* rive_artboard_statemachine_default(rive_artboard_t*);
+rive_scene_t* rive_artboard_scene_default(rive_artboard_t*);
+
+int32_t rive_artboard_animation_count(rive_artboard_t*);
+rive_animation_t* rive_artboard_animation_at(rive_artboard_t*, int32_t index);
+rive_animation_t* rive_artboard_animation_named(rive_artboard_t*, const char name[]);
+
+int32_t rive_artboard_count_statemachines(rive_artboard_t*);
+rive_statemachine_t* rive_artboard_statemachine_at(rive_artboard_t*, int32_t index);
+rive_statemachine_t* rive_artboard_statemachine_named(rive_artboard_t*, const char name[]);
+
+rive_aabb_t rive_scene_bounds(rive_scene_t*);
+void rive_scene_draw(rive_scene_t*, rive_renderer_t*);
+bool rive_scene_advance(rive_scene_t*, float seconds);
+
+void rive_file_delete(rive_file_t*);
+void rive_artboard_delete(rive_artboard_t*);
+void rive_scene_delete(rive_scene_t*);
+
+RIVE_C_PLUS_PLUS_END_GUARD
+
+#endif
diff --git a/include/capi/rive_types.h b/include/capi/rive_types.h
new file mode 100644
index 0000000..89f6d32
--- /dev/null
+++ b/include/capi/rive_types.h
@@ -0,0 +1,48 @@
+#ifndef _RIVE_TYPES_H_
+#define _RIVE_TYPES_H_
+
+#include <stdint.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+    #define RIVE_C_PLUS_PLUS_BEGIN_GUARD    extern "C" {
+    #define RIVE_C_PLUS_PLUS_END_GUARD      }
+#else
+    #include <stdbool.h>
+    #define RIVE_C_PLUS_PLUS_BEGIN_GUARD
+    #define RIVE_C_PLUS_PLUS_END_GUARD
+#endif
+
+RIVE_C_PLUS_PLUS_BEGIN_GUARD
+
+typedef uint32_t rive_colorint_t;
+
+typedef struct {
+    float x, y;
+} rive_vec2d_t;
+
+typedef struct {
+    float left, top, right, bottom;
+} rive_aabb_t;
+
+typedef struct {
+    float mat[6];
+} rive_mat2d_t;
+
+typedef struct {
+    void* buffer;
+    size_t size;
+} rive_span_t;
+
+typedef struct rive_file_t rive_file_t;
+typedef struct rive_artboard_t rive_artboard_t;
+typedef struct rive_scene_t rive_scene_t;
+typedef struct rive_animation_t rive_animation_t;
+typedef struct rive_statemachine_t rive_statemachine_t;
+
+typedef struct rive_factory_t rive_factory_t;
+typedef struct rive_renderer_t rive_renderer_t;
+
+RIVE_C_PLUS_PLUS_END_GUARD
+
+#endif
diff --git a/test/capi_test.cpp b/test/capi_test.cpp
new file mode 100644
index 0000000..a7c5a4c
--- /dev/null
+++ b/test/capi_test.cpp
@@ -0,0 +1,30 @@
+#include "capi/rive_api.h"
+
+#include "no_op_factory.hpp"
+#include <catch.hpp>
+#include <cstdio>
+
+static rive::NoOpFactory gNoOpFactory;
+
+static rive_file_t* loadfile(const char path[]) {
+    FILE* fp = fopen(path, "rb");
+    REQUIRE(fp != nullptr);
+
+    fseek(fp, 0, SEEK_END);
+    const size_t length = ftell(fp);
+    fseek(fp, 0, SEEK_SET);
+    std::vector<uint8_t> bytes(length);
+    REQUIRE(fread(bytes.data(), 1, length, fp) == length);
+    fclose(fp);
+
+    rive_span_t span = {bytes.data(), length};
+    return rive_file_import(span, (rive_factory_t*)&gNoOpFactory);
+}
+
+TEST_CASE("capi", "[file]") {
+    auto file = loadfile("../../test/assets/two_artboards.riv");
+    
+    REQUIRE(rive_file_artboard_count(file) == 2);
+
+    rive_file_delete(file);
+}