diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Author name here (c) 2017
+
+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 Author name here 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/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,1 @@
+# websockets-simple
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/src/Network/WebSockets/Simple.hs b/src/Network/WebSockets/Simple.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/WebSockets/Simple.hs
@@ -0,0 +1,95 @@
+{-# LANGUAGE
+    DeriveGeneric
+  , DeriveDataTypeable
+  , RankNTypes
+  , ScopedTypeVariables
+  , NamedFieldPuns
+  , FlexibleContexts
+  #-}
+
+module Network.WebSockets.Simple where
+
+import Network.WebSockets (DataMessage (..), sendTextData, receiveDataMessage, acceptRequest)
+import Network.Wai.Trans (ServerAppT, ClientAppT)
+import Data.Aeson (ToJSON (..), FromJSON (..))
+import qualified Data.Aeson as Aeson
+import Data.ByteString.Lazy (ByteString)
+
+import Control.Monad (void, forever)
+import Control.Monad.Catch (Exception, throwM, MonadThrow)
+import Control.Monad.Trans.Control (MonadBaseControl (..))
+import Control.Concurrent.Async (Async, async)
+import Control.Concurrent.STM (atomically)
+import Control.Concurrent.STM.TChan (TChan, newTChanIO, writeTChan)
+
+import GHC.Generics (Generic)
+import Data.Typeable (Typeable)
+
+
+data WebSocketsApp send receive m = WebSocketsApp
+  { onOpen    :: (send -> m ()) -> m ()
+  , onReceive :: (send -> m ()) -> receive -> m ()
+  } deriving (Generic, Typeable)
+
+
+-- | This can throw a 'WebSocketSimpleError' when json parsing fails. However, do note:
+--   the 'onOpen' is called once, but is still forked when called. Likewise, the 'onReceive'
+--   function is called /every time/ a (parsable) response is received from the other party,
+--   and is forked on every invocation.
+toClientAppT :: forall send receive m
+              . ( ToJSON send
+                , FromJSON receive
+                , MonadBaseControl IO m
+                , MonadThrow m
+                )
+             => WebSocketsApp send receive m
+             -> ClientAppT m WebSocketsAppThreads
+toClientAppT WebSocketsApp{onOpen,onReceive} = \conn -> do
+  let send :: send -> m ()
+      send x = liftBaseWith $ \_ -> sendTextData conn (Aeson.encode x)
+
+  onOpenThread <- liftBaseWith $ \runToBase ->
+    async $ void $ runToBase $ onOpen send
+
+  onReceiveThreads <- liftBaseWith $ \_ -> newTChanIO
+
+  forever $ do
+    data' <- liftBaseWith $ \_ -> receiveDataMessage conn
+    let data'' = case data' of
+                   Text xs -> xs
+                   Binary xs -> xs
+    case Aeson.decode data'' of
+      Nothing -> throwM (JSONParseError data'')
+      Just received -> liftBaseWith $ \runToBase -> do
+        thread <- async $ void $ runToBase $ onReceive send received
+        atomically $ writeTChan onReceiveThreads thread
+
+  pure WebSocketsAppThreads
+    { onOpenThread
+    , onReceiveThreads
+    }
+
+
+
+toClientAppT' :: (ToJSON send, FromJSON receive, MonadBaseControl IO m, MonadThrow m) => WebSocketsApp send receive m -> ClientAppT m ()
+toClientAppT' wsApp conn = void (toClientAppT wsApp conn)
+
+
+toServerAppT :: (ToJSON send, FromJSON receive, MonadBaseControl IO m, MonadThrow m) => WebSocketsApp send receive m -> ServerAppT m
+toServerAppT wsApp pending = do
+  conn <- liftBaseWith $ \_ -> acceptRequest pending
+  toClientAppT' wsApp conn
+
+
+
+data WebSocketSimpleError
+  = JSONParseError ByteString
+  deriving (Generic, Eq, Show)
+
+instance Exception WebSocketSimpleError
+
+
+data WebSocketsAppThreads = WebSocketsAppThreads
+  { onOpenThread     :: Async ()
+  , onReceiveThreads :: TChan (Async ())
+  }
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,2 @@
+main :: IO ()
+main = putStrLn "Test suite not yet implemented"
diff --git a/websockets-simple.cabal b/websockets-simple.cabal
new file mode 100644
--- /dev/null
+++ b/websockets-simple.cabal
@@ -0,0 +1,41 @@
+name:                websockets-simple
+version:             0.0.1
+synopsis:            Simpler interface to the websockets api
+-- description:
+homepage:            https://github.com/athanclark/websockets-simple#readme
+license:             BSD3
+license-file:        LICENSE
+author:              Athan Clark
+maintainer:          athan.clark@gmail.com
+copyright:           2017 Athan Clark
+category:            Web
+build-type:          Simple
+extra-source-files:  README.md
+cabal-version:       >=1.10
+
+library
+  hs-source-dirs:      src
+  exposed-modules:     Network.WebSockets.Simple
+  build-depends:       base >= 4.8 && < 5
+                     , aeson
+                     , async
+                     , bytestring
+                     , exceptions
+                     , monad-control
+                     , stm
+                     , wai-transformers
+                     , websockets
+  default-language:    Haskell2010
+
+test-suite websockets-simple-test
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      test
+  main-is:             Spec.hs
+  build-depends:       base
+                     , websockets-simple
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N
+  default-language:    Haskell2010
+
+source-repository head
+  type:     git
+  location: https://github.com/githubuser/websockets-simple
