diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+1.6.1.0 [2026-02-10]
+--------------------
+* Create Mini.Hash.Class: The class of hashable types
+* Create Mini.Hash.Murmur32: An implementation of MurmurHash3_x86_32 supporting
+  incremental addition
+
 1.6.0.0 [2025-04-27]
 --------------------
 * Mini.Data.Recursion:
diff --git a/Mini/Hash/Class.hs b/Mini/Hash/Class.hs
new file mode 100644
--- /dev/null
+++ b/Mini/Hash/Class.hs
@@ -0,0 +1,100 @@
+-- | The class of hashable types
+module Mini.Hash.Class (
+  -- * Class
+  Hashable (
+    toBytes
+  ),
+) where
+
+import Data.Bits (
+  FiniteBits,
+  finiteBitSize,
+  shiftR,
+ )
+import Data.Int (
+  Int,
+  Int16,
+  Int32,
+  Int64,
+  Int8,
+ )
+import Data.Word (
+  Word,
+  Word16,
+  Word32,
+  Word64,
+  Word8,
+ )
+import Prelude (
+  Bool,
+  Char,
+  Enum,
+  Integral,
+  concatMap,
+  div,
+  fmap,
+  fromEnum,
+  fromIntegral,
+  iterate,
+  pure,
+  take,
+  ($),
+  (.),
+ )
+
+-- Class
+
+-- | Instances should use little-endian byte order
+class Hashable a where
+  -- | Convert a hashable type to a sequence of bytes
+  toBytes :: a -> [Word8]
+
+instance Hashable Bool where
+  toBytes = enumToBytes
+
+instance Hashable Char where
+  toBytes = enumToBytes
+
+instance Hashable Int where
+  toBytes = finiteBitsIntegralToBytes
+
+instance Hashable Int8 where
+  toBytes = finiteBitsIntegralToBytes
+
+instance Hashable Int16 where
+  toBytes = finiteBitsIntegralToBytes
+
+instance Hashable Int32 where
+  toBytes = finiteBitsIntegralToBytes
+
+instance Hashable Int64 where
+  toBytes = finiteBitsIntegralToBytes
+
+instance Hashable Word where
+  toBytes = finiteBitsIntegralToBytes
+
+instance Hashable Word8 where
+  toBytes = pure
+
+instance Hashable Word16 where
+  toBytes = finiteBitsIntegralToBytes
+
+instance Hashable Word32 where
+  toBytes = finiteBitsIntegralToBytes
+
+instance Hashable Word64 where
+  toBytes = finiteBitsIntegralToBytes
+
+instance (Hashable a) => Hashable [a] where
+  toBytes = concatMap toBytes
+
+-- Helpers
+
+enumToBytes :: (Enum a) => a -> [Word8]
+enumToBytes = pure . fromIntegral . fromEnum
+
+finiteBitsIntegralToBytes :: (FiniteBits a, Integral a) => a -> [Word8]
+finiteBitsIntegralToBytes w =
+  take (finiteBitSize w `div` 8)
+    . fmap fromIntegral
+    $ iterate (`shiftR` 8) w
diff --git a/Mini/Hash/Murmur32.hs b/Mini/Hash/Murmur32.hs
new file mode 100644
--- /dev/null
+++ b/Mini/Hash/Murmur32.hs
@@ -0,0 +1,136 @@
+-- | An implementation of MurmurHash3_x86_32 supporting incremental addition
+module Mini.Hash.Murmur32 (
+  -- * Type
+  Hash32,
+
+  -- * Construction
+  seed,
+
+  -- * Operations
+  add,
+  digest,
+) where
+
+import Data.Bits (
+  rotateL,
+  shiftL,
+  shiftR,
+  xor,
+ )
+import Data.Function (
+  on,
+ )
+import Data.Word (
+  Word32,
+  Word8,
+ )
+import Mini.Data.Recursion (
+  bool,
+  uncurry3,
+ )
+import Mini.Hash.Class (
+  Hashable,
+  toBytes,
+ )
+import Numeric (
+  showHex,
+ )
+import Prelude (
+  Eq,
+  Ord,
+  Show,
+  compare,
+  foldr,
+  fromIntegral,
+  null,
+  showsPrec,
+  ($),
+  (*),
+  (+),
+  (.),
+  (<>),
+  (==),
+ )
+
+-- Type
+
+-- | Abstract representation of a 32-bit hash value
+data Hash32 = Hash32 Word32 Word32 [Word8]
+
+instance Eq Hash32 where
+  (==) = (==) `on` digest
+
+instance Ord Hash32 where
+  compare = compare `on` digest
+
+instance Show Hash32 where
+  showsPrec _ = showHex . digest
+
+-- Construction
+
+-- | Make a hash from a seed
+seed :: Word32 -> Hash32
+seed s = Hash32 s 0 []
+
+-- Operations
+
+-- | Add a hashable type to a hash
+add :: (Hashable a) => a -> Hash32 -> Hash32
+add a = addBytes $ toBytes a
+
+-- | Get the digest of a hash
+digest :: Hash32 -> Word32
+digest (Hash32 h1 len t) =
+  let (k0, tlen) =
+        foldr
+          (\a (b, n) -> (fromIntegral a `xor` (b `shiftL` 8), n + 1))
+          (0, 0)
+          t
+      len' = len + tlen
+      k1 = k0 * 0xcc9e2d51
+      k2 = k1 `rotateL` 15
+      k3 = k2 * 0x1b873593
+      h2 = h1 `xor` k3
+   in mix
+        . bool
+          (h2 `xor` len')
+          (h1 `xor` len)
+        $ null t
+
+-- Helpers
+
+addBytes :: [Word8] -> Hash32 -> Hash32
+addBytes new (Hash32 h0 len0 old) = uncurry3 Hash32 $ go h0 len0 (old <> new)
+ where
+  go h len (a : b : c : d : rest) =
+    let d' = fromIntegral d `shiftL` 24
+        c' = fromIntegral c `shiftL` 16
+        b' = fromIntegral b `shiftL` 8
+        a' = fromIntegral a
+        w = a' `xor` b' `xor` c' `xor` d'
+        h' = murmur w h
+        len' = len + 4
+     in go h' len' rest
+  go h len bs = (h, len, bs)
+
+murmur :: Word32 -> Word32 -> Word32
+murmur k1 h1 =
+  let c1 = 0xcc9e2d51
+      c2 = 0x1b873593
+      c3 = 0xe6546b64
+      k2 = k1 * c1
+      k3 = k2 `rotateL` 15
+      k4 = k3 * c2
+      h2 = h1 `xor` k4
+      h3 = h2 `rotateL` 13
+      h4 = h3 * 5 + c3
+   in h4
+
+mix :: Word32 -> Word32
+mix h1 =
+  let h2 = h1 `xor` (h1 `shiftR` 16)
+      h3 = h2 * 0x85ebca6b
+      h4 = h3 `xor` (h3 `shiftR` 13)
+      h5 = h4 * 0xc2b2ae35
+      h6 = h5 `xor` (h5 `shiftR` 16)
+   in h6
diff --git a/Mini/Transformers/EitherT.hs b/Mini/Transformers/EitherT.hs
--- a/Mini/Transformers/EitherT.hs
+++ b/Mini/Transformers/EitherT.hs
@@ -57,7 +57,7 @@
 -- | A terminable transformer with termination /e/, inner monad /m/, return /a/
 newtype EitherT e m a = EitherT
   { runEitherT :: m (Either e a)
-  -- ^ Unwrap an 'EitherT' computation
+  -- ^ Unwrap a transformer computation
   }
 
 instance (Monad m) => Functor (EitherT e m) where
diff --git a/Mini/Transformers/MaybeT.hs b/Mini/Transformers/MaybeT.hs
--- a/Mini/Transformers/MaybeT.hs
+++ b/Mini/Transformers/MaybeT.hs
@@ -55,7 +55,7 @@
 -- | A terminable transformer with inner monad /m/, return /a/
 newtype MaybeT m a = MaybeT
   { runMaybeT :: m (Maybe a)
-  -- ^ Unwrap a 'MaybeT' computation
+  -- ^ Unwrap a transformer computation
   }
 
 instance (Monad m) => Functor (MaybeT m) where
diff --git a/Mini/Transformers/ParserT.hs b/Mini/Transformers/ParserT.hs
--- a/Mini/Transformers/ParserT.hs
+++ b/Mini/Transformers/ParserT.hs
@@ -114,7 +114,7 @@
 -- | A transformer parsing symbols /s/, inner monad /m/, return /a/
 newtype ParserT s m a = ParserT
   { runParserT :: [s] -> m (Maybe (a, [s]))
-  -- ^ Unwrap a 'ParserT' computation with a sequence of symbols to parse
+  -- ^ Unwrap a transformer computation with a sequence of symbols to parse
   }
 
 instance (Monad m) => Functor (ParserT s m) where
diff --git a/Mini/Transformers/ReaderT.hs b/Mini/Transformers/ReaderT.hs
--- a/Mini/Transformers/ReaderT.hs
+++ b/Mini/Transformers/ReaderT.hs
@@ -48,7 +48,7 @@
 -- | A transformer with read-only /r/, inner monad /m/, return /a/
 newtype ReaderT r m a = ReaderT
   { runReaderT :: r -> m a
-  -- ^ Unwrap a 'ReaderT' computation with an initial read-only value
+  -- ^ Unwrap a transformer computation with an initial read-only value
   }
 
 instance (Monad m) => Functor (ReaderT r m) where
diff --git a/Mini/Transformers/StateT.hs b/Mini/Transformers/StateT.hs
--- a/Mini/Transformers/StateT.hs
+++ b/Mini/Transformers/StateT.hs
@@ -53,7 +53,7 @@
 -- | A transformer with state /s/, inner monad /m/, return /a/
 newtype StateT s m a = StateT
   { runStateT :: s -> m (a, s)
-  -- ^ Unwrap a 'StateT' computation with an initial state
+  -- ^ Unwrap a transformer computation with an initial state
   }
 
 instance (Monad m) => Functor (StateT s m) where
diff --git a/Mini/Transformers/WriterT.hs b/Mini/Transformers/WriterT.hs
--- a/Mini/Transformers/WriterT.hs
+++ b/Mini/Transformers/WriterT.hs
@@ -51,7 +51,7 @@
 -- | A transformer with monoidal write-only /w/, inner monad /m/, return /a/
 newtype WriterT w m a = WriterT
   { runWriterT :: m (a, w)
-  -- ^ Unwrap a 'WriterT' computation
+  -- ^ Unwrap a transformer computation
   }
 
 instance (Monad m, Monoid w) => Functor (WriterT w m) where
diff --git a/mini.cabal b/mini.cabal
--- a/mini.cabal
+++ b/mini.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.0
 name:               mini
-version:            1.6.0.0
+version:            1.6.1.0
 license:            MIT
 license-file:       LICENSE
 author:             Victor Wallsten <victor.wallsten@protonmail.com>
@@ -9,14 +9,14 @@
 bug-reports:        https://gitlab.com/vicwall/mini/issues
 synopsis:           Minimal essentials
 description:
-  Everyday essentials: data structures, primitive recursion, lenses,
+  Everyday essentials: data structures, primitive recursion, hashing, lenses,
   transformers, and parsing.
 
   Uncompromisingly light on dependencies.
 
   Easily navigable code base, keeping indirection and clutter to a minimum.
 category:           library
-tested-with:        GHC == 9.6.7
+tested-with:        GHC == 9.8.2
 extra-doc-files:    CHANGELOG.md
 extra-source-files: .editorconfig
                     .hlint.yaml
@@ -33,6 +33,8 @@
     Mini.Data.Map
     Mini.Data.Recursion
     Mini.Data.Set
+    Mini.Hash.Class
+    Mini.Hash.Murmur32
     Mini.Optics.Lens
     Mini.Transformers.Class
     Mini.Transformers.EitherT
@@ -58,3 +60,4 @@
     -Wmissing-import-lists
     -Wpartial-fields
     -Wredundant-constraints
+    -haddock
