diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## Identicon 0.2.1
+
+* Added `Semigroup` and `Monoid` instances for `Layer`.
+
 ## Identicon 0.2.0
 
 * Added benchmarks.
diff --git a/Graphics/Identicon.hs b/Graphics/Identicon.hs
--- a/Graphics/Identicon.hs
+++ b/Graphics/Identicon.hs
@@ -1,6 +1,6 @@
 -- |
 -- Module      :  Graphics.Identicon
--- Copyright   :  © 2016 Mark Karpov
+-- Copyright   :  © 2016–2017 Mark Karpov
 -- License     :  BSD 3 clause
 --
 -- Maintainer  :  Mark Karpov <markkarpov@openmailbox.org>
@@ -40,6 +40,7 @@
 -- black. The circle is mirrored 4 times, and every repetition is rotated by
 -- 90°. This identicon consumes 4 bytes and has one layer.
 
+{-# LANGUAGE CPP                  #-}
 {-# LANGUAGE DataKinds            #-}
 {-# LANGUAGE FlexibleContexts     #-}
 {-# LANGUAGE FlexibleInstances    #-}
@@ -73,7 +74,12 @@
 import Data.Word (Word8)
 import GHC.TypeLits
 import qualified Data.ByteString as B
+import qualified Data.Semigroup  as S
 
+#if !MIN_VERSION_base(4,8,0)
+import Data.Monoid
+#endif
+
 ----------------------------------------------------------------------------
 -- Basic types
 
@@ -122,6 +128,13 @@
 
 newtype Layer = Layer
   { unLayer :: Int -> Int -> Int -> Int -> PixelRGB8 }
+
+instance S.Semigroup Layer where
+  Layer a <> Layer b = Layer (\w h -> mixPixels (a w h) (b w h))
+
+instance Monoid Layer where
+  mempty  = Layer $ \_ _ _ _ -> PixelRGB8 0 0 0
+  mappend = (S.<>)
 
 -- | The 'BytesAvailable' type function calculates how many bytes available
 -- for consumption in a given identicon.
diff --git a/Graphics/Identicon/Primitive.hs b/Graphics/Identicon/Primitive.hs
--- a/Graphics/Identicon/Primitive.hs
+++ b/Graphics/Identicon/Primitive.hs
@@ -1,6 +1,6 @@
 -- |
 -- Module      :  Graphics.Identicon.Primitive
--- Copyright   :  © 2016 Mark Karpov
+-- Copyright   :  © 2016–2017 Mark Karpov
 -- License     :  BSD 3 clause
 --
 -- Maintainer  :  Mark Karpov <markkarpov@openmailbox.org>
diff --git a/LICENSE.md b/LICENSE.md
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -1,4 +1,4 @@
-Copyright © 2016 Mark Karpov
+Copyright © 2016–2017 Mark Karpov
 
 All rights reserved.
 
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -92,6 +92,6 @@
 
 ## License
 
-Copyright © 2016 Mark Karpov
+Copyright © 2016–2017 Mark Karpov
 
 Distributed under BSD 3 clause license.
diff --git a/bench/Main.hs b/bench/Main.hs
--- a/bench/Main.hs
+++ b/bench/Main.hs
@@ -1,7 +1,7 @@
 --
 -- Benchmarks for the ‘identicon’ package.
 --
--- Copyright © 2016 Mark Karpov <markkarpov@openmailbox.org>
+-- Copyright © 2016–2017 Mark Karpov <markkarpov@openmailbox.org>
 --
 -- Redistribution and use in source and binary forms, with or without
 -- modification, are permitted provided that the following conditions are
diff --git a/identicon.cabal b/identicon.cabal
--- a/identicon.cabal
+++ b/identicon.cabal
@@ -1,7 +1,7 @@
 --
 -- Cabal configuration for ‘identicon’ package.
 --
--- Copyright © 2016 Mark Karpov <markkarpov@openmailbox.org>
+-- Copyright © 2016–2017 Mark Karpov <markkarpov@openmailbox.org>
 --
 -- Redistribution and use in source and binary forms, with or without
 -- modification, are permitted provided that the following conditions are
@@ -31,7 +31,7 @@
 -- POSSIBILITY OF SUCH DAMAGE.
 
 name:                 identicon
-version:              0.2.0
+version:              0.2.1
 cabal-version:        >= 1.10
 license:              BSD3
 license-file:         LICENSE.md
@@ -60,6 +60,8 @@
   build-depends:      base             >= 4.7     && < 5.0
                     , JuicyPixels      >= 3.2.6.5 && < 4.0
                     , bytestring       >= 0.10.6  && < 0.13
+  if !impl(ghc >= 8.0)
+    build-depends:    semigroups       == 0.18.*
   exposed-modules:    Graphics.Identicon
                     , Graphics.Identicon.Primitive
   if flag(dev)
@@ -72,11 +74,14 @@
   main-is:            Spec.hs
   hs-source-dirs:     tests
   type:               exitcode-stdio-1.0
-  build-depends:      base             >= 4.7     && < 5.0
-                    , JuicyPixels      >= 3.2.6.5 && < 4.0
+  build-depends:      JuicyPixels      >= 3.2.6.5 && < 4.0
+                    , QuickCheck       >= 2.7     && < 3.0
+                    , base             >= 4.7     && < 5.0
                     , bytestring       >= 0.10.6  && < 0.13
                     , hspec            >= 2.0     && < 3.0
-                    , identicon        >= 0.2.0
+                    , identicon        >= 0.2.1
+  if !impl(ghc >= 8.0)
+    build-depends:    semigroups       == 0.18.*
   if flag(dev)
     ghc-options:      -Wall -Werror
   else
@@ -91,7 +96,7 @@
                     , JuicyPixels      >= 3.2.6.5 && < 4.0
                     , bytestring       >= 0.10.6  && < 0.13
                     , criterion        >= 0.6.2.1 && < 1.2
-                    , identicon        >= 0.2.0
+                    , identicon        >= 0.2.1
                     , random           >= 1.1     && < 1.2
                     , tf-random        >= 0.4     && < 0.6
   if flag(dev)
diff --git a/tests/Spec.hs b/tests/Spec.hs
--- a/tests/Spec.hs
+++ b/tests/Spec.hs
@@ -1,7 +1,7 @@
 --
 -- Tests for the ‘identicon’ package.
 --
--- Copyright © 2016 Mark Karpov <markkarpov@openmailbox.org>
+-- Copyright © 2016–2017 Mark Karpov <markkarpov@openmailbox.org>
 --
 -- Redistribution and use in source and binary forms, with or without
 -- modification, are permitted provided that the following conditions are
@@ -30,6 +30,7 @@
 -- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 -- POSSIBILITY OF SUCH DAMAGE.
 
+{-# LANGUAGE CPP               #-}
 {-# LANGUAGE DataKinds         #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE TypeOperators     #-}
@@ -45,8 +46,13 @@
 import Graphics.Identicon
 import Graphics.Identicon.Primitive
 import Test.Hspec
+import Test.QuickCheck hiding (oneof)
 import qualified Data.ByteString as B
 
+#if !MIN_VERSION_base(4,8,0)
+import Data.Monoid
+#endif
+
 main :: IO ()
 main = hspec spec
 
@@ -68,6 +74,26 @@
     "data-examples/identicon-21.png"
   ω gen2 [0xf9,0x9b,0xb7,0x11,0x5b,0xca,0x00]
     "data-examples/identicon-22.png"
+  describe "Semigroup and Monoid instances of Layer" $ do
+    it "mempty always returns black pixel" $
+      property $ \w h x y ->
+        let (Layer f) = mempty
+        in f w h x y `shouldBe` PixelRGB8 0 0 0
+    it "mappend combines layers" $
+      property $ \w'' h'' x'' y'' ->
+        let w = w'' `mod` 10
+            h = h'' `mod` 10
+            x = x'' `mod` 10
+            y = y'' `mod` 10
+            (Layer f) = Layer a `mappend` Layer b
+            a w' h' x' y' = PixelRGB8 (g $ w' + h') (g $ h' + x') (g $ x' + y')
+            b w' h' x' y' = PixelRGB8 (g $ w' + y') (g $ h' + w') (g $ x' + w')
+            g = fromIntegral
+        in f w h x y
+           `shouldBe` PixelRGB8
+           (g $ w + h + w + y)
+           (g $ h + x + h + w)
+           (g $ x + y + x + w)
 
 renderIdenticonSpec :: Spec
 renderIdenticonSpec = do
