diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2015, IMVU, Chad Austin, Andy Friesen
+
+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 Chad Austin 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/bench/Bench.hs b/bench/Bench.hs
new file mode 100644
--- /dev/null
+++ b/bench/Bench.hs
@@ -0,0 +1,49 @@
+{-# LANGUAGE OverloadedStrings, RecordWildCards #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE BangPatterns #-}
+
+module Main where
+
+import           Control.DeepSeq (force)
+import           Criterion
+import           Criterion.Main
+import qualified Data.Aeson as Aeson
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Lazy as BSL
+import qualified Data.BufferBuilder.Json as Json
+import qualified Data.BufferBuilder.Aeson ()
+import qualified Data.Text as Text
+import qualified Data.Vector as Vector
+import qualified Data.Vector.Unboxed as UnboxedVector
+
+assumeSuccess :: Either a b -> b
+assumeSuccess (Right r) = r
+assumeSuccess _ = error "assumeSuccess"
+
+main :: IO ()
+main = do
+    content <- BS.readFile "test.json"
+    let lazyContent = force $ BSL.fromChunks [content]
+
+    let parsedUserList :: [Aeson.Value]
+        Just parsedUserList = Aeson.decode lazyContent
+
+    let compareBench name !value =
+            bgroup name
+                [ bench "bufferbuilder" $ nf Json.encodeJson value
+                , bench "aeson"         $ nf Aeson.encode value
+                ]
+
+    defaultMain
+        [ compareBench "list bool" $ Aeson.Array $ Vector.fromList $ fmap Aeson.Bool $ replicate 65536 False
+        , compareBench "list null" $ Aeson.Array $ Vector.replicate 65536 Aeson.Null
+        , compareBench "list empty object" $ Aeson.Array $ Vector.replicate 65536 $ Aeson.object []
+        , compareBench "list empty array" $ Aeson.Array $ Vector.replicate 65536 $ Aeson.Array Vector.empty
+        , compareBench "list string" $ Aeson.Array $ Vector.fromList $ fmap (Aeson.String . Text.pack . show) ([0..65535] :: [Int])
+        , compareBench "list int" $ Aeson.Array $ Vector.fromList $ fmap (Aeson.Number . fromIntegral) ([0..65535] :: [Int])
+        , compareBench "hash string" $ Aeson.object $ fmap (\e -> (Text.pack $ show e, Aeson.Null)) ([0..65535] :: [Int])
+
+        , compareBench "list record" parsedUserList
+        , bench "intvector" $ nf Json.encodeJson (UnboxedVector.fromList $! [0..65535] :: UnboxedVector.Vector Int)
+        ]
diff --git a/buffer-builder-aeson.cabal b/buffer-builder-aeson.cabal
new file mode 100644
--- /dev/null
+++ b/buffer-builder-aeson.cabal
@@ -0,0 +1,78 @@
+-- Initial buffer-builder-aeson.cabal generated by cabal init.  For further
+--  documentation, see http://haskell.org/cabal/users-guide/
+
+name:                buffer-builder-aeson
+version:             0.1.0.1
+synopsis:            Serialize Aeson values with Data.BufferBuilder
+-- description:
+license:             MIT
+license-file:        LICENSE
+author:              Andy Friesen
+maintainer:          andy.friesen@gmail.com
+-- copyright:
+category:            Data
+build-type:          Simple
+-- extra-source-files:
+cabal-version:       >=1.10
+
+library
+  exposed-modules:     Data.BufferBuilder.Aeson
+  hs-source-dirs:      src
+  ghc-options: -O2 -Wall
+  -- ghc-options: -ddump-ds -ddump-simpl -ddump-stg -ddump-opt-cmm -ddump-asm -ddump-to-file
+
+  -- other-modules:
+  -- other-extensions:
+  build-depends:       base == 4.*
+                     , aeson
+                     , integer-gmp
+                     , buffer-builder >=0.2.0.0
+                     , unordered-containers
+                     , vector
+                     , scientific
+                     , bytestring
+  hs-source-dirs:
+  default-language:    Haskell2010
+
+test-suite tests
+  type: exitcode-stdio-1.0
+  main-is: Main.hs
+  hs-source-dirs: test
+  default-language: Haskell2010
+  ghc-options: -O2 -Wall
+
+  build-depends: base ==4.*
+               , buffer-builder-aeson
+               , buffer-builder
+               , text
+               , tasty
+               , tasty-hunit
+               , tasty-quickcheck
+               , tasty-th
+               , QuickCheck
+               , unordered-containers
+               , vector
+               , scientific
+               , bytestring
+               , HUnit
+               , text
+               , hashable
+               , attoparsec
+               , aeson
+
+benchmark json-bench
+  type: exitcode-stdio-1.0
+  main-is: Bench.hs
+  hs-source-dirs: bench
+  default-language: Haskell2010
+  ghc-options: -O2 -Wall
+  -- ghc-options: -ddump-ds -ddump-simpl -ddump-stg -ddump-opt-cmm -ddump-asm -ddump-to-file
+  build-depends: base
+               , buffer-builder
+               , buffer-builder-aeson
+               , aeson
+               , bytestring
+               , text
+               , deepseq
+               , criterion
+               , vector
diff --git a/src/Data/BufferBuilder/Aeson.hs b/src/Data/BufferBuilder/Aeson.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/BufferBuilder/Aeson.hs
@@ -0,0 +1,39 @@
+{-# LANGUAGE MagicHash #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-} 
+
+module Data.BufferBuilder.Aeson () where
+
+import GHC.Base
+import GHC.Integer.GMP.Internals
+import           Data.Aeson (Value (..))
+import           Data.BufferBuilder.Json (ToJson (..), nullValue, unsafeAppendBS, unsafeAppendUtf8Builder)
+import qualified Data.BufferBuilder.Json as Json
+import qualified Data.BufferBuilder.Utf8 as Utf8Builder
+import qualified Data.ByteString.Lazy as BSL
+import qualified Data.ByteString.Lazy.Builder as BB
+import qualified Data.ByteString.Builder.Scientific as BB
+import qualified Data.Scientific as Scientific
+
+-- TODO: this doesn't need to convert the bytestring to strict before appending it
+-- there is an appendBSL
+slowNumber :: Scientific.Scientific -> Json.Value
+slowNumber n = unsafeAppendBS
+                    $ BSL.toStrict
+                    $ BB.toLazyByteString
+                    $ BB.formatScientificBuilder BB.Fixed Nothing n
+
+instance ToJson Value where
+    {-# INLINE toJson #-}
+    toJson (Object o) = toJson o
+    toJson (Array a) = toJson a
+    toJson (String s) = toJson s
+    toJson (Number n) = case Scientific.coefficient n of
+        (S# smallcoeff) -> case Scientific.base10Exponent n of
+            0 -> toJson (I# smallcoeff)
+            exp' -> unsafeAppendUtf8Builder $ do
+                Utf8Builder.appendDecimalSignedInt (I# smallcoeff)
+                Utf8Builder.appendChar7 'e'
+                Utf8Builder.appendDecimalSignedInt exp'
+        _ -> slowNumber n
+    toJson (Bool b) = toJson b
+    toJson Null = nullValue
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,52 @@
+{-# LANGUAGE MagicHash, OverloadedStrings, TemplateHaskell #-}
+
+module Main where
+
+import           Test.Tasty
+import           Test.Tasty.TH
+import           Test.Tasty.HUnit
+import           Test.Tasty.QuickCheck
+import           Data.String (IsString (..))
+import           Data.BufferBuilder.Json
+import           Data.BufferBuilder.Aeson ()
+import           Data.ByteString (ByteString)
+import qualified Data.ByteString as BS
+import qualified Data.Attoparsec.ByteString as Atto
+import qualified Data.Aeson.Parser as JsonParse
+import qualified Data.Aeson as Aeson
+import           AesonQuickCheck ()
+import           Debug.Trace
+
+ae :: String -> ByteString -> IO ()
+ae expected actual = assertEqual expected (fromString expected) actual
+
+decodeJsonFragment :: Aeson.FromJSON j => BS.ByteString -> Maybe j
+decodeJsonFragment str = case parsed of
+    Right r -> case Aeson.fromJSON r of
+        Aeson.Success a -> Just a
+        _              -> Nothing
+    _ -> Nothing
+  where
+    parsed = Atto.parseOnly JsonParse.value' str
+
+case_serialize_simple_things :: IO ()
+case_serialize_simple_things = do
+    ae "false" (encodeJson $ Aeson.Bool False)
+    ae "true" (encodeJson $ Aeson.Bool True)
+    ae "null" (encodeJson $ Aeson.Null)
+    ae "9" (encodeJson $ Aeson.Number 9)
+    ae "-9" (encodeJson $ Aeson.Number (-9))
+
+prop_matches_aeson :: Aeson.Value -> Bool
+prop_matches_aeson value =
+    let encoded = encodeJson value
+        decoded = decodeJsonFragment encoded
+    in if decoded == Just value
+        then True
+        else trace (show (value, encoded, decoded)) False
+
+tests :: TestTree
+tests = $(testGroupGenerator)
+
+main :: IO ()
+main = defaultMain tests
