diff --git a/Control/Broccoli.hs b/Control/Broccoli.hs
new file mode 100644
--- /dev/null
+++ b/Control/Broccoli.hs
@@ -0,0 +1,248 @@
+-- | Small experimental library for interactive functional programs.
+
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE RankNTypes #-}
+module Control.Broccoli (
+  X,
+  E,
+  Output(..),
+  runProgram,
+  edge,
+  accumulate,
+  snapshot,
+  snapshot_,
+  out,
+  filterE,
+  justE,
+  maybeE,
+  never,
+  debugX,
+  debugE,
+  newX,
+  newE
+) where
+
+import Control.Applicative
+import Data.Functor
+import Data.Monoid
+import Control.Monad
+import Data.Unamb
+import Data.IORef
+import Control.Concurrent
+import Control.Concurrent.STM
+import Data.Function
+import System.IO.Unsafe
+
+-- | A value of type a that varies.
+data X a where
+  PureX :: a -> X a
+  FmapX :: forall a b . (b -> a) -> X b -> X a
+  ApplX :: forall a b . X (b -> a) -> X b -> X a
+  PortX :: TVar a -> X a
+
+-- | An event that carries values of type a when it occurs.
+data E a where
+  NeverE    :: E a
+  FmapE     :: forall a b . (b -> a) -> E b -> E a
+  MappendE  :: E a -> E a -> E a
+  ProductE  :: (b -> c -> a) -> E b -> E c -> E a
+  SnapshotE :: E b -> X a -> E a
+  JustE     :: E (Maybe a) -> E a
+  PortE     :: TChan a -> E a
+
+-- | A special IO action which will react to events. See 'out'.
+newtype Output = Output (IO ())
+
+instance Functor X where
+  fmap f x = FmapX f x
+
+instance Applicative X where
+  pure x = PureX x
+  f <*> x = ApplX f x
+
+instance Functor E where
+  fmap f e = FmapE f e
+
+instance Monoid (E a) where
+  mempty = NeverE
+  mappend e1 e2 = MappendE e1 e2
+
+dupE :: E a -> IO (E a)
+dupE e = case e of
+  NeverE -> return NeverE
+  FmapE f e' -> do
+    e'' <- dupE e'
+    return (FmapE f e'')
+  MappendE e1 e2 -> do
+    e1' <- dupE e1
+    e2' <- dupE e2
+    return (MappendE e1' e2')
+  PortE ch -> do
+    ch' <- atomically (dupTChan ch)
+    return (PortE ch')
+  ProductE f e1 e2 -> do
+    e1' <- dupE e1
+    e2' <- dupE e2
+    return (ProductE f e1' e2')
+  SnapshotE e' x -> do
+    e'' <- dupE e'
+    return (SnapshotE e'' x)
+  JustE e' -> do
+    e'' <- dupE e'
+    return (JustE e'')
+
+readE :: E a -> IO a
+readE e = case e of
+  NeverE -> hang
+  PortE ch -> atomically (readTChan ch)
+  MappendE e1 e2 -> race (readE e1) (readE e2)
+  FmapE f e' -> f <$> readE e'
+  ProductE f e1 e2 -> do
+    x <- readE e1
+    y <- readE e2
+    return (f x y)
+  SnapshotE e' x -> do
+    readE e'
+    atomically (readX x)
+  JustE e' -> fix $ \loop -> do
+    m <- readE e'
+    case m of
+      Nothing -> loop
+      Just x  -> return x
+
+readX :: X a -> STM a
+readX x = case x of
+  PureX v -> return v
+  FmapX f xx -> f <$> readX xx
+  ApplX ff xx -> do
+    f <- readX ff
+    x <- readX xx
+    return (f x)
+  PortX tv -> readTVar tv
+
+hang :: IO a
+hang = do
+  threadDelay (100 * 10^(6::Int))
+  hang
+
+waitE :: E a -> IO a
+waitE e0 = do
+  e <- dupE e0
+  readE e
+
+runEvent :: E a -> (a -> IO ()) -> IO ()
+runEvent e0 act = do
+  e <- dupE e0
+  forever (readE e >>= act)
+
+---
+
+-- | Create a new signal with an initial value. Get back an IO action to
+-- change the value of the signal. This is the only way the signal will
+-- change.
+newX :: a -> IO (a -> IO (), X a)
+newX v0 = do
+  tv <- newTVarIO v0
+  return
+    ( \x -> atomically (writeTVar tv x)
+    , PortX tv )
+
+-- | Create a new event and the associated IO action to trigger the event.
+newE :: IO (a -> IO (), E a)
+newE = do
+  ch <- newBroadcastTChanIO
+  return
+    ( \x -> atomically (writeTChan ch x)
+    , PortE ch )
+
+-- | An event which gets the value of a signal when another event occurs.
+snapshot :: E a -> X b -> E (a,b)
+snapshot e x = ProductE (,) e (SnapshotE e x)
+
+-- | Like snapshot but ignores the original event's payload.
+snapshot_ :: E a -> X b -> E b
+snapshot_ e x = SnapshotE e x
+
+-- | Filter out events with the value of Nothing.
+justE :: E (Maybe a) -> E a
+justE = JustE
+
+-- | Filter out events using a Maybe function.
+maybeE :: (a -> Maybe b) -> E a -> E b
+maybeE f e = justE (f <$> e)
+
+-- | Filter out events using a Bool function.
+filterE :: (a -> Bool) -> E a -> E a
+filterE p e = maybeE (\x -> if p x then Just x else Nothing) e
+
+-- | An event that never happens.
+never :: E a
+never = mempty
+
+-- | Events will occur when an "edge" is detected in a signal. The detection
+-- algorithm checks the two values before and after a discrete change in
+-- the signal.
+edge :: X a -> (a -> a -> Maybe b) -> E b
+edge x diff = PortE ch where
+  ch = unsafePerformIO $ do
+    out <- newBroadcastTChanIO
+    forkIO $ do
+      v0 <- atomically (readX x)
+      ref <- newIORef v0
+      forever $ do
+        v <- readIORef ref
+        (d, v') <- atomically $ do
+          v' <- readX x
+          case diff v v' of
+            Just d  -> return (d, v')
+            Nothing -> retry
+        writeIORef ref v'
+        atomically (writeTChan out d)
+    return out
+
+-- | Create a signal out of an input event and a state machine.
+accumulate :: E a -> s -> (a -> s -> s) -> X s
+accumulate e0 s0 trans = PortX tv where
+  tv = unsafePerformIO $ do
+    state <- newTVarIO s0
+    forkIO $ do
+      e <- dupE e0
+      forever $ do
+        x <- readE e
+        atomically $ do
+          s <- readTVar state
+          let s' = trans x s
+          writeTVar state s'
+    return state
+
+-- | A handler for events.
+out :: E a -> (a -> IO ()) -> Output
+out e0 act = Output (runEvent e0 act)
+
+-- | Prints the values of a signal as they change.
+debugX :: (Eq a, Show a) => X a -> Output
+debugX x = Output $ do
+  v0 <- atomically (readX x)
+  print v0
+  let diff a b = if a == b then Nothing else Just b
+  runEvent (edge x diff) print
+
+-- | Prints the values of events as they occur.
+debugE :: (Show a) => E a -> Output
+debugE e = out e print
+
+-- | Run a set of Outputs. This spawns several threads then waits for an
+-- event. The output threads will then be killed to stop further processing.
+-- However other threads which no longer have any effect will probably remain,
+-- taking up resources. After threads have been spawned but before waiting,
+-- the given "boot" action will be executed.
+runProgram :: IO () -- ^ action to execute after initialization
+           -> E ()  -- ^ event that will shutdown the system
+           -> [Output] -- ^ set of output event handlers to run
+           -> IO ()
+runProgram notifyBoot stop outs = do
+  tids <- forM outs (\(Output io) -> forkIO io)
+  threadDelay 5000
+  notifyBoot
+  waitE stop
+  forM_ tids killThread
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2015, Evan Rinehart
+
+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 Evan Rinehart 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/broccoli.cabal b/broccoli.cabal
new file mode 100644
--- /dev/null
+++ b/broccoli.cabal
@@ -0,0 +1,35 @@
+-- Initial broccoli.cabal generated by cabal init.  For further 
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+name:                broccoli
+version:             0.1.0.0
+synopsis:            Small library for interactive functional programs.
+description:         Small library for interactive functional programs.
+license:             BSD3
+license-file:        LICENSE
+author:              Evan Rinehart
+maintainer:          evanrinehart@gmail.com
+-- copyright:           
+category:            FRP
+build-type:          Simple
+-- extra-source-files:  
+cabal-version:       >=1.10
+
+library
+  exposed-modules:     Control.Broccoli
+  -- other-modules:       
+  other-extensions:    GADTs, RankNTypes
+  build-depends:       base >=4.7 && <4.8,
+                       unamb >=0.2 && <0.3,
+                       stm >=2.4 && <2.5
+  -- hs-source-dirs:      
+  default-language:    Haskell2010
+
+source-repository head
+  type: git
+  location: https://github.com/evanrinehart/broccoli
+
+source-repository this
+  type: git
+  location: https://github.com/evanrinehart/broccoli
+  tag: 0.1.0.0
