diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2012, Chris Done
+
+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 Chris Done nor the names of other
+      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
+OWNER 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,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/sourcemap.cabal b/sourcemap.cabal
new file mode 100644
--- /dev/null
+++ b/sourcemap.cabal
@@ -0,0 +1,27 @@
+name:                sourcemap
+version:             0.1.0.0
+synopsis:            Implementation of source maps as proposed by Google and Mozilla.
+description:         Implementation of source maps, revision 3, proposed by Google and Mozilla here
+                     https://wiki.mozilla.org/DevTools/Features/SourceMap and here
+                     https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
+license:             BSD3
+license-file:        LICENSE
+author:              Chris Done
+stability:           alpha
+maintainer:          chrisdone@gmail.com
+copyright:           2012 Chris Done
+category:            Development
+build-type:          Simple
+cabal-version:       >=1.8
+
+library
+  exposed-modules:     SourceMap, SourceMap.Types
+  other-modules:       VLQ
+  hs-source-dirs:      src
+  build-depends:       base ==4.5.*,
+                       bytestring >= 0.10.2.0,
+                       aeson,
+                       unordered-containers,
+                       attoparsec,
+                       process,
+                       utf8-string
diff --git a/src/SourceMap.hs b/src/SourceMap.hs
new file mode 100644
--- /dev/null
+++ b/src/SourceMap.hs
@@ -0,0 +1,94 @@
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# OPTIONS -Wall #-}
+
+module SourceMap where
+
+import           SourceMap.Types
+import qualified VLQ as VLQ
+
+import           Control.Monad hiding (forM_)
+import           Control.Monad.ST
+import           Data.Aeson hiding ((.=))
+import           Data.ByteString.Lazy (ByteString)
+import qualified Data.ByteString.Lazy as Bytes
+import           Data.Foldable (forM_)
+import qualified Data.HashMap.Lazy as Map
+import           Data.List
+import           Data.Maybe
+import           Data.Ord
+import           Data.STRef
+
+-- | Generate the JSON.
+generate :: SourceMapping -> Value
+generate SourceMapping{..} = Object (Map.fromList obj) where
+  obj = [("version",toJSON version)
+        ,("file",toJSON smFile)
+        ,("sources",toJSON sources)
+        ,("names",toJSON names)
+        ,("mappings",toJSON (encodeMappings sources names smMappings))] ++
+        [("sourceRoot",toJSON root) | Just root <- [smSourceRoot]]
+  names = nub $ mapMaybe mapName smMappings
+  sources = symbols mapSourceFile
+  symbols f = sort (nub (mapMaybe f smMappings))
+
+encodeMappings :: [FilePath] -> [String] -> [Mapping] -> ByteString
+encodeMappings sources names = go . sortBy (comparing mapGenerated) where
+  go mappings = runST $ do
+    -- State.
+    prevGenCol   <- newSTRef 0
+    prevGenLine  <- newSTRef 1
+    prevOrigCol  <- newSTRef 0
+    prevOrigLine <- newSTRef 0
+    prevName     <- newSTRef 0
+    prevSource   <- newSTRef 0
+    result       <- newSTRef Bytes.empty
+    -- Generate the groupings.
+    forM_ (zip [0::Integer ..] mappings) $ \(i,Mapping{..}) -> do
+      -- Continuations on the same line are separated by “,”, whereas
+      -- new lines are separted by “;”.
+      updating prevGenLine $ \previousGeneratedLine ->
+        if posLine mapGenerated /= previousGeneratedLine
+           then do prevGenCol .= 0
+                   result += Bytes.replicate (fromIntegral (posLine mapGenerated - previousGeneratedLine))
+                                             (fromIntegral (fromEnum ';'))
+                   return (posLine mapGenerated)
+           else do when (i > 0)
+                        (result += ",")
+                   return previousGeneratedLine
+      -- Original generated column (also offsetted from previous entries).
+      updating prevGenCol $ \previousGeneratedColumn -> do
+        result += VLQ.encode (posColumn mapGenerated - previousGeneratedColumn)
+        return (posColumn mapGenerated)
+      -- Optional additional fields.
+      case liftM2 (,) mapSourceFile mapOriginal of
+        Nothing -> return ()
+        Just (source,original) -> do
+          -- Source index.
+          updating prevSource $ \previousSource -> do
+           result += VLQ.encode (indexOf source sources - previousSource)
+           return (indexOf source sources)
+          -- Original line (also offsetted from previous entries).
+          updating prevOrigLine $ \previousOriginalLine -> do
+           result += VLQ.encode (posLine original - 1 - previousOriginalLine)
+           return (posLine original - 1)
+          -- Original column (also offsetted from previous entries).
+          updating prevOrigCol $ \previousOriginalColumn -> do
+           result += VLQ.encode (posColumn original - previousOriginalColumn)
+           return (posColumn original)
+          -- Optional name
+          forM_ mapName $ \name -> do
+            updating prevName $ \previousName -> do
+             result += VLQ.encode (indexOf name names - previousName)
+             return (indexOf name names)
+    -- Return the byte buffer.
+    readSTRef result
+
+  updating r f = readSTRef r >>= \x -> f x >>= writeSTRef r
+  r += y = modifySTRef r (\x -> Bytes.append x y)
+  x .= y = writeSTRef x y; infixr 1 .=
+  indexOf e xs = fromIntegral (fromMaybe 0 (elemIndex e xs))
+
+-- | Format version.
+version :: Integer
+version = 3
diff --git a/src/SourceMap/Types.hs b/src/SourceMap/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/SourceMap/Types.hs
@@ -0,0 +1,33 @@
+{-# OPTIONS -Wall #-}
+
+-- | Types for the source maps.
+
+module SourceMap.Types where
+
+import Data.Int
+import Data.Monoid
+import Data.Function
+
+-- | The source mapping.
+data SourceMapping = SourceMapping
+  { smFile       :: FilePath
+  , smSourceRoot :: Maybe FilePath
+  , smMappings   :: [Mapping]
+  } deriving Show
+
+-- | A mapping.
+data Mapping = Mapping
+  { mapGenerated  :: Pos
+  , mapOriginal   :: Maybe Pos
+  , mapSourceFile :: Maybe FilePath
+  , mapName       :: Maybe String
+  } deriving Show
+
+-- | A source position.
+data Pos = Pos
+  { posLine   :: Int32
+  , posColumn :: Int32
+  } deriving (Eq,Show)
+
+instance Ord Pos where
+  compare a b = on compare posLine a b <> on compare posColumn a b
diff --git a/src/VLQ.hs b/src/VLQ.hs
new file mode 100644
--- /dev/null
+++ b/src/VLQ.hs
@@ -0,0 +1,121 @@
+-- | Implements Base 64-encoded VLQ for 32-bit
+-- integers. Implementation copied from
+-- https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java
+--
+
+{-# OPTIONS -Wall #-}
+
+module VLQ
+  (encode
+  ,decode)
+  where
+
+import           Data.Bits hiding (shift)
+import           Data.ByteString.Lazy (ByteString)
+import qualified Data.ByteString.Lazy as B
+import           Data.Int
+import           Data.List
+import           Data.Word
+import           Prelude hiding ((>>))
+
+-- | A Base64 VLQ digit can represent 5 bits, so it is base-32.
+baseShift :: Int
+baseShift = 5
+
+-- | Base point.
+base :: Int32
+base = 1 << baseShift
+
+-- | A mask of bits for a VLQ digit (11111), 31 decimal.
+baseMask :: Int32
+baseMask = base - 1
+
+-- | The continuation bit is the 6th bit.
+continuationBit :: Int32
+continuationBit = base
+
+-- | Converts from a two-complement value to a value where the sign
+-- bit is is placed in the least significant bit.  For example, as
+-- decimals:
+--   1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
+--   2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
+toVlqSigned :: Int32 -> Int32
+toVlqSigned value =
+  if value < 0
+     then ((-value) << 1) + 1
+     else (value << 1) + 0
+
+-- | Converts to a two-complement value from a value where the sign
+-- bit is is placed in the least significant bit.  For example, as
+-- decimals:
+--   2 (10 binary) becomes 1, 3 (11 binary) becomes -1
+--   4 (100 binary) becomes 2, 5 (101 binary) becomes -2
+fromVlgSigned :: Int32 -> Int32
+fromVlgSigned value =
+  let value' = value >> 1
+  in if (value & 1) == 1
+        then -value'
+        else value'
+
+-- | Produces a ByteString containing a VLQ-encoded value of the given 32-bit integer.
+encode :: Int32 -> ByteString
+encode = B.map encodeBase64 . start where
+  start 0 = B.singleton (fst (continue 0))
+  start n = B.unfoldr go . toVlqSigned $ n
+
+  go value
+    | value <= 0 = Nothing
+    | otherwise  = Just (continue value)
+
+  continue value =
+    let digit = value & baseMask
+        value' = value >> baseShift
+        digit' = if value' > 0
+                    then digit .|. continuationBit
+                    else digit
+    in (fromIntegral digit',value')
+
+
+-- | Decodes the given VLQ-encoded value into a 32-bit integer.
+decode :: ByteString -> Int32
+decode = fromVlgSigned . go (0,0) . B.map decodeBase64 where
+  go (result,shift) bytes =
+    case B.uncons bytes of
+      Nothing -> result
+      Just (c,next) ->
+        let digit = fromIntegral c
+            continuation = (digit & continuationBit) /= 0
+            digit' = digit & baseMask
+            result' = result + (digit' << shift)
+            shift' = shift + baseShift
+        in if continuation
+              then go (result',shift') next
+              else result'
+
+-- | Base 64 characters.
+base64Chars :: [Word8]
+base64Chars = map (fromIntegral.fromEnum) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+
+-- | Encode the given number to a base 64 character.
+encodeBase64 :: Word8 -> Word8
+encodeBase64 i = maybe (error "Base 64 char must be between 0 and 63.")
+                       id
+                       (lookup i (zip [0..] base64Chars))
+
+-- | Encode the given base 64 character to a number.
+decodeBase64 :: Word8 -> Word8
+decodeBase64 i = maybe (error "Not a valid base 65 digit.")
+                       id
+                        (lookup i (zip base64Chars [0..]))
+
+-- | Makes the code more familiar to read. Shift-left.
+(<<) :: Int32 -> Int -> Int32
+(<<) = shiftL
+
+-- | Makes the code more familiar to read. Shift-right.
+(>>) :: Int32 -> Int -> Int32
+(>>) = shiftR
+
+-- | Makes the code more familiar to read. And.
+(&) :: Int32 -> Int32 -> Int32
+(&) = (.&.)
