diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2014 Ian Duncan
+
+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/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/engine-io-growler.cabal b/engine-io-growler.cabal
new file mode 100644
--- /dev/null
+++ b/engine-io-growler.cabal
@@ -0,0 +1,42 @@
+-- Initial growler-socket-io.cabal generated by cabal init.  For further 
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+name:                engine-io-growler
+version:             0.1.0.0
+-- synopsis:            
+description:         This package provides an @engine-io@ @ServerAPI@ that is compatible with
+  <http://http://hackage.haskell.org/package/growler Growler>. It also provides a few helper utilities for setting up @socket-io@
+homepage:            http://github.com/iand675/growler
+license:             MIT
+license-file:        LICENSE
+author:              Ian Duncan
+maintainer:          ian@iankduncan.com
+-- copyright:           
+category:            Web
+build-type:          Simple
+-- extra-source-files:  
+cabal-version:       >=1.10
+
+library
+  exposed-modules:     Network.EngineIO.Growler,
+                       Network.SocketIO.Growler
+  -- other-modules:       
+  -- other-extensions:    
+  build-depends:       base >=4.6 && <5,
+                       growler == 0.6.*,
+                       wai >=3.0 && < 4,
+                       engine-io == 1.2.*,
+                       socket-io == 1.3.*,
+                       websockets == 0.9.*,
+                       wai-websockets == 3.*,
+                       text,
+                       bytestring,
+                       unordered-containers,
+                       mtl,
+                       pipes == 4.*,
+                       pipes-attoparsec == 0.5.*,
+                       pipes-wai == 3.*,
+                       transformers,
+                       http-types == 0.8.*
+  hs-source-dirs:      src
+  default-language:    Haskell2010
diff --git a/src/Network/EngineIO/Growler.hs b/src/Network/EngineIO/Growler.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/EngineIO/Growler.hs
@@ -0,0 +1,54 @@
+{-# LANGUAGE OverloadedStrings #-}
+module Network.EngineIO.Growler where
+import           Control.Applicative ((<$>))
+import           Control.Arrow (second)
+import           Control.Monad.Reader
+import           Control.Monad.Trans.State.Strict (evalStateT)
+import qualified Data.ByteString.Builder as B
+import qualified Data.HashMap.Strict as H
+import           Data.Maybe (maybeToList)
+import           Network.EngineIO hiding (Socket, initialize)
+import qualified Network.HTTP.Types.Status as S
+import           Network.Wai (queryString, requestMethod, responseLBS)
+import qualified Network.Wai.Handler.WebSockets as WaiWS
+import qualified Network.WebSockets as WS
+import           Web.Growler (HandlerT, bytestring, request, raw, setHeader, status, abort, currentResponse)
+import           Pipes.Attoparsec (parse)
+import           Pipes.Wai (producerRequestBody)
+
+growlerAPI :: MonadIO m => ServerAPI (HandlerT m)
+growlerAPI = ServerAPI
+  { srvTerminateWithResponse = \code ct b -> do
+      let aStatus = filter ((==) code . S.statusCode) [S.status100..S.status511]
+      case aStatus of
+        [] -> error "not a valid status code"
+        (st:_) -> do
+          status st
+          setHeader "Content-Type" ct
+          bytestring $ B.toLazyByteString b
+          currentResponse >>= abort
+          undefined
+
+  , srvGetQueryParams = makeParams <$> request
+
+  , srvParseRequestBody = \p -> do
+      r <- request
+      eps <- evalStateT (parse p) $ producerRequestBody r
+      return $ case eps of
+        Nothing -> Left "Exhausted request body"
+        Just ps -> case ps of
+          Left err -> Left $ show err
+          Right res -> Right res
+
+  , srvGetRequestMethod = requestMethod <$> request
+
+  , srvRunWebSocket = \app -> do
+      r <- request
+      unless (WaiWS.isWebSocketsReq r) undefined
+      let socketRunner src sink = liftIO $ WaiWS.runWebSockets WS.defaultConnectionOptions
+            (WaiWS.getRequestHead r) app src sink
+      raw socketRunner $ responseLBS S.status404 [] ""
+  }
+  where
+    makeParams = H.fromListWith (++) . map (second maybeToList) . queryString
+
diff --git a/src/Network/SocketIO/Growler.hs b/src/Network/SocketIO/Growler.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/SocketIO/Growler.hs
@@ -0,0 +1,13 @@
+module Network.SocketIO.Growler where
+import           Control.Monad.Trans (MonadIO)
+import           Control.Monad.Trans.Reader (ReaderT)
+import           Control.Monad.Trans.State.Strict (StateT)
+import           Network.SocketIO (initialize, RoutingTable, Socket)
+import           Web.Growler (GrowlerT, HandlerT, matchAny, literal)
+import           Network.EngineIO.Growler (growlerAPI)
+
+initializeSocketIO :: MonadIO m => StateT RoutingTable (ReaderT Socket (HandlerT m)) a -> IO (HandlerT m ())
+initializeSocketIO = initialize growlerAPI
+
+socketIOHandler :: MonadIO m => HandlerT m () -> GrowlerT m ()
+socketIOHandler = matchAny (literal "/socket.io")
