diff --git a/sodium.cabal b/sodium.cabal
--- a/sodium.cabal
+++ b/sodium.cabal
@@ -1,5 +1,5 @@
 name:                sodium
-version:             0.7.0.0
+version:             0.8.0.0
 synopsis:            Sodium Reactive Programming (FRP) System
 description:         
   A general purpose Reactive Programming (FRP) system. This is part of a project to
@@ -41,6 +41,8 @@
    * 0.6.0.2 - Fix memory leak - see memory-test-8 & memory-test-8a;
   .
    * 0.7.0.0 - Add split primitive.
+  .
+   * 0.8.0.0 - Add executeIO primitive.
 
 license:             BSD3
 license-file:        LICENSE
@@ -145,8 +147,12 @@
 
 library
   hs-source-dirs:      src
-  exposed-modules:     FRP.Sodium, FRP.Sodium.Internal, FRP.Sodium.Context
+  exposed-modules:     FRP.Sodium,
+                       FRP.Sodium.Internal,
+                       FRP.Sodium.Context,
+                       FRP.Sodium.IO
   other-modules:       FRP.Sodium.Plain
   build-depends:       base >= 4.3.0.0 && < 4.8.0.0,
                        containers >= 0.4.0.0 && < 0.6.0.0,
                        mtl >= 2.0.0.0 && < 2.2.0.0
+  ghc-options:         -O2
diff --git a/src/FRP/Sodium/Context.hs b/src/FRP/Sodium/Context.hs
--- a/src/FRP/Sodium/Context.hs
+++ b/src/FRP/Sodium/Context.hs
@@ -26,8 +26,6 @@
     --
     -- State changes to 'hold' values occur after processing of the transaction is complete.
     sync          :: Reactive r a -> IO a
-    -- Lift an arbitrary IO action into a 'Reactive'.
-    ioReactive    :: IO a -> Reactive r a
     -- | Returns an event, and a push action for pushing a value into the event.
     newEvent      :: Reactive r (Event r a, a -> Reactive r ())
     -- | Listen for firings of this event. The returned @IO ()@ is an IO action
@@ -88,6 +86,28 @@
     -- a block of input data into frames. We obviously want each frame to have
     -- its own transaction so that state is updated separately each frame.
     split         :: Event r [a] -> Event r a
+
+class Context r => ContextIO r where
+    -- | Execute the specified IO operation asynchronously on a separate thread, and
+    -- signal the output event in a new transaction upon its completion.
+    --
+    -- Caveat: Where 'switch' or 'switchE' is used, when some reactive logic has been
+    -- switched away, we rely on garbage collection to actually disconnect this logic
+    -- from any input it may be listening to. With normal Sodium code, everything is
+    -- pure, so before garbage collection happens, the worst we will get is some wasted
+    -- CPU cycles. If you are using 'executeAsyncIO'/'executeSyncIO' inside a 'switch'
+    -- or 'switchE', however, it is possible that logic that has been switched away
+    -- hasn't been garbage collected yet. This logic /could/ still run, and if it has
+    -- observable effects, you could see it running after it is supposed to have been
+    -- switched out. One way to avoid this is to pipe the source event for IO out of the
+    -- switch, run the 'executeAsyncIO'/'executeSyncIO' outside the switch, and pipe its
+    -- output back into the switch contents.
+    executeAsyncIO :: Event r (IO a) -> Event r a
+    -- | Execute the specified IO operation synchronously and fire the output event
+    -- in the same transaction.
+    --
+    -- Caveat: See 'executeAsyncIO'.
+    executeSyncIO  :: Event r (IO a) -> Event r a
 
 -- | A time-varying value, British spelling.
 type Behaviour r a = Behavior r a
diff --git a/src/FRP/Sodium/IO.hs b/src/FRP/Sodium/IO.hs
new file mode 100644
--- /dev/null
+++ b/src/FRP/Sodium/IO.hs
@@ -0,0 +1,48 @@
+module FRP.Sodium.IO where
+
+import FRP.Sodium.Context
+import FRP.Sodium.Internal
+
+import Control.Concurrent (forkIO)
+
+
+-- | Execute the specified IO operation asynchronously on a separate thread, and
+-- signal the output event in a new transaction upon its completion.
+--
+-- Caveat: Where 'switch' or 'switchE' is used, when some reactive logic has been
+-- switched away, we rely on garbage collection to actually disconnect this logic
+-- from any input it may be listening to. With normal Sodium code, everything is
+-- pure, so before garbage collection happens, the worst we will get is some wasted
+-- CPU cycles. If you are using 'executeAsyncIO'/'executeSyncIO' inside a 'switch'
+-- or 'switchE', however, it is possible that logic that has been switched away
+-- hasn't been garbage collected yet. This logic /could/ still run, and if it has
+-- observable effects, you could see it running after it is supposed to have been
+-- switched out. One way to avoid this is to pipe the source event for IO out of the
+-- switch, run the 'executeAsyncIO'/'executeSyncIO' outside the switch, and pipe its
+-- output back into the switch contents.
+executeAsyncIO :: Event Plain (IO a) -> Event Plain a
+executeAsyncIO ev = Event gl cacheRef (dep ev)
+  where
+    cacheRef = unsafeNewIORef Nothing ev
+    gl = do
+        (l, push, nodeRef) <- ioReactive newEventImpl
+        unlistener <- later $ linkedListen ev (Just nodeRef) False $ \action -> do
+            ioReactive $ do
+                _ <- forkIO $ sync . push =<< action
+                return ()
+        addCleanup_Listen unlistener l
+
+-- | Execute the specified IO operation synchronously and fire the output event
+-- in the same transaction.
+--
+-- Caveat: See 'executeAsyncIO'.
+executeSyncIO :: Event Plain (IO a) -> Event Plain a
+executeSyncIO ev = Event gl cacheRef (dep ev)
+  where
+    cacheRef = unsafeNewIORef Nothing ev
+    gl = do
+        (l, push, nodeRef) <- ioReactive newEventImpl
+        unlistener <- later $ linkedListen ev (Just nodeRef) False $ \action -> do
+            push =<< ioReactive action
+        addCleanup_Listen unlistener l
+
diff --git a/src/FRP/Sodium/Internal.hs b/src/FRP/Sodium/Internal.hs
--- a/src/FRP/Sodium/Internal.hs
+++ b/src/FRP/Sodium/Internal.hs
@@ -19,7 +19,10 @@
         addCleanup_Listen,
         Sample(..),
         addCleanup_Sample,
-        later
+        later,
+        dep,
+        unsafeNewIORef,
+        Plain
     ) where
 
 import qualified FRP.Sodium.Context as C
diff --git a/src/FRP/Sodium/Plain.hs b/src/FRP/Sodium/Plain.hs
--- a/src/FRP/Sodium/Plain.hs
+++ b/src/FRP/Sodium/Plain.hs
@@ -110,7 +110,6 @@
         }
 
     sync = sync
-    ioReactive = ioReactive
     newEvent = newEvent
     listen = listen
     never = never
