diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,9 @@
 # Revision history for bytesmith
 
-## 0.3.2.0 -- 2019-??-??
+## 0.3.3.0 -- 2020-01-22
+* Add `hexWord8`, `hexWord16`, and `hexFixedWord8`.
+
+## 0.3.2.0 -- 2019-12-27
 
 * Add `parseBytesEither` and `parseBytesMaybe`.
 * Add common idioms from other parser libaries. This includes: `satisfy`,
diff --git a/bytesmith.cabal b/bytesmith.cabal
--- a/bytesmith.cabal
+++ b/bytesmith.cabal
@@ -1,6 +1,6 @@
 cabal-version: 2.2
 name: bytesmith
-version: 0.3.2.0
+version: 0.3.3.0
 synopsis: Nonresumable byte parser
 description:
   Parse bytes as fast as possible. This is a nonresumable parser
@@ -31,7 +31,7 @@
   build-depends:
     , base >=4.12 && <5
     , bytestring >=0.10.8 && <0.11
-    , byteslice >=0.1.3 && <0.3
+    , byteslice >=0.1.4 && <0.3
     , contiguous >= 0.4 && < 0.6
     , primitive >=0.7 && <0.8
     , text-short >=0.1.3 && <0.2
diff --git a/src/Data/Bytes/Parser/Latin.hs b/src/Data/Bytes/Parser/Latin.hs
--- a/src/Data/Bytes/Parser/Latin.hs
+++ b/src/Data/Bytes/Parser/Latin.hs
@@ -66,7 +66,13 @@
   , decUnsignedInteger
   , decTrailingInteger
     -- ** Hexadecimal
+    -- *** Variable Length
+  , hexWord8
+  , hexWord16
+    -- *** Fixed Length
+  , hexFixedWord8
   , hexFixedWord16
+    -- *** Digit
   , hexNibbleLower
   , tryHexNibbleLower
   , hexNibble
@@ -398,6 +404,26 @@
     (# s1, r #) -> (# s1, upcastWord8Result r #)
   )
 
