diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Revision history for word-array
+
+## 0.1.0.0 -- YYYY-mm-dd
+
+* First version. Released on an unsuspecting world.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2021 Zachary Churchill
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/bench/Main.hs b/bench/Main.hs
new file mode 100644
--- /dev/null
+++ b/bench/Main.hs
@@ -0,0 +1,81 @@
+{-# LANGUAGE
+    BangPatterns
+  , GADTs
+  , DeriveGeneric
+  , StandaloneDeriving
+  , MagicHash
+  , DataKinds
+  , GeneralizedNewtypeDeriving
+  , TypeApplications
+  , ScopedTypeVariables
+#-}
+
+{-# OPTIONS_GHC
+    -fno-warn-orphans
+#-}
+
+import Control.Monad.Primitive
+import Data.Functor.Identity (Identity(Identity))
+import Data.Primitive
+import Data.Word
+import Data.Word64Array.Word8
+import Test.Tasty.Bench (bench, bgroup, defaultMain, env, nf, nfIO)
+import Control.DeepSeq (deepseq, NFData (rnf))
+import Control.Monad.Trans.State.Strict
+
+{-# NOINLINE someArray #-}
+someArray :: WordArray
+someArray = toWordArray (maxBound `div` 2)
+
+{-# NOINLINE mkPrimArray #-}
+mkPrimArray :: IO (MutablePrimArray (PrimState IO) Word8)
+mkPrimArray = do
+  arr <- newPrimArray 7
+  setPrimArray arr 0 7 (0 :: Word8)
+  pure arr
+
+{-# INLINE overIndexPA #-}
+overIndexPA :: (Prim a, PrimMonad m) => Int -> (a -> a) -> MutablePrimArray (PrimState m) a -> m ()
+overIndexPA i f arr = do
+  v <- readPrimArray arr i
+  writePrimArray arr i (f v)
+
+{-# NOINLINE iforPrimArray #-}
+iforPrimArray :: (Prim a, PrimMonad m) => MutablePrimArray (PrimState m) a -> (Int -> a -> m ()) -> m ()
+iforPrimArray arr f = do
+  readPrimArray arr 0 >>= f 0
+  readPrimArray arr 1 >>= f 1
+  readPrimArray arr 2 >>= f 2
+  readPrimArray arr 3 >>= f 3
+  readPrimArray arr 4 >>= f 4
+  readPrimArray arr 5 >>= f 5
+  readPrimArray arr 6 >>= f 6
+  readPrimArray arr 7 >>= f 7
+
+{-# NOINLINE iforWordArray' #-}
+iforWordArray' :: Applicative f => WordArray
+  -> (Int -> Word8 -> f ())
+  -> f ()
+iforWordArray' = iforWordArray
+
+sumState :: Monad m => Int -> Word8 -> StateT Int m ()
+sumState i w = 
+  modify $ \s -> i + s + fromIntegral w
+
+main :: IO ()
+main = do
+
+  defaultMain
+    [ bgroup "word-array"
+        [ bench "overIndex" $ nf (overIndex 0 (+1)) someArray
+        , bench "ifor" $ nf (flip iforWordArray (\i w -> i `deepseq` w `deepseq` Identity ())) (toWordArray maxBound)
+        , bench "ifor sum" $ nf (flip runState 0 . flip iforWordArray sumState) (toWordArray maxBound)
+        ]
+      , bgroup "prim-array"
+        [ env mkPrimArray $ \arr -> bench "overIndex" $ nfIO (overIndexPA 0 (+1) arr)
+        , env mkPrimArray $ \arr -> bench "ifor" $ nfIO (iforPrimArray arr (\i w -> i `deepseq` w `deepseq` pure ()))
+        , env mkPrimArray $ \arr -> bench "ifor sum" $ nfIO $ do
+            (_, !i) <- flip runStateT 0 $ iforPrimArray arr sumState
+            pure i
+        ]
+    ]
diff --git a/src/Data/Word64Array/Word8.hs b/src/Data/Word64Array/Word8.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Word64Array/Word8.hs
@@ -0,0 +1,188 @@
+{-# LANGUAGE
+    MagicHash
+  , TypeOperators
+  , DataKinds
+  , BangPatterns
+  , KindSignatures
+  , TypeFamilies
+  , StandaloneDeriving
+  , GeneralizedNewtypeDeriving
+  , TypeApplications
+  , ScopedTypeVariables
+  , InstanceSigs
+  , BinaryLiterals
+  , RankNTypes
+  , UnboxedTuples
+#-}
+
+module Data.Word64Array.Word8
+  ( WordArray(..)
+  , Index(..)
+  , toWordArray
+  , readArray
+  , writeArray
+  , overIndex
+  , iforWordArray
+  , toList
+  , toTuple
+  , fromTuple
+  , displayWordArray
+  ) where
+
+import Control.DeepSeq
+import Data.MonoTraversable
+import Data.Word
+import Data.Maybe (fromMaybe)
+import Data.Bits
+import Numeric (showHex)
+import Text.Show (showListWith)
+
+{- Note [Representation of WordArray]
+WordArray has its constituent Word8s packed in order from *left-to-right*, i.e.
+the first Word8 occupies the most-significant bits.
+
+Hence the offset to find the start of the ith Word8 is (-8*i) + 56.
+-}
+
+newtype WordArray = WordArray { fromWordArray :: Word64 }
+  deriving (Show, Eq, Ord, NFData)
+
+type instance Element WordArray = Word8
+
+newtype Index = Index { getIndex :: Int }
+  deriving (Show, Num, Eq, Ord)
+
+instance Bounded Index where
+  maxBound = 7
+  minBound = 0
+
+{-# INLINE toWordArray #-}
+toWordArray :: Word64 -> WordArray
+toWordArray = WordArray
+
+displayWordArray :: WordArray -> String
+displayWordArray wa = displayWordArrayS wa ""
+  where
+  displayHex x s = "0x" <> showHex x s
+  displayWordArrayS = showListWith displayHex . toList
+
+{-# INLINE toTuple #-}
+toTuple :: WordArray -> (# Element WordArray, Element WordArray, Element WordArray, Element WordArray, Element WordArray, Element WordArray, Element WordArray, Element WordArray #)
+toTuple (WordArray !w) = 
+  let
+    !w7 = w
+    !w6 = unsafeShiftR w7 8
+    !w5 = unsafeShiftR w6 8
+    !w4 = unsafeShiftR w5 8
+    !w3 = unsafeShiftR w4 8
+    !w2 = unsafeShiftR w3 8
+    !w1 = unsafeShiftR w2 8
+    !w0 = unsafeShiftR w1 8
+  in 
+  (# fromIntegral w0
+  ,  fromIntegral w1
+  ,  fromIntegral w2
+  ,  fromIntegral w3
+  ,  fromIntegral w4
+  ,  fromIntegral w5
+  ,  fromIntegral w6
+  ,  fromIntegral w7
+  #)
+
+{-# INLINE fromTuple #-}
+fromTuple :: (# Element WordArray, Element WordArray, Element WordArray, Element WordArray, Element WordArray, Element WordArray, Element WordArray, Element WordArray #) -> WordArray
+fromTuple (# !w0, !w1, !w2, !w3, !w4, !w5, !w6, !w7 #) =
+    WordArray
+      (                (fromIntegral w7)
+      .|. unsafeShiftL (fromIntegral w6) 8
+      .|. unsafeShiftL (fromIntegral w5) 16
+      .|. unsafeShiftL (fromIntegral w4) 24
+      .|. unsafeShiftL (fromIntegral w3) 32
+      .|. unsafeShiftL (fromIntegral w2) 40
+      .|. unsafeShiftL (fromIntegral w1) 48
+      .|. unsafeShiftL (fromIntegral w0) 56
+      )
+
+{-# INLINE toList #-}
+toList :: WordArray -> [Element WordArray]
+toList w =
+  let (# !w0, !w1, !w2, !w3, !w4, !w5, !w6, !w7 #) = toTuple w
+  in [w0, w1, w2, w3, w4, w5, w6, w7]
+
+{-# INLINE readArray #-}
+readArray :: WordArray -> Index -> Element WordArray
+readArray (WordArray !w) !i =
+  -- See Note [Representation of WordArray]
+  fromIntegral $ unsafeShiftR w (offset i)
+
+{-# INLINE offset #-}
+offset :: Index -> Int
+offset !i = (-8 * getIndex i) + 56
+
+{-# INLINE writeArray #-}
+writeArray :: WordArray -> Index -> Element WordArray -> WordArray
+writeArray (WordArray !w) !i !w8 =
+  -- See Note [Representation of WordArray]
+  let w64 :: Word64
+      w64 = unsafeShiftL (fromIntegral w8) (offset i)
+  in WordArray ((w .&. mask i) + w64)
+
+{-# INLINE overIndex #-}
+-- | Modify the word at a given index.
+overIndex :: Index -> (Element WordArray -> Element WordArray) -> WordArray -> WordArray
+overIndex !i f !w = writeArray w i $ f $ readArray w i
+
+{-# INLINE mask #-}
+mask :: Index -> Word64
+mask 0 = 0x00ffffffffffffff
+mask 1 = 0xff00ffffffffffff
+mask 2 = 0xffff00ffffffffff
+mask 3 = 0xffffff00ffffffff
+mask 4 = 0xffffffff00ffffff
+mask 5 = 0xffffffffff00ffff
+mask 6 = 0xffffffffffff00ff
+mask 7 = 0xffffffffffffff00
+mask _ = error "mask"
+
+{-# INLINE iforWordArray #-}
+iforWordArray :: Applicative f => WordArray -> (Int -> Element WordArray -> f ()) -> f ()
+iforWordArray !w f =
+  let (# !w0, !w1, !w2, !w3, !w4, !w5, !w6, !w7 #) = toTuple w
+  in   f 0 w0 *> f 1 w1 *> f 2 w2 *> f 3 w3 
+    *> f 4 w4 *> f 5 w5 *> f 6 w6 *> f 7 w7
+
+instance MonoFunctor WordArray where
+  omap f w =
+    let (# !w0, !w1, !w2, !w3, !w4, !w5, !w6, !w7 #) = toTuple w
+    in fromTuple (# f w0, f w1, f w2, f w3, f w4, f w5, f w6, f w7 #)
+
+instance MonoFoldable WordArray where
+  {-# INLINE ofoldr #-}
+  ofoldr f !b !w =
+    let (# !w0, !w1, !w2, !w3, !w4, !w5, !w6, !w7 #) = toTuple w
+    in  f w0 $ f w1 $ f w2 $ f w3 $ f w4 $ f w5 $ f w6 $ f w7 b
+  {-# INLINE ofoldl' #-}
+  ofoldl' f z0 xs = ofoldr f' id xs z0
+    where f' x k z = k $! f z x
+  {-# INLINE ofoldMap #-}
+  ofoldMap f = ofoldr (mappend . f) mempty
+  {-# INLINE onull #-}
+  onull _ = False
+  {-# INLINE oelem #-}
+  oelem e = ofoldr (\a b -> a == e || b) False
+  {-# INLINE ofoldr1Ex #-}
+  ofoldr1Ex f xs = fromMaybe 
+      (errorWithoutStackTrace "error in word-array ofoldr1Ex: empty array")
+      (ofoldr mf Nothing xs)
+    where
+    mf x m = Just $ case m of
+      Nothing -> x
+      Just y  -> f x y
+  {-# INLINE ofoldl1Ex' #-}
+  ofoldl1Ex' f xs = fromMaybe 
+      (errorWithoutStackTrace "error in word-array ofoldr1Ex: empty array")
+      (ofoldl' mf Nothing xs)
+    where
+    mf m y = Just $ case m of
+      Nothing -> y
+      Just x  -> f x y
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,72 @@
+{-# LANGUAGE 
+    MagicHash 
+  , TemplateHaskell
+  , TypeApplications
+  , DataKinds
+  , StandaloneDeriving
+  , GeneralizedNewtypeDeriving
+#-}
+
+{-# OPTIONS_GHC
+    -fno-warn-orphans
+#-}
+
+module Main where
+
+-- import Test.Tasty.HUnit
+import Data.MonoTraversable
+import Data.Proxy
+import Data.Vector
+import Data.Word
+import Data.Word64Array.Word8 as WordArray
+import Test.QuickCheck
+import Test.QuickCheck.Classes
+import Test.Tasty
+import Test.Tasty.QuickCheck
+import qualified Data.Word64Array.Word8 as W64A
+deriving instance Arbitrary W64A.WordArray
+
+toVector :: WordArray -> Vector Word8
+toVector = fromList . WordArray.toList
+
+instance Arbitrary Index where
+  arbitrary = Index <$> choose (0, 7)
+  shrink (Index i) = if i == 0 then [] else [Index (i-1)]
+
+main :: IO ()
+main = do
+  lawsCheckMany typeclassLaws
+  defaultMain tests
+
+tests :: TestTree
+tests = 
+  testGroup "Tests" [unitTests]
+
+unitTests :: TestTree
+unitTests = testGroup "Unit tests"
+  [ testGroup "word-array" [
+        testProperty "write" $ \(arr, ix@(Index i), w8) ->
+            toVector (writeArray arr ix w8) == toVector arr // [(i, w8)]
+        , testProperty "read" $ \(arr, ix@(Index i)) ->
+            readArray arr ix == toVector arr ! i
+        , testProperty "fold" $ \arr ->
+            ofoldr (+) 0 arr == Prelude.foldr (+) 0 (toVector arr)
+    ]
+  ]
+
+w64w8Laws :: [Laws]
+w64w8Laws =
+  let p = Proxy :: Proxy W64A.WordArray
+  in [eqLaws p, ordLaws p]
+
+typeclassLaws :: ([(String, [Laws])])
+typeclassLaws = 
+  [ ("Word64Array Word8", w64w8Laws)
+  ]
+
+unwrap :: Either a b -> b
+unwrap = either (error "unwrap") id
+
+testLaws :: Laws -> TestTree
+testLaws (Laws tc lp) = testProperties tc lp
+
diff --git a/word-array.cabal b/word-array.cabal
new file mode 100644
--- /dev/null
+++ b/word-array.cabal
@@ -0,0 +1,73 @@
+cabal-version: 2.4
+name: word-array
+version: 0.1.0.0
+synopsis: treat integral types as arrays of smaller integral types
+
+description: treat integral types as arrays of smaller integral types.
+  mostly a collection of bitwise operations. you can, for instance,
+  split a @Word64@ into 8 @Word8@s
+homepage: https://github.com/goolord/word-array/
+
+bug-reports: https://github.com/goolord/word-array/issues
+license: MIT
+license-file: LICENSE
+author: Zachary Churchill
+maintainer: zacharyachurchill@gmail.com
+
+-- copyright:
+category: Data
+extra-source-files: CHANGELOG.md
+
+library
+  exposed-modules:
+      Data.Word64Array.Word8
+
+  build-depends: 
+    , base >=4.13 && <5.0
+    , mono-traversable
+    , deepseq
+  hs-source-dirs: src
+  default-language: Haskell2010
+  ghc-options: -Wall -O2
+
+test-suite test
+  type: exitcode-stdio-1.0
+  hs-source-dirs: test
+  main-is: Main.hs
+  build-depends:
+      HUnit
+    , tasty
+    , QuickCheck
+    , base
+    , mono-traversable
+    , primitive
+    , quickcheck-classes
+    , tasty
+    , tasty-hunit
+    , tasty-quickcheck
+    , vector
+    , word-array
+  ghc-options:
+    -Wall
+    -O2
+  default-language: Haskell2010
+
+benchmark bench
+  type: exitcode-stdio-1.0
+  build-depends:
+      base
+    , tasty
+    , weigh == 0.0.16
+    , tasty-bench
+    , word-array
+    , deepseq
+    , primitive
+    , transformers
+  ghc-options: -Wall -O3
+  default-language: Haskell2010
+  hs-source-dirs: bench
+  main-is: Main.hs
+
+source-repository head
+  type: git
+  location: https://github.com/goolord/word-array/
