diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Revision history for essence-of-live-coding-warp
+
+## 0.2.1
+
+* Simple single-threaded version
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2018, Manuel Bärenz
+
+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 Manuel Bärenz 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/essence-of-live-coding-warp.cabal b/essence-of-live-coding-warp.cabal
new file mode 100644
--- /dev/null
+++ b/essence-of-live-coding-warp.cabal
@@ -0,0 +1,61 @@
+name:                essence-of-live-coding-warp
+version:             0.2.1
+synopsis: General purpose live coding framework
+description:
+  essence-of-live-coding is a general purpose and type safe live coding framework.
+  .
+  You can run programs in it, and edit, recompile and reload them while they're running.
+  Internally, the state of the live program is automatically migrated when performing hot code swap.
+  .
+  The library also offers an easy to use FRP interface.
+  It is parametrized by its side effects,
+  separates data flow cleanly from control flow,
+  and allows to develop live programs from reusable, modular components.
+  There are also useful utilities for debugging and quickchecking.
+  .
+  This library contains a single-threaded interface to the WARP web server.
+  WAI applications can be run this way.
+
+license:             BSD3
+license-file:        LICENSE
+author:              Manuel Bärenz
+maintainer:          programming@manuelbaerenz.de
+homepage:            https://www.manuelbaerenz.de/#computerscience
+category:            FRP, Live coding
+build-type:          Simple
+extra-source-files:  CHANGELOG.md
+cabal-version:       >=1.10
+
+source-repository head
+  type:     git
+  location: git@github.com:turion/essence-of-live-coding.git
+
+source-repository this
+  type:     git
+  location: git@github.com:turion/essence-of-live-coding.git
+  tag:      v0.2.1
+
+library
+  exposed-modules:
+      LiveCoding.Warp
+  other-extensions:    DeriveDataTypeable
+  build-depends:
+      base >= 4.11 && < 5
+    , http-types >= 0.12.3
+    , wai >= 3.2.2.1
+    , warp >= 3.3.13
+    , essence-of-live-coding >= 0.2.1
+  hs-source-dirs:      src
+  default-language:    Haskell2010
+  default-extensions:  StrictData
+
+test-suite test
+  type: exitcode-stdio-1.0
+  main-is: Main.hs
+  hs-source-dirs: test
+  build-depends:
+      base >= 4.11 && < 5
+    , http-client >= 0.7.1
+    , essence-of-live-coding >= 0.2.1
+    , essence-of-live-coding-warp
+  default-language:    Haskell2010
diff --git a/src/LiveCoding/Warp.hs b/src/LiveCoding/Warp.hs
new file mode 100644
--- /dev/null
+++ b/src/LiveCoding/Warp.hs
@@ -0,0 +1,73 @@
+{-# LANGUAGE Arrows #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE TupleSections #-}
+module LiveCoding.Warp
+  ( runWarpC
+  , runWarpC_
+  , module X
+  ) where
+
+-- base
+import Control.Concurrent
+import Control.Monad.IO.Class
+
+-- http-types
+import Network.HTTP.Types as X
+
+-- wai
+import Network.Wai as X
+
+-- warp
+import Network.Wai.Handler.Warp
+
+-- essence-of-live-coding
+import LiveCoding
+
+data WaiHandle = WaiHandle
+  { requestVar  :: MVar Request
+  , responseVar :: MVar Response
+  , appThread   :: ThreadId
+  }
+
+waiHandle :: Port -> Handle IO WaiHandle
+waiHandle port = Handle
+  { create = do
+      requestVar <- newEmptyMVar
+      responseVar <- newEmptyMVar
+      let app request respond = do
+              putMVar requestVar request
+              response <- takeMVar responseVar
+              respond response
+      appThread <- forkIO $ run port app
+      return WaiHandle { .. }
+  , destroy = \WaiHandle { .. } -> killThread appThread
+  }
+
+{- | Run a 'Cell' as a WARP application.
+
+1. Starts a WARP application on the given port in a background thread
+2. Block until the next request arrives
+3. Supplies the cell with the input and the current request
+4. Serve the response and return the output
+
+Keep in mind that the resulting cell is blocking.
+For a non-blocking cell, use 'LiveCoding.NonBlocking'.
+-}
+
+runWarpC
+  :: Port
+  -> Cell IO (a, Request) (b, Response)
+  -> Cell (HandlingStateT IO) a b
+runWarpC port cell = proc a -> do
+  WaiHandle { .. } <- handling $ waiHandle port -< ()
+  request <- arrM $ liftIO . takeMVar           -< requestVar
+  (b, response) <- liftCell cell                     -< (a, request)
+  arrM $ liftIO . uncurry putMVar               -< (responseVar, response)
+  returnA                                       -< b
+
+
+runWarpC_
+  :: Port
+  -> Cell IO Request Response
+  -> Cell (HandlingStateT IO) () ()
+runWarpC_ port cell = runWarpC port $ arr snd >>> cell >>> arr ((), )
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,27 @@
+{-# LANGUAGE Arrows #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+-- base
+import System.Exit
+
+-- http-client
+import Network.HTTP.Client hiding (Response)
+
+-- essence-of-live-coding
+import LiveCoding
+
+-- essence-of-live-coding-warp
+import LiveCoding.Warp
+
+response :: Response
+response = responseLBS status200 [("Content-Type", "text/plain")] "hai"
+
+main :: IO ()
+main = do
+  launch $ liveCell $ runHandlingStateC $ runWarpC_ 8080 $ arr $ const response
+  manager <- newManager $ defaultManagerSettings
+  request <- parseRequest "http://localhost:8080"
+  response <- httpLbs request manager
+  if responseBody response == "hai"
+    then return ()
+    else exitFailure
