diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,5 @@
 # Changelog for keid-resource-gltf
 
-## Unreleased changes
+## 0.1.0.1
+
+- Switch to block-derived Storable instance.
diff --git a/keid-resource-gltf.cabal b/keid-resource-gltf.cabal
--- a/keid-resource-gltf.cabal
+++ b/keid-resource-gltf.cabal
@@ -1,11 +1,11 @@
 cabal-version: 1.12
 
--- This file has been generated from package.yaml by hpack version 0.34.4.
+-- This file has been generated from package.yaml by hpack version 0.35.1.
 --
 -- see: https://github.com/sol/hpack
 
 name:           keid-resource-gltf
-version:        0.1.0.0
+version:        0.1.0.1
 synopsis:       GLTF loader for Keid engine.
 category:       Game Engine
 author:         IC Rainbow
@@ -93,12 +93,8 @@
       base >=4.7 && <5
     , bytestring
     , containers
-    , derive-storable
-    , derive-storable-plugin
-    , geomancy
+    , geomancy >=0.2.5.0
     , gltf-codec
     , keid-core
     , rio >=0.1.12.0
-    , rio-app
-    , vulkan
   default-language: Haskell2010
diff --git a/src/Resource/Gltf/Load.hs b/src/Resource/Gltf/Load.hs
--- a/src/Resource/Gltf/Load.hs
+++ b/src/Resource/Gltf/Load.hs
@@ -68,11 +68,15 @@
 
 -- XXX: OTOH, it may be better to unfold scene first 🤔
 loadMeshPrimitives
-  :: HasLogFunc env
+  :: ( MonadReader env m
+     , HasLogFunc env
+     , MonadThrow m
+     , MonadUnliftIO m
+     )
   => Bool
   -> Bool
   -> FilePath
-  -> RIO env
+  -> m
     ( Root.GlTF
     , Vector (Vector MeshPrimitive)
     )
@@ -268,14 +272,18 @@
   pure (root, meshPrimitives)
 
 accessBuffer
-  :: forall a env . (Storable a)
-  => (Accessor.AccessorIx -> RIO env Accessor.Accessor)
-  -> (BufferView.BufferViewIx -> RIO env BufferView.BufferView)
-  -> (Buffer.BufferIx -> RIO env ByteString)
+  :: forall a m
+  .  ( MonadThrow m
+     , MonadIO m
+     , Storable a
+     )
+  => (Accessor.AccessorIx -> m Accessor.Accessor)
+  -> (BufferView.BufferViewIx -> m BufferView.BufferView)
+  -> (Buffer.BufferIx -> m ByteString)
   -> Accessor.AttributeType
   -> Accessor.ComponentType
   -> Accessor.AccessorIx
-  -> RIO env [a]
+  -> m [a]
 accessBuffer getAccessor getBufferView getBuffer expectAttribute expectComponent aix = do
   Accessor.Accessor{bufferView, byteOffset=accOffset, componentType, count, type'} <- getAccessor aix
 
@@ -304,11 +312,11 @@
     Foreign.peekArray count . Foreign.castPtr
 
 unexpected
-  :: (Eq e, Exception exception)
+  :: (Eq e, Exception exception, MonadThrow m)
   => (e -> e -> exception)
   -> e
   -> e
-  -> RIO env ()
+  -> m ()
 unexpected cons expected got =
   unless (expected == got) $
     throwM $ cons expected got
diff --git a/src/Resource/Gltf/Model.hs b/src/Resource/Gltf/Model.hs
--- a/src/Resource/Gltf/Model.hs
+++ b/src/Resource/Gltf/Model.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE DeriveAnyClass #-}
+
 module Resource.Gltf.Model
   ( Mesh
   , MeshPrimitive
@@ -15,9 +17,10 @@
 import RIO
 
 import Codec.GlTF.Material qualified as GlTF (Material)
-import Foreign qualified
 import Data.Semigroup (Semigroup(..))
 import Geomancy (Vec2)
+import Geomancy.Gl.Block (Block)
+import Geomancy.Gl.Block qualified as Block
 import Geomancy.Vec3 qualified as Vec3
 import RIO.List qualified as List
 import RIO.Vector qualified as Vector
@@ -103,20 +106,6 @@
   { vaTexCoord :: Vec2
   , vaNormal   :: Vec3.Packed
   , vaTangent  :: Vec3.Packed
-  } deriving (Eq, Ord, Show)
-
-instance Storable VertexAttrs where
-  alignment ~_ = 16
-
-  sizeOf ~_ = 8 + 12 + 12
-
-  peek ptr = do
-    vaTexCoord <- Foreign.peekByteOff ptr  0 -- +8
-    vaNormal   <- Foreign.peekByteOff ptr  8 -- +12
-    vaTangent  <- Foreign.peekByteOff ptr 20 -- +12
-    pure VertexAttrs{..}
-
-  poke ptr VertexAttrs{..} = do
-    Foreign.pokeByteOff ptr  0 vaTexCoord
-    Foreign.pokeByteOff ptr  8 vaNormal
-    Foreign.pokeByteOff ptr 20 vaTangent
+  }
+  deriving (Eq, Ord, Show, Generic, Block)
+  deriving Storable via (Block.Packed VertexAttrs)
