diff --git a/AC-Colour.cabal b/AC-Colour.cabal
new file mode 100644
--- /dev/null
+++ b/AC-Colour.cabal
@@ -0,0 +1,25 @@
+Cabal-Version: >= 1.6
+Name:          AC-Colour
+Version:       1.1.1
+Stability:     Experimental
+Synopsis:      Efficient RGB colour types.
+
+Description:
+
+  This Haskell library is a basic RGB colour types, with
+  both @Double@ and @Word8@ channel types. It implements
+  efficient conversions between the two (GHC-specific),
+  and general arithmetic over colours.
+
+Category:      Data, Math, Numerical, Graphics
+License:       BSD3
+License-file:  Licence.txt
+Author:        Andrew Coppin
+Maintainer:    MathematicalOrchid@hotmail.com
+Build-Type:    Simple
+Tested-With:   GHC == 6.10.3
+
+Library
+  Exposed-modules: Data.Colour.Double, Data.Colour.Word8, Data.Colour.FastFloor, Data.Colour
+  Build-Depends:   base >= 4 && < 5, ghc-prim >= 0.1.0.0 && < 0.2
+  HS-Source-Dirs:  .
diff --git a/Data/Colour.hs b/Data/Colour.hs
new file mode 100644
--- /dev/null
+++ b/Data/Colour.hs
@@ -0,0 +1,59 @@
+{- |
+  This module exports all the main interesting parts of the various
+  colour modules. It also provides functions for converting between
+  'Colour' and 'Colour8'.
+
+  It is the general intention that \"most\" work will be done with
+  'Colour', with values converted to @Colour8@ only as a final step.
+  However, full arithmetic on 'Colour8' is supported anyway,
+  in case anybody wants to work that way. It is slightly less
+  efficient and flexible, however.
+-}
+
+module Data.Colour
+    (
+      Colour (..),
+      grey, cscale, clip,
+      cBlack, cWhite, cRed, cYellow, cGreen, cCyan, cBlue, cMagenta,
+
+      Colour8 (..),
+      grey8, c8scale,
+      c8Black, c8White, c8Red, c8Yellow, c8Green, c8Cyan, c8Blue, c8Magenta,
+
+      cpromote, cdemote
+    )
+  where
+
+import Data.Colour.Double
+import Data.Colour.Word8
+import Data.Colour.FastFloor
+
+{- |
+  Convert a 'Colour8' into a 'Colour'. Recall that
+  0x00 means zero and 0xFF means one; this function will
+  remap such values appropriately.
+-}
+cpromote :: Colour8 -> Colour
+cpromote (Colour8 r g b) = Colour
+  {
+    red   = fromIntegral r / 0xFF,
+    green = fromIntegral g / 0xFF,
+    blue  = fromIntegral b / 0xFF
+  }
+
+{- |
+  Convert a 'Colour' into a 'Colour8'. Any values outside
+  the range 0--1 will be `wrapped' to that range. You may
+  want to run 'clip' before calling this function to
+  prevent this behaviour (unless you know the values can't
+  be outside the permitted range). This function is the
+  exact inverse of 'cpromote'; 0 is mapped to 0x00 and
+  1 is mapped to 0xFF.
+-}
+cdemote :: Colour -> Colour8
+cdemote (Colour r g b) = Colour8
+  {
+    red8   = fast_floor (r * 0xFF),
+    green8 = fast_floor (g * 0xFF),
+    blue8  = fast_floor (b * 0xFF)
+  }
diff --git a/Data/Colour/Double.hs b/Data/Colour/Double.hs
new file mode 100644
--- /dev/null
+++ b/Data/Colour/Double.hs
@@ -0,0 +1,107 @@
+{- |
+  This module provides 'Colour', which stores RGB (red, green, blue)
+  colour values where each channel is a @Double@. It also provides
+  arithmetic over such colours, and a few predefined colours.
+-}
+
+module Data.Colour.Double where
+
+{- |
+  The @Colour@ type. Stores a red, a green and a blue component as
+  strict, unboxed @Double@ values. (So it should be quite efficient
+  in time and space.) Also provides various class instances for
+  arithmetic, etc. It is generally assumed that each channel will
+  have a value somewhere between 0 and 1 at all times.
+
+  Note that @(*)@ acts channel-wise. This is usually what is wanted.
+-}
+data Colour = Colour {red, green, blue :: {-# UNPACK #-} !Double}
+  deriving (Eq, Ord, Show)
+
+{- |
+  Apply a function to every channel in a colour. (Mostly used
+  internally, but exposed here in case it may be useful.)
+-}
+cmap :: (Double -> Double) -> Colour -> Colour
+cmap f (Colour r g b) = Colour (f r) (f g) (f b)
+
+{- |
+  This is similar to 'Data.List.zipWith'. (Mostly used internally,
+  but exposed here in case it may be useful.)
+-}
+czip :: (Double -> Double -> Double) -> Colour -> Colour -> Colour
+czip f (Colour r1 g1 b1) (Colour r2 g2 b2) = Colour (f r1 r2) (f g1 g2) (f b1 b2)
+
+{- |
+  Use a function to collapse a @Colour@ into a @Double@. No
+  particular order of application is promised.
+-}
+cfold :: (Double -> Double -> Double) -> Colour -> Double
+cfold f (Colour r g b) = f r (f g b)
+
+{- | Turn a @Double@ into a shade of grey. -}
+grey :: Double -> Colour
+grey x = Colour x x x
+
+{- |
+  Scale a @Colour@ by a specified amount. (That is, change the
+  brightness while not affecting the shade.)
+-}
+cscale :: Double -> Colour -> Colour
+cscale x = cmap (x*)
+
+instance Num Colour where
+  (+) = czip (+)
+  (-) = czip (-)
+  (*) = czip (*)
+  negate = cmap negate
+  abs    = cmap abs
+  signum = cmap signum
+  fromInteger = grey . fromInteger
+
+instance Fractional Colour where
+  (/) = czip (/)
+  recip = cmap recip
+  fromRational = grey . fromRational
+
+{- |
+  Take a @Colour@ and clip all channels to the range 0--1
+  inclusive. Any value outside that range will be replaced
+  with the nearest endpoint (i.e., 0 for negative numbers,
+  1 for positive numbers higher than 1). Values inside
+  the range are unaffected.
+-}
+clip :: Colour -> Colour
+clip = cmap (min 1 . max 0)
+
+-- | Constant: Black.
+cBlack   :: Colour
+cBlack   = Colour 0 0 0
+
+-- | Constant: Red.
+cRed     :: Colour
+cRed     = Colour 1 0 0
+
+-- | Constant: Yellow.
+cYellow  :: Colour
+cYellow  = Colour 1 1 0
+
+-- | Constant: Green.
+cGreen   :: Colour
+cGreen   = Colour 0 1 0
+
+-- | Constant: Cyan.
+cCyan    :: Colour
+cCyan    = Colour 0 1 1
+
+-- | Constant: Blue.
+cBlue    :: Colour
+cBlue    = Colour 0 0 1
+
+-- | Constant: Magenta.
+cMagenta :: Colour
+cMagenta = Colour 1 0 1
+
+-- | Constant: White.
+cWhite   :: Colour
+cWhite   = Colour 1 1 1
diff --git a/Data/Colour/FastFloor.hs b/Data/Colour/FastFloor.hs
new file mode 100644
--- /dev/null
+++ b/Data/Colour/FastFloor.hs
@@ -0,0 +1,35 @@
+{-# LANGUAGE MagicHash #-}
+
+{- |
+  This module shouldn't need to exist. It exists because
+  GHC's implementation of 'floor' is currently (6.10.3)
+  very slow. See the following tickets:
+
+  <http://hackage.haskell.org/trac/ghc/ticket/1434>
+
+  <http://hackage.haskell.org/trac/ghc/ticket/2271>
+-}
+
+module Data.Colour.FastFloor where
+
+import GHC.Prim
+import GHC.Types
+import GHC.Word
+
+{- |
+  This is a special version of the regular 'floor' function.
+  It works by directly calling the low-level internal GHC
+  primitives, and thus is as fast as you'd expect for such
+  a trivial operation.
+
+  (The standard 'floor' function does something crazy like
+  converting a @Double@ to a numerator/denominator @Integer@
+  pair and then computing the integer part of the quotient as
+  an @Integer@, then truncating that to a @Word8@. Which,
+  obviously, is ludicrously slow.)
+
+  Hopefully one day the need for this low-level hackery
+  will disappear.
+-}
+fast_floor :: Double -> Word8
+fast_floor (D# d#) = W8# (int2Word# (double2Int# d#))
diff --git a/Data/Colour/Word8.hs b/Data/Colour/Word8.hs
new file mode 100644
--- /dev/null
+++ b/Data/Colour/Word8.hs
@@ -0,0 +1,105 @@
+{- |
+  This module provides 'Colour8', which stores RGB (red, green, blue)
+  colour values where each channel is a @Word8@. It also provides
+  arithmetic over such colours, and a few predefined colours.
+
+  It is the general intention that \"most\" work will be done with
+  "Data.Colour.Double", with values converted to @Colour8@ only
+  as a final step. However, full arithmetic is supported anyway,
+  in case anybody wants to work that way. It is slightly less
+  efficient and flexible, however.
+-}
+
+module Data.Colour.Word8 where
+
+import Data.Word
+
+{- |
+  The @Colour@ type. Stores a red, a green and a blue component as
+  strict, unboxed @Word8@ values. (So it should be quite efficient
+  in time and space.) Also provides various class instances for
+  arithmetic, etc.
+
+  Note that 0x00 is assumed to mean zero, and 0xFF to mean one.
+  That means that @(*)@ is slightly slower than you might expect
+  due to the extra steps required for renormalisation; @(+)@ and
+  @(-)@ are still efficient, however.
+-}
+data Colour8 = Colour8 {red8, green8, blue8 :: {-# UNPACK #-} !Word8}
+  deriving (Eq, Ord, Show)
+
+{- |
+  Apply a function to every channel of a @Colour8@. (Mostly used
+  internally; exposed here in case it might be useful.)
+-}
+c8map :: (Word8 -> Word8) -> Colour8 -> Colour8
+c8map f (Colour8 r g b) = Colour8 (f r) (f g) (f b)
+
+{- |
+  The colour equivilent of 'Data.List.zipWith'. (Mostly used
+  internally; exposed here in case it might be useful.)
+-}
+c8zip :: (Word8 -> Word8 -> Word8) -> Colour8 -> Colour8 -> Colour8
+c8zip f (Colour8 r1 g1 b1) (Colour8 r2 g2 b2) = Colour8 (f r1 r2) (f g1 g2) (f b1 b2)
+
+{- |
+  Use a function to fold the three values in a @Colour8@ into
+  a single value. No particular order of application is promised.
+-}
+c8fold :: (Word8 -> Word8 -> Word8) -> Colour8 -> Word8
+c8fold f (Colour8 r g b) = f r (f g b)
+
+{- | Convert a @Word8@ into a shade of grey. -}
+grey8 :: Word8 -> Colour8
+grey8 x = Colour8 x x x
+
+{- |
+  Scale a @Colour8@ by the specified amount. Recall that 0x00
+  means zero, and 0xFF means one. This means that it is impossible
+  to make a colour /brighter/, only darker. It also means this
+  operation is modestly inefficient due to the renormalisation
+  steps.
+-}
+c8scale :: Word8 -> Colour8 -> Colour8
+c8scale x c = grey8 x * c
+
+instance Num Colour8 where
+  (+) = c8zip (+)
+  (-) = c8zip (-)
+  (*) = c8zip (\x y -> let (x', y') = (fromIntegral x, fromIntegral y) :: (Word16, Word16) in fromIntegral (x' * y' `div` 0x00FF))
+  negate = c8map negate
+  abs    = c8map abs
+  signum = c8map signum
+  fromInteger = grey8 . fromInteger
+
+-- | Constant: Black.
+c8Black   :: Colour8
+c8Black   = Colour8 0x00 0x00 0x00
+
+-- | Constant: Red.
+c8Red     :: Colour8
+c8Red     = Colour8 0xFF 0x00 0x00
+
+-- | Constant: Yellow.
+c8Yellow  :: Colour8
+c8Yellow  = Colour8 0xFF 0xFF 0x00
+
+-- | Constant: Green.
+c8Green   :: Colour8
+c8Green   = Colour8 0x00 0xFF 0x00
+
+-- | Constant: Cyan.
+c8Cyan    :: Colour8
+c8Cyan    = Colour8 0x00 0xFF 0xFF
+
+-- | Constant: Blue.
+c8Blue    :: Colour8
+c8Blue    = Colour8 0x00 0x00 0xFF
+
+-- | Constant: Magenta.
+c8Magenta :: Colour8
+c8Magenta = Colour8 0xFF 0x00 0xFF
+
+-- | Constant: White.
+c8White   :: Colour8
+c8White   = Colour8 0xFF 0xFF 0xFF
diff --git a/Licence.txt b/Licence.txt
new file mode 100644
--- /dev/null
+++ b/Licence.txt
@@ -0,0 +1,10 @@
+Copyright (c) 2009, Andrew Coppin
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+    * Neither the name of Andrew Coppin nor the names of the contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,3 @@
+import Distribution.Simple
+
+main = defaultMain