+-- | Parse a hexadecimal-encoded 8-bit word. If the number is larger
+-- than 255, this parser fails. This allows leading zeroes and is
+-- insensitive to case. For example, @00A@, @0a@ and @A@ would all
+-- be accepted as the same number.
+hexWord8 :: e -> Parser e s Word8
+hexWord8 e = Parser
+  (\chunk0 s0 -> case hexSmallWordStart e 256 (boxBytes chunk0) s0 of
+    (# s1, r #) -> (# s1, upcastWord8Result r #)
+  )
+
+-- | Parse a hexadecimal-encoded 16-bit word. If the number is larger
+-- than 65535, this parser fails. This allows leading zeroes and is
+-- insensitive to case. For example, @0100a@ and @100A@ would both
+-- be accepted as the same number.
+hexWord16 :: e -> Parser e s Word16
+hexWord16 e = Parser
+  (\chunk0 s0 -> case hexSmallWordStart e 65536 (boxBytes chunk0) s0 of
+    (# s1, r #) -> (# s1, upcastWord16Result r #)
+  )
+
 -- | Parse a decimal-encoded 16-bit word. If the number is larger
 -- than 65535, this parser fails.
 decWord16 :: e -> Parser e s Word16
@@ -434,6 +460,17 @@
     (# s1, r #) -> (# s1, upcastWord64Result r #)
   )
 
+hexSmallWordStart ::
+     e -- Error message
+  -> Word -- Upper Bound
+  -> Bytes -- Chunk
+  -> ST# s (Result# e Word# )
+hexSmallWordStart e !limit !chunk0 s0 = if length chunk0 > 0
+  then case oneHexMaybe (PM.indexByteArray (array chunk0) (offset chunk0)) of
+    Nothing -> (# s0, (# e | #) #)
+    Just w -> (# s0, hexSmallWordMore e w limit (Bytes.unsafeDrop 1 chunk0) #)
+  else (# s0, (# e | #) #)
+
 decSmallWordStart ::
      e -- Error message
   -> Word -- Upper Bound
@@ -479,6 +516,21 @@
 upcastWord64Result (# e | #) = (# e | #)
 upcastWord64Result (# | (# a, b, c #) #) = (# | (# W64# a, b, c #) #)
 
+hexSmallWordMore ::
+     e -- Error message
+  -> Word -- Accumulator
+  -> Word -- Upper Bound
+  -> Bytes -- Chunk
+  -> Result# e Word#
+hexSmallWordMore e !acc !limit !chunk0 = if length chunk0 > 0
+  then case oneHexMaybe (PM.indexByteArray (array chunk0) (offset chunk0)) of
+    Nothing -> (# | (# unW acc, unI (offset chunk0), unI (length chunk0)  #) #)
+    Just w -> let w' = acc * 16 + w in
+      if w' < limit
+        then hexSmallWordMore e w' limit (Bytes.unsafeDrop 1 chunk0)
+        else (# e | #)
+  else (# | (# unW acc, unI (offset chunk0), 0# #) #)
+
 decSmallWordMore ::
      e -- Error message
   -> Word -- Accumulator
@@ -842,7 +894,7 @@
     else (# | (# (), unI (offset c + 1), unI (length c - 1) #) #)
 
 -- | Parse exactly four ASCII-encoded characters, interpretting
--- them as the hexadecimal encoding of a 32-bit number. Note that
+-- them as the hexadecimal encoding of a 16-bit number. Note that
 -- this rejects a sequence such as @5A9@, requiring @05A9@ instead.
 -- This is insensitive to case. This is particularly useful when
 -- parsing escape sequences in C or JSON, which allow encoding
@@ -875,6 +927,34 @@
            | otherwise -> (# e | #)
   else (# e | #)
 
+-- | Parse exactly two ASCII-encoded characters, interpretting
+-- them as the hexadecimal encoding of a 8-bit number. Note that
+-- this rejects a sequence such as @A@, requiring @0A@ instead.
+-- This is insensitive to case.
+hexFixedWord8 :: e -> Parser e s Word8
+{-# inline hexFixedWord8 #-}
+hexFixedWord8 e = Parser
+  (\x s0 -> case runParser (hexFixedWord8# e) x s0 of
+    (# s1, r #) -> case r of
+      (# err | #) -> (# s1, (# err | #) #)
+      (# | (# a, b, c #) #) -> (# s1, (# | (# W8# a, b, c #) #) #)
+  )
+
+hexFixedWord8# :: e -> Parser e s Word#
+{-# noinline hexFixedWord8# #-}
+hexFixedWord8# e = uneffectfulWord# $ \chunk -> if length chunk >= 2
+  then
+    let !w0@(W# n0) = oneHex $ PM.indexByteArray (array chunk) (offset chunk)
+        !w1@(W# n1) = oneHex $ PM.indexByteArray (array chunk) (offset chunk + 1)
+     in if | w0 .|. w1 /= maxBound ->
+             (# |
+                (# (n0 `Exts.timesWord#` 16##) `Exts.plusWord#`
+                   n1
+                ,  unI (offset chunk) +# 2#
+                ,  unI (length chunk) -# 2# #) #)
+           | otherwise -> (# e | #)
+  else (# e | #)
+
 -- | Consume a single character that is the lowercase hexadecimal
 -- encoding of a 4-bit word. Fails if the character is not in the class
 -- @[a-f0-9]@.
@@ -935,6 +1015,14 @@
   | w >= 65 && w < 71 = (fromIntegral w - 55)
   | w >= 97 && w < 103 = (fromIntegral w - 87)
   | otherwise = maxBound
+
+oneHexMaybe :: Word8 -> Maybe Word
+{-# inline oneHexMaybe #-}
+oneHexMaybe w
+  | w >= 48 && w < 58 = Just (fromIntegral w - 48)
+  | w >= 65 && w < 71 = Just (fromIntegral w - 55)
+  | w >= 97 && w < 103 = Just (fromIntegral w - 87)
+  | otherwise = Nothing
 
 uneffectfulWord# :: (Bytes -> Result# e Word#) -> Parser e s Word#
 uneffectfulWord# f = Parser
