Add isTranslucent()
diff --git a/include/rive/artboard.hpp b/include/rive/artboard.hpp
index f098f02..a418d15 100644
--- a/include/rive/artboard.hpp
+++ b/include/rive/artboard.hpp
@@ -74,6 +74,7 @@
         const std::vector<Core*>& objects() const { return m_Objects; }
 
         AABB bounds() const;
+        bool isTranslucent() const;
 
         template <typename T = Component> T* find(std::string name)
         {
@@ -121,4 +122,4 @@
     };
 } // namespace rive
 
-#endif
\ No newline at end of file
+#endif
diff --git a/src/artboard.cpp b/src/artboard.cpp
index dacfaa7..5f8032c 100644
--- a/src/artboard.cpp
+++ b/src/artboard.cpp
@@ -486,6 +486,25 @@
 
 AABB Artboard::bounds() const { return AABB(0.0f, 0.0f, width(), height()); }
 
+bool Artboard::isTranslucent() const {
+    if (clip()) {
+        // can we query to know if the path contains our bounds?
+        // if so, we could effectively ignore it (and not return true)
+        return true;
+    }
+
+    // What does it mean to have a m_FrameOrigin? Does that mean we'll not draw inside
+    // our bounds()? If that's true, will that make the client think we're translucent?
+
+    constexpr float effectlyOpaqueAlpha = 0.999f;   // will turn into 0xFF when drawn
+    for (const auto sp : m_ShapePaints) {
+        if (sp->renderOpacity() >= effectlyOpaqueAlpha) {
+            return false;   // one opaque background fill is all we need to be opaque
+        }
+    }
+    return true;
+}
+
 LinearAnimation* Artboard::firstAnimation() const
 {
     if (m_Animations.empty())
@@ -613,4 +632,4 @@
         backboardImporter->addMissingArtboard();
     }
     return result;
-}
\ No newline at end of file
+}