diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -4,6 +4,14 @@
 Since `rhine` reexports modules from `dunai`,
 every major version in `dunai` triggers a major version in `rhine`.
 
+## 0.7.0
+
+* Replaced old reactimation mechanism by clock erasure
+* Dropped GHC support for < 8.4
+* Reworked `gloss` backends.
+  There are now two pure backends and an `IO` backend.
+* Relaxed all upper version bounds
+
 ## 0.6.0
 
 * Synced with `dunai` version numbers
diff --git a/rhine.cabal b/rhine.cabal
--- a/rhine.cabal
+++ b/rhine.cabal
@@ -1,6 +1,6 @@
 name:                rhine
 
-version:             0.6.0
+version:             0.7.0
 
 synopsis: Functional Reactive Programming with type-level clocks
 
@@ -46,7 +46,7 @@
 source-repository this
   type:     git
   location: git@github.com:turion/rhine.git
-  tag:      v0.6.0
+  tag:      v0.7.0
 
 
 library
@@ -54,14 +54,16 @@
     Control.Monad.Schedule
     FRP.Rhine
     FRP.Rhine.Clock
+    FRP.Rhine.Clock.FixedStep
     FRP.Rhine.Clock.Periodic
+    FRP.Rhine.Clock.Proxy
     FRP.Rhine.Clock.Realtime.Audio
     FRP.Rhine.Clock.Realtime.Busy
     FRP.Rhine.Clock.Realtime.Event
     FRP.Rhine.Clock.Realtime.Millisecond
     FRP.Rhine.Clock.Realtime.Stdin
     FRP.Rhine.Clock.Select
-    FRP.Rhine.Clock.FixedStep
+    FRP.Rhine.Clock.Util
     FRP.Rhine.ClSF
     FRP.Rhine.ClSF.Core
     FRP.Rhine.ClSF.Except
@@ -70,7 +72,7 @@
     FRP.Rhine.ClSF.Upsample
     FRP.Rhine.ClSF.Util
     FRP.Rhine.Reactimation
-    FRP.Rhine.Reactimation.Tick
+    FRP.Rhine.Reactimation.ClockErasure
     FRP.Rhine.Reactimation.Combinators
     FRP.Rhine.ResamplingBuffer
     FRP.Rhine.ResamplingBuffer.Collect
@@ -100,14 +102,14 @@
   -- Other library packages from which modules are imported.
   build-depends:       base         >= 4.9 && < 5
                      , dunai        >= 0.6
-                     , transformers == 0.5.*
+                     , transformers >= 0.5
                      , time         >= 1.8
-                     , free         == 5.1.*
-                     , containers   == 0.6.*
-                     , vector-sized == 1.4.*
-                     , deepseq      == 1.4.*
-                     , random       == 1.1.*
-                     , MonadRandom  == 0.5.*
+                     , free         >= 5.1
+                     , containers   >= 0.5
+                     , vector-sized >= 1.4
+                     , deepseq      >= 1.4
+                     , random       >= 1.1
+                     , MonadRandom  >= 0.5
                      , simple-affine-space
 
   -- Directories containing source files.
diff --git a/src/FRP/Rhine.hs b/src/FRP/Rhine.hs
--- a/src/FRP/Rhine.hs
+++ b/src/FRP/Rhine.hs
@@ -20,6 +20,8 @@
 
 -- rhine
 import FRP.Rhine.Clock                    as X
+import FRP.Rhine.Clock.Proxy              as X
+import FRP.Rhine.Clock.Util               as X
 import FRP.Rhine.ClSF                     as X
 import FRP.Rhine.Reactimation             as X
 import FRP.Rhine.Reactimation.Combinators as X
diff --git a/src/FRP/Rhine/Clock.hs b/src/FRP/Rhine/Clock.hs
--- a/src/FRP/Rhine/Clock.hs
+++ b/src/FRP/Rhine/Clock.hs
@@ -70,7 +70,6 @@
     :: cl -- ^ The clock value, containing e.g. settings or device parameters
     -> RunningClockInit m (Time cl) (Tag cl) -- ^ The stream of time stamps, and the initial time
 
-
 -- * Auxiliary definitions and utilities
 
 -- | An annotated, rich time stamp.
@@ -91,22 +90,6 @@
   => (Tag cl1 -> Tag cl2)
   -> TimeInfo cl1 -> TimeInfo cl2
 retag f TimeInfo {..} = TimeInfo { tag = f tag, .. }
-
-
--- | Given a clock value and an initial time,
---   generate a stream of time stamps.
-genTimeInfo
-  :: (Monad m, Clock m cl)
-  => cl -> Time cl
-  -> MSF m (Time cl, Tag cl) (TimeInfo cl)
-genTimeInfo _ initialTime = proc (absolute, tag) -> do
-  lastTime <- iPre initialTime -< absolute
-  returnA                      -< TimeInfo
-    { sinceLast = absolute `diffTime` lastTime
-    , sinceInit = absolute `diffTime` initialTime
-    , ..
-    }
-
 
 -- * Certain universal building blocks to produce new clocks from given ones
 
diff --git a/src/FRP/Rhine/Clock/FixedStep.hs b/src/FRP/Rhine/Clock/FixedStep.hs
--- a/src/FRP/Rhine/Clock/FixedStep.hs
+++ b/src/FRP/Rhine/Clock/FixedStep.hs
@@ -26,6 +26,7 @@
 
 -- rhine
 import FRP.Rhine.Clock
+import FRP.Rhine.Clock.Proxy
 import FRP.Rhine.ResamplingBuffer
 import FRP.Rhine.ResamplingBuffer.Collect
 import FRP.Rhine.ResamplingBuffer.Util
@@ -50,6 +51,8 @@
       &&& arr (const ())
     , 0
     )
+
+instance GetClockProxy (FixedStep n)
 
 -- | A singleton clock that counts the ticks.
 type Count = FixedStep 1
diff --git a/src/FRP/Rhine/Clock/Periodic.hs b/src/FRP/Rhine/Clock/Periodic.hs
--- a/src/FRP/Rhine/Clock/Periodic.hs
+++ b/src/FRP/Rhine/Clock/Periodic.hs
@@ -27,6 +27,7 @@
 
 -- rhine
 import FRP.Rhine.Clock
+import FRP.Rhine.Clock.Proxy
 import Control.Monad.Schedule
 
 -- * The 'Periodic' clock
@@ -48,6 +49,8 @@
     ( cycleS (theList cl) >>> withSideEffect wait >>> (accumulateWith (+) 0) &&& arr (const ())
     , 0
     )
+
+instance GetClockProxy (Periodic v)
 
 -- * Type-level trickery to extract the type value from the singleton
 
diff --git a/src/FRP/Rhine/Clock/Proxy.hs b/src/FRP/Rhine/Clock/Proxy.hs
new file mode 100644
--- /dev/null
+++ b/src/FRP/Rhine/Clock/Proxy.hs
@@ -0,0 +1,88 @@
+{-# LANGUAGE DefaultSignatures #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE TypeFamilies #-}
+module FRP.Rhine.Clock.Proxy where
+
+-- base
+import Data.Kind (Type)
+
+-- rhine
+import FRP.Rhine.Clock
+import FRP.Rhine.Schedule
+
+-- | Witnesses the structure of a clock type,
+--   in particular whether 'SequentialClock's or 'ParallelClock's are involved.
+data ClockProxy cl where
+  LeafProxy
+    :: (cl ~ In cl, cl ~ Out cl)
+    => ClockProxy cl
+  SequentialProxy
+    :: ClockProxy cl1
+    -> ClockProxy cl2
+    -> ClockProxy (SequentialClock m cl1 cl2)
+  ParallelProxy
+    :: ClockProxy clL
+    -> ClockProxy clR
+    -> ClockProxy (ParallelClock m clL clR)
+
+inProxy :: ClockProxy cl -> ClockProxy (In cl)
+inProxy LeafProxy = LeafProxy
+inProxy (SequentialProxy p1 p2) = inProxy p1
+inProxy (ParallelProxy pL pR) = ParallelProxy (inProxy pL) (inProxy pR)
+
+outProxy :: ClockProxy cl -> ClockProxy (Out cl)
+outProxy LeafProxy = LeafProxy
+outProxy (SequentialProxy p1 p2) = outProxy p2
+outProxy (ParallelProxy pL pR) = ParallelProxy (outProxy pL) (outProxy pR)
+
+-- | Return the incoming tag, assuming that the incoming clock is ticked,
+--   and 'Nothing' otherwise.
+inTag :: ClockProxy cl -> Tag cl -> Maybe (Tag (In cl))
+inTag (SequentialProxy p1 _) (Left  tag1) = inTag p1 tag1
+inTag (SequentialProxy _  _) (Right _)    = Nothing
+inTag (ParallelProxy pL _) (Left  tagL) = Left  <$> inTag pL tagL
+inTag (ParallelProxy _ pR) (Right tagR) = Right <$> inTag pR tagR
+inTag LeafProxy tag = Just tag
+
+-- | Return the incoming tag, assuming that the outgoing clock is ticked,
+--   and 'Nothing' otherwise.
+outTag :: ClockProxy cl -> Tag cl -> Maybe (Tag (Out cl))
+outTag (SequentialProxy _ _ ) (Left  _)    = Nothing
+outTag (SequentialProxy _ p2) (Right tag2) = outTag p2 tag2
+outTag (ParallelProxy pL _) (Left  tagL) = Left  <$> outTag pL tagL
+outTag (ParallelProxy _ pR) (Right tagR) = Right <$> outTag pR tagR
+outTag LeafProxy tag = Just tag
+
+-- TODO Should this be a superclass with default implementation of clocks? But then we have a circular dependency...
+-- No we don't, Schedule should not depend on clock (the type).
+-- | Clocks should be able to automatically generate a proxy for themselves.
+class GetClockProxy cl where
+  getClockProxy :: ClockProxy cl
+
+  default getClockProxy
+    :: (cl ~ In cl, cl ~ Out cl)
+    => ClockProxy cl
+  getClockProxy = LeafProxy
+
+instance (GetClockProxy cl1, GetClockProxy cl2) => GetClockProxy (SequentialClock m cl1 cl2) where
+  getClockProxy = SequentialProxy getClockProxy getClockProxy
+
+instance (GetClockProxy cl1, GetClockProxy cl2) => GetClockProxy (ParallelClock m cl1 cl2) where
+  getClockProxy = ParallelProxy getClockProxy getClockProxy
+
+instance GetClockProxy cl => GetClockProxy (HoistClock m1 m2 cl)
+instance GetClockProxy cl => GetClockProxy (RescaledClock cl time)
+instance GetClockProxy cl => GetClockProxy (RescaledClockM m cl time)
+instance GetClockProxy cl => GetClockProxy (RescaledClockS m cl time tag)
+
+-- | Extract a clock proxy from a type.
+class ToClockProxy a where
+  type Cl a :: Type
+
+  toClockProxy :: a -> ClockProxy (Cl a)
+
+  default toClockProxy
+    :: GetClockProxy (Cl a)
+    => a -> ClockProxy (Cl a)
+  toClockProxy _ = getClockProxy
diff --git a/src/FRP/Rhine/Clock/Realtime/Audio.hs b/src/FRP/Rhine/Clock/Realtime/Audio.hs
--- a/src/FRP/Rhine/Clock/Realtime/Audio.hs
+++ b/src/FRP/Rhine/Clock/Realtime/Audio.hs
@@ -36,6 +36,7 @@
 
 -- rhine
 import FRP.Rhine.Clock
+import FRP.Rhine.Clock.Proxy
 
 -- | Rates at which audio signals are typically sampled.
 data AudioRate
@@ -122,6 +123,8 @@
       , initialTime
       )
 
+instance GetClockProxy (AudioClock rate bufferSize)
+
 {- |
 A side-effect free clock for audio synthesis and analysis.
 The sample rate is given by 'rate' (of type 'AudioRate').
@@ -149,6 +152,7 @@
     , 0
     )
 
+instance GetClockProxy (PureAudioClock rate)
 
 -- | A rescaled version of 'PureAudioClock' with 'TimeDomain' 'Float'.
 type PureAudioClockF (rate :: AudioRate) = RescaledClock (PureAudioClock rate) Float
@@ -160,4 +164,4 @@
 pureAudioClockF = RescaledClock
   { unscaledClock = PureAudioClock
   , rescale       = double2Float
-}
+  }
diff --git a/src/FRP/Rhine/Clock/Realtime/Busy.hs b/src/FRP/Rhine/Clock/Realtime/Busy.hs
--- a/src/FRP/Rhine/Clock/Realtime/Busy.hs
+++ b/src/FRP/Rhine/Clock/Realtime/Busy.hs
@@ -9,6 +9,7 @@
 
 -- rhine
 import FRP.Rhine.Clock
+import FRP.Rhine.Clock.Proxy
 
 {- |
 A clock that ticks without waiting.
@@ -28,3 +29,5 @@
         &&& arr (const ())
       , initialTime
       )
+
+instance GetClockProxy Busy
diff --git a/src/FRP/Rhine/Clock/Realtime/Event.hs b/src/FRP/Rhine/Clock/Realtime/Event.hs
--- a/src/FRP/Rhine/Clock/Realtime/Event.hs
+++ b/src/FRP/Rhine/Clock/Realtime/Event.hs
@@ -43,6 +43,7 @@
 import Control.Monad.Trans.Reader
 
 -- rhine
+import FRP.Rhine.Clock.Proxy
 import FRP.Rhine.ClSF
 import FRP.Rhine.Schedule
 import FRP.Rhine.Schedule.Concurrently
@@ -160,6 +161,8 @@
           return (time, event)
       , initialTime
       )
+
+instance GetClockProxy (EventClock event)
 
 -- | Create an event clock that is bound to a specific event channel.
 --   This is usually only useful if you can't apply 'runEventChanT'
diff --git a/src/FRP/Rhine/Clock/Realtime/Millisecond.hs b/src/FRP/Rhine/Clock/Realtime/Millisecond.hs
--- a/src/FRP/Rhine/Clock/Realtime/Millisecond.hs
+++ b/src/FRP/Rhine/Clock/Realtime/Millisecond.hs
@@ -21,6 +21,7 @@
 
 -- rhine
 import FRP.Rhine.Clock
+import FRP.Rhine.Clock.Proxy
 import FRP.Rhine.Clock.FixedStep
 import FRP.Rhine.Schedule
 import FRP.Rhine.ResamplingBuffer
@@ -46,6 +47,7 @@
   type Tag  (Millisecond n) = Bool
   initClock (Millisecond cl) = initClock cl
 
+instance GetClockProxy (Millisecond n)
 
 -- | This implementation measures the time after each tick,
 --   and waits for the remaining time until the next tick.
diff --git a/src/FRP/Rhine/Clock/Realtime/Stdin.hs b/src/FRP/Rhine/Clock/Realtime/Stdin.hs
--- a/src/FRP/Rhine/Clock/Realtime/Stdin.hs
+++ b/src/FRP/Rhine/Clock/Realtime/Stdin.hs
@@ -18,6 +18,7 @@
 
 -- rhine
 import FRP.Rhine.Clock
+import FRP.Rhine.Clock.Proxy
 import Data.Semigroup
 
 {- |
@@ -39,6 +40,8 @@
           return (time, line)
       , initialTime
       )
+
+instance GetClockProxy StdinClock
 
 instance Semigroup StdinClock where
   _ <> _ = StdinClock
diff --git a/src/FRP/Rhine/Clock/Select.hs b/src/FRP/Rhine/Clock/Select.hs
--- a/src/FRP/Rhine/Clock/Select.hs
+++ b/src/FRP/Rhine/Clock/Select.hs
@@ -16,6 +16,7 @@
 
 -- rhine
 import FRP.Rhine.Clock
+import FRP.Rhine.Clock.Proxy
 import FRP.Rhine.Schedule
 
 -- dunai
@@ -50,6 +51,7 @@
         returnA                     -< (time, ) <$> select tag
     return (runningSelectClock, initialTime)
 
+instance GetClockProxy (SelectClock cl a)
 
 -- | A universal schedule for two subclocks of the same main clock.
 --   The main clock must be a 'Semigroup' (e.g. a singleton).
diff --git a/src/FRP/Rhine/Clock/Util.hs b/src/FRP/Rhine/Clock/Util.hs
new file mode 100644
--- /dev/null
+++ b/src/FRP/Rhine/Clock/Util.hs
@@ -0,0 +1,24 @@
+{-# LANGUAGE Arrows #-}
+{-# LANGUAGE RecordWildCards #-}
+module FRP.Rhine.Clock.Util where
+
+-- rhine
+import FRP.Rhine.Clock
+import FRP.Rhine.Clock.Proxy
+import FRP.Rhine.TimeDomain
+
+-- * Auxiliary definitions and utilities
+
+-- | Given a clock value and an initial time,
+--   generate a stream of time stamps.
+genTimeInfo
+  :: (Monad m, Clock m cl)
+  => ClockProxy cl -> Time cl
+  -> MSF m (Time cl, Tag cl) (TimeInfo cl)
+genTimeInfo _ initialTime = proc (absolute, tag) -> do
+  lastTime <- iPre initialTime -< absolute
+  returnA                      -< TimeInfo
+    { sinceLast = absolute `diffTime` lastTime
+    , sinceInit = absolute `diffTime` initialTime
+    , ..
+    }
diff --git a/src/FRP/Rhine/Reactimation.hs b/src/FRP/Rhine/Reactimation.hs
--- a/src/FRP/Rhine/Reactimation.hs
+++ b/src/FRP/Rhine/Reactimation.hs
@@ -3,18 +3,23 @@
 as main loops.
 -}
 
+{-# LANGUAGE Arrows #-}
 {-# LANGUAGE GADTs #-}
 {-# LANGUAGE RecordWildCards #-}
 module FRP.Rhine.Reactimation where
 
+-- base
+import Control.Monad ((>=>))
+import Data.Functor (void)
 
 -- dunai
 import Data.MonadicStreamFunction.InternalCore
 
 -- rhine
 import FRP.Rhine.Clock
+import FRP.Rhine.Clock.Proxy
 import FRP.Rhine.ClSF.Core
-import FRP.Rhine.Reactimation.Tick
+import FRP.Rhine.Reactimation.ClockErasure
 import FRP.Rhine.Reactimation.Combinators
 import FRP.Rhine.Schedule
 import FRP.Rhine.Type
@@ -53,32 +58,20 @@
 -- TODO Can we chuck the constraints into Clock m cl?
 flow
   :: ( Monad m, Clock m cl
+     , GetClockProxy cl
      , Time cl ~ Time (In  cl)
      , Time cl ~ Time (Out cl)
      )
   => Rhine m cl () () -> m ()
-flow Rhine {..} = do
-  (runningClock, initTime) <- initClock clock
-  -- Run the main loop
-  flow' runningClock $ createTickable
-    (trivialResamplingBuffer clock)
-    sn
-    (trivialResamplingBuffer clock)
-    initTime
-    where
-      flow' runningClock tickable = do
-        -- Fetch the next time stamp from the stream, wait if necessary
-        ((now, tag), runningClock') <- unMSF runningClock ()
-        -- Process the part of the signal network that is scheduled to run
-        tickable' <- tick tickable now tag
-        -- Loop
-        flow' runningClock' tickable'
-
+flow rhine = do
+  msf <- eraseClock rhine
+  reactimate $ msf >>> arr (const ())
 
 -- | Run a synchronous 'ClSF' with its clock as a main loop,
 --   similar to Yampa's, or Dunai's, 'reactimate'.
 reactimateCl
   :: ( Monad m, Clock m cl
+     , GetClockProxy cl
      , cl ~ In  cl, cl ~ Out cl
      )
   => cl -> ClSF m cl () () -> m ()
diff --git a/src/FRP/Rhine/Reactimation/ClockErasure.hs b/src/FRP/Rhine/Reactimation/ClockErasure.hs
new file mode 100644
--- /dev/null
+++ b/src/FRP/Rhine/Reactimation/ClockErasure.hs
@@ -0,0 +1,111 @@
+{- |
+Translate clocked signal processing components to stream functions without explicit clock types.
+
+This module is not meant to be used externally,
+and is thus not exported from 'FRP.Rhine'.
+-}
+{-# LANGUAGE Arrows #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE TupleSections #-}
+module FRP.Rhine.Reactimation.ClockErasure where
+
+-- base
+import Control.Monad (join)
+import Data.Maybe (fromJust, fromMaybe)
+
+-- dunai
+import Control.Monad.Trans.MSF.Reader
+import Data.MonadicStreamFunction
+import Data.MonadicStreamFunction.InternalCore
+
+-- rhine
+import FRP.Rhine.Clock
+import FRP.Rhine.Clock.Proxy
+import FRP.Rhine.Clock.Util
+import FRP.Rhine.ClSF hiding (runReaderS)
+import FRP.Rhine.ResamplingBuffer
+import FRP.Rhine.Schedule
+import FRP.Rhine.SN
+
+-- | Run a clocked signal function as a monadic stream function,
+--   accepting the timestamps and tags as explicit inputs.
+eraseClockClSF
+  :: (Monad m, Clock m cl)
+  => ClockProxy cl -> Time cl
+  -> ClSF m cl a b
+  -> MSF m (Time cl, Tag cl, a) b
+eraseClockClSF proxy initialTime clsf = proc (time, tag, a) -> do
+  timeInfo <- genTimeInfo proxy initialTime -< (time, tag)
+  runReaderS clsf                           -< (timeInfo, a)
+
+-- | Run a signal network as a monadic stream function.
+--
+--   Depending on the incoming clock,
+--   input data may need to be provided,
+--   and depending on the outgoing clock,
+--   output data may be generated.
+--   There are thus possible invalid inputs,
+--   which 'eraseClockSN' does not gracefully handle.
+eraseClockSN
+  :: (Monad m, Clock m cl, GetClockProxy cl)
+  => Time cl
+  -> SN m cl a b
+  -> MSF m (Time cl, Tag cl, Maybe a) (Maybe b)
+
+-- A synchronous signal network is run by erasing the clock from the clocked signal function.
+eraseClockSN initialTime sn@(Synchronous clsf) = proc (time, tag, Just a) -> do
+  b <- eraseClockClSF (toClockProxy sn) initialTime clsf -< (time, tag, a)
+  returnA                                                -< Just b
+
+-- A sequentially composed signal network may either be triggered in its first component,
+-- or its second component. In either case,
+-- the resampling buffer (which connects the two components) may be triggered,
+-- but only if the outgoing clock of the first component ticks,
+-- or the incoming clock of the second component ticks.
+eraseClockSN initialTime (Sequential sn1 resBuf sn2) =
+  let
+    proxy1 = toClockProxy sn1
+    proxy2 = toClockProxy sn2
+  in proc (time, tag, maybeA) -> do
+  resBufIn <- case tag of
+    Left  tagL -> do
+      maybeB <- eraseClockSN initialTime sn1 -< (time, tagL, maybeA)
+      returnA -< Left <$> ((time, , ) <$> outTag proxy1 tagL <*> maybeB)
+    Right tagR -> do
+      returnA -< Right <$> (time, ) <$> inTag proxy2 tagR
+  maybeC <- mapMaybeS $ eraseClockResBuf (outProxy proxy1) (inProxy proxy2) initialTime resBuf -< resBufIn
+  case tag of
+    Left  _    -> do
+      returnA -< Nothing
+    Right tagR -> do
+      eraseClockSN initialTime sn2 -< (time, tagR, join maybeC)
+
+eraseClockSN initialTime (Parallel snL snR) = proc (time, tag, maybeA) -> do
+  case tag of
+    Left  tagL -> eraseClockSN initialTime snL -< (time, tagL, maybeA)
+    Right tagR -> eraseClockSN initialTime snR -< (time, tagR, maybeA)
+
+-- | Translate a resampling buffer into a monadic stream function.
+--
+--   The input decides whether the buffer is to accept input or has to produce output.
+--   (In the latter case, only time information is provided.)
+eraseClockResBuf
+  :: ( Monad m
+     , Clock m cl1, Clock m cl2
+     , Time cl1 ~ Time cl2
+     )
+  => ClockProxy cl1 -> ClockProxy cl2 -> Time cl1
+  -> ResBuf m cl1 cl2 a b
+  -> MSF m (Either (Time cl1, Tag cl1, a) (Time cl2, Tag cl2)) (Maybe b)
+eraseClockResBuf proxy1 proxy2 initialTime resBuf0 = feedback resBuf0 $ proc (input, resBuf) -> do
+  case input of
+    Left (time1, tag1, a) -> do
+      timeInfo1 <- genTimeInfo proxy1 initialTime   -< (time1, tag1)
+      resBuf'   <- arrM (uncurry $ uncurry put)     -< ((resBuf, timeInfo1), a)
+      returnA                                       -< (Nothing, resBuf')
+    Right (time2, tag2) -> do
+      timeInfo2    <- genTimeInfo proxy2 initialTime -< (time2, tag2)
+      (b, resBuf') <- arrM (uncurry get)             -< (resBuf, timeInfo2)
+      returnA                                        -< (Just b, resBuf')
diff --git a/src/FRP/Rhine/Reactimation/Combinators.hs b/src/FRP/Rhine/Reactimation/Combinators.hs
--- a/src/FRP/Rhine/Reactimation/Combinators.hs
+++ b/src/FRP/Rhine/Reactimation/Combinators.hs
@@ -21,6 +21,7 @@
 
 -- rhine
 import FRP.Rhine.Clock
+import FRP.Rhine.Clock.Proxy
 import FRP.Rhine.ClSF.Core
 import FRP.Rhine.ResamplingBuffer
 import FRP.Rhine.Schedule
@@ -98,8 +99,9 @@
          , Time cl1 ~ Time cl2
          , Time (Out cl1) ~ Time cl1
          , Time (In  cl2) ~ Time cl2
-         , Clock m (Out cl1)
-         , Clock m (In  cl2)
+         , Clock m (Out cl1), Clock m (Out cl2)
+         , Clock m (In  cl1), Clock m (In  cl2)
+         , GetClockProxy cl1, GetClockProxy cl2
          )
       => RhineAndResamplingPoint   m cl1 cl2  a b
       -> Rhine m                         cl2    b c
@@ -139,6 +141,8 @@
 infix 3 @++
 (@++)
   :: ( Monad m, Clock m clL, Clock m clR
+     , Clock m (Out clL), Clock m (Out clR)
+     , GetClockProxy clL, GetClockProxy clR
      , Time clL ~ Time (Out clL), Time clR ~ Time (Out clR)
      , Time clL ~ Time (In  clL), Time clR ~ Time (In  clR)
      , Time clL ~ Time clR
@@ -176,6 +180,8 @@
 infix 3 @||
 (@||)
   :: ( Monad m, Clock m clL, Clock m clR
+     , Clock m (Out clL), Clock m (Out clR)
+     , GetClockProxy clL, GetClockProxy clR
      , Time clL ~ Time (Out clL), Time clR ~ Time (Out clR)
      , Time clL ~ Time (In  clL), Time clR ~ Time (In  clR)
      , Time clL ~ Time clR
diff --git a/src/FRP/Rhine/Reactimation/Tick.hs b/src/FRP/Rhine/Reactimation/Tick.hs
deleted file mode 100644
--- a/src/FRP/Rhine/Reactimation/Tick.hs
+++ /dev/null
@@ -1,231 +0,0 @@
-{- |
-This module contains internals needed for the reactimation of signal functions.
-None of it should be relevant for a typical user of this library.
--}
-
-{-# LANGUAGE GADTs #-}
-{-# LANGUAGE NamedFieldPuns #-}
-{-# LANGUAGE RecordWildCards #-}
-module FRP.Rhine.Reactimation.Tick where
-
--- transformers
-import Control.Monad.Trans.Reader
-
--- dunai
-import Data.MonadicStreamFunction
-import Data.MonadicStreamFunction.InternalCore
-
--- rhine
-import FRP.Rhine.Clock
-import FRP.Rhine.ResamplingBuffer
-import FRP.Rhine.Schedule
-import FRP.Rhine.SN
-
-
-{- | A signal network ('SN') enclosed by matching 'ResamplingBuffer's and further auxiliary data,
-such that it can be stepped with each arriving tick from a clock 'cl'.
-They play a similar role like 'ReactHandle's in dunai.
-
-The type parameters:
-
-* 'm': The monad in which the 'SN' and the 'ResamplingBuffer's produce side effects
-* 'cla': The (irrelevant) input clock of the left 'ResamplingBuffer'
-* 'clb': The clock at which the left 'ResamplingBuffer' produces output
-* 'cl': The clock at which the 'SN' ticks
-* 'clc': The clock at which the right 'ResamplingBuffer' accepts input
-* 'cld': The (irrelevant) output clock of the right 'ResamplingBuffer'
-* 'a': The (irrelevant) input type of the left 'ResamplingBuffer'
-* 'b': The input type of the 'SN'
-* 'c': The output type of the 'SN'
-* 'd': The (irrelevant) output type of the right 'ResamplingBuffer'
--}
-data Tickable m cla clb cl clc cld a b c d = Tickable
-  { -- | The left buffer from which the input is taken.
-    buffer1     :: ResamplingBuffer m cla clb          a b
-    -- | The signal network that will process the data.
-  , ticksn      :: SN               m        cl          b c
-    -- | The right buffer in which the output is stored.
-  , buffer2     :: ResamplingBuffer m          clc cld     c d
-    -- | The leftmost clock of the signal network, 'cl',
-    --   may be a parallel subclock of the buffer clock.
-    --   'parClockIn' specifies in which position 'In cl'
-    --   is a parallel subclock of 'clb'.
-  , parClockIn  :: ParClockInclusion (In  cl) clb
-    -- | The same on the output side.
-  , parClockOut :: ParClockInclusion (Out cl) clc
-    -- | The last times when the different parts of the signal tree have ticked.
-  , lastTime    :: LastTime cl
-    -- | The time when the whole clock was initialised.
-  , initTime    :: Time cl
-  }
-
-
--- | Initialise the tree of last tick times.
-initLastTime :: SN m cl a b -> Time cl -> LastTime cl
-initLastTime (Synchronous _)        initTime = LeafLastTime initTime
-initLastTime (Sequential sn1 _ sn2) initTime =
-  SequentialLastTime
-    (initLastTime sn1 initTime)
-    (initLastTime sn2 initTime)
-initLastTime (Parallel sn1 sn2)     initTime =
-  ParallelLastTime
-    (initLastTime sn1 initTime)
-    (initLastTime sn2 initTime)
-
--- | Initialise a 'Tickable' from a signal network,
---   two matching enclosing resampling buffers and an initial time.
-createTickable
-  :: ResamplingBuffer m cla (In cl)                 a b
-  -> SN               m         cl                      b c
-  -> ResamplingBuffer m                (Out cl) cld     c d
-  -> Time cl
-  -> Tickable         m cla (In cl) cl (Out cl) cld a b c d
-createTickable buffer1 ticksn buffer2 initTime = Tickable
-  { parClockIn  = ParClockRefl
-  , parClockOut = ParClockRefl
-  , lastTime    = initLastTime ticksn initTime
-  , ..
-  }
-
-{- | In this function, one tick, or step of an asynchronous signal network happens.
-The 'TimeInfo' holds the information which part of the signal tree will tick.
-This information is encoded in the 'Tag' of the 'TimeInfo',
-which is of type 'Either tag1 tag2' in case of a 'SequentialClock' or a 'ParallelClock',
-encoding either a tick for the left clock or the right clock.
--}
-tick :: ( Monad m, Clock m cl
-        , Time cla ~ Time cl
-        , Time clb ~ Time cl
-        , Time clc ~ Time cl
-        , Time cld ~ Time cl
-        , Time (In  cl) ~ Time cl
-        , Time (Out cl) ~ Time cl
-        )
-     => Tickable    m cla clb cl clc cld a b c d
-     -> Time cl -- ^ Timestamp of the present tick
-     -> Tag cl -- ^ 'Tag' of the overall clock; contains the information which subsystem will become active
-     -> m (Tickable m cla clb cl clc cld a b c d)
--- Only if we have reached a leaf of the tree, data is actually processed.
-tick Tickable
-  { ticksn   = Synchronous clsf
-  , lastTime = LeafLastTime lastTime
-  , .. } now tag = do
-    let
-      ti = TimeInfo
-        { sinceLast = diffTime now lastTime
-        , sinceInit = diffTime now initTime
-        , absolute  = now
-        , tag       = tag
-        }
-    -- Get an input value from the left buffer
-    (b, buffer1') <- get buffer1 $ retag (parClockTagInclusion parClockIn ) ti
-    -- Run it through the signal function
-    (c, clsf')  <- unMSF clsf b `runReaderT` ti
-    -- Put the output into the right buffer
-    buffer2'      <- put buffer2 (retag (parClockTagInclusion parClockOut) ti) c
-    return Tickable
-      { buffer1  = buffer1'
-      , ticksn   = Synchronous clsf'
-      , buffer2  = buffer2'
-      , lastTime = LeafLastTime now
-      , .. }
--- The left part of a sequential composition is stepped.
-tick tickable@Tickable
-  { ticksn   = Sequential sn1 bufferMiddle sn2
-  , lastTime = SequentialLastTime lastTimeL lastTimeR
-  , initTime
-  , parClockIn
-  } now (Left tag) = do
-    leftTickable <- tick Tickable
-      { buffer1     = buffer1 tickable
-      , ticksn      = sn1
-      , buffer2     = bufferMiddle
-      , parClockIn  = parClockIn
-      , parClockOut = ParClockRefl
-      , lastTime    = lastTimeL
-      , initTime    = initTime
-      } now tag
-    return $ tickable
-      { buffer1  = buffer1 leftTickable
-      , ticksn   = Sequential (ticksn leftTickable) (buffer2 leftTickable) sn2
-      , lastTime = SequentialLastTime (lastTime leftTickable) lastTimeR
-      }
--- The right part of a sequential composition is stepped.
-tick tickable@Tickable
-  { ticksn   = Sequential sn1 bufferMiddle sn2
-  , lastTime = SequentialLastTime lastTimeL lastTimeR
-  , initTime
-  , parClockOut
-  } now (Right tag) = do
-    rightTickable <- tick Tickable
-      { buffer1     = bufferMiddle
-      , ticksn      = sn2
-      , buffer2     = buffer2 tickable
-      , parClockIn  = ParClockRefl
-      , parClockOut = parClockOut
-      , lastTime    = lastTimeR
-      , initTime    = initTime
-      } now tag
-    return $ tickable
-      { buffer2  = buffer2 rightTickable
-      , ticksn   = Sequential sn1 (buffer1 rightTickable) (ticksn rightTickable)
-      , lastTime = SequentialLastTime lastTimeL (lastTime rightTickable)
-      }
--- A parallel composition is stepped.
-tick tickable@Tickable
-  { ticksn   = Parallel snA snB
-  , lastTime = ParallelLastTime lastTimeA lastTimeB
-  , initTime
-  , parClockIn
-  , parClockOut
-  } now tag = case tag of
-    Left tagL -> do
-      leftTickable <- tick Tickable
-        { buffer1     = buffer1 tickable
-        , ticksn      = snA
-        , buffer2     = buffer2 tickable
-        , parClockIn  = ParClockInL parClockIn
-        , parClockOut = ParClockInL parClockOut
-        , lastTime    = lastTimeA
-        , initTime    = initTime
-        } now tagL
-      return $ tickable
-        { buffer1  = buffer1 leftTickable
-        , ticksn   = Parallel (ticksn leftTickable) snB
-        , buffer2  = buffer2 leftTickable
-        , lastTime = ParallelLastTime (lastTime leftTickable) lastTimeB
-        }
-    Right tagR -> do
-      rightTickable <- tick Tickable
-        { buffer1     = buffer1 tickable
-        , ticksn      = snB
-        , buffer2     = buffer2 tickable
-        , parClockIn  = ParClockInR parClockIn
-        , parClockOut = ParClockInR parClockOut
-        , lastTime    = lastTimeB
-        , initTime    = initTime
-        } now tagR
-      return $ tickable
-        { buffer1  = buffer1 rightTickable
-        , ticksn   = Parallel snA (ticksn rightTickable)
-        , buffer2  = buffer2 rightTickable
-        , lastTime = ParallelLastTime lastTimeA (lastTime rightTickable)
-        }
-tick Tickable {} _ _ = error "Impossible pattern in tick"
-
--- TODO It seems wasteful to unwrap and rewrap log(N) Tickables
--- (where N is the size of the clock tree) each tick,
--- but I have no better idea.
-
-{- | A 'ResamplingBuffer' producing only units.
-(Slightly more efficient and direct implementation than the one in 'FRP.Rhine.Timeless'
-that additionally unifies the clock types in a way needed for the tick implementation.)
--}
-trivialResamplingBuffer
-  :: Monad m => cl
-  -> ResamplingBuffer m (Out cl) (In cl) () ()
-trivialResamplingBuffer _ = go
-  where
-    go  = ResamplingBuffer {..}
-    put _ _ = return      go
-    get _   = return ((), go)
diff --git a/src/FRP/Rhine/ResamplingBuffer.hs b/src/FRP/Rhine/ResamplingBuffer.hs
--- a/src/FRP/Rhine/ResamplingBuffer.hs
+++ b/src/FRP/Rhine/ResamplingBuffer.hs
@@ -18,7 +18,6 @@
 -- rhine
 import FRP.Rhine.Clock
 
-
 -- base
 import Control.Arrow (second)
 
diff --git a/src/FRP/Rhine/SN.hs b/src/FRP/Rhine/SN.hs
--- a/src/FRP/Rhine/SN.hs
+++ b/src/FRP/Rhine/SN.hs
@@ -7,13 +7,16 @@
 combinators are found in a submodule.
 -}
 
+{-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE GADTs #-}
 {-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE TypeFamilies #-}
 module FRP.Rhine.SN where
 
 
 -- rhine
 import FRP.Rhine.Clock
+import FRP.Rhine.Clock.Proxy
 import FRP.Rhine.ClSF.Core
 import FRP.Rhine.ResamplingBuffer
 import FRP.Rhine.Schedule
@@ -41,6 +44,9 @@
   -- | Two 'SN's may be sequentially composed if there is a matching 'ResamplingBuffer' between them.
   Sequential
     :: ( Clock m clab, Clock m clcd
+       , Clock m (Out clab), Clock m (Out clcd)
+       , Clock m (In  clab), Clock m (In  clcd)
+       , GetClockProxy clab, GetClockProxy clcd
        , Time clab ~ Time clcd
        , Time clab ~ Time (Out clab)
        , Time clcd ~ Time (In  clcd)
@@ -52,6 +58,8 @@
   -- | Two 'SN's with the same input and output data may be parallely composed.
   Parallel
     :: ( Clock m cl1, Clock m cl2
+       , Clock m (Out cl1), Clock m (Out cl2)
+       , GetClockProxy cl1, GetClockProxy cl2
        , Time cl1 ~ Time (Out cl1)
        , Time cl2 ~ Time (Out cl2)
        , Time cl1 ~ Time cl2
@@ -61,3 +69,6 @@
     => SN m                  cl1      a b
     -> SN m                      cl2  a b
     -> SN m (ParallelClock m cl1 cl2) a b
+
+instance GetClockProxy cl => ToClockProxy (SN m cl a b) where
+  type Cl (SN m cl a b) = cl
diff --git a/src/FRP/Rhine/SN/Combinators.hs b/src/FRP/Rhine/SN/Combinators.hs
--- a/src/FRP/Rhine/SN/Combinators.hs
+++ b/src/FRP/Rhine/SN/Combinators.hs
@@ -2,12 +2,14 @@
 Combinators for composing signal networks sequentially and parallely.
 -}
 
+{-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE GADTs #-}
 module FRP.Rhine.SN.Combinators where
 
 
 -- rhine
 import FRP.Rhine.ClSF.Core
+import FRP.Rhine.Clock.Proxy
 import FRP.Rhine.ResamplingBuffer.Util
 import FRP.Rhine.Schedule
 import FRP.Rhine.SN
@@ -61,6 +63,8 @@
 --   Note: This is essentially an infix synonym of 'Parallel'
 (||||)
   :: ( Monad m, Clock m clL, Clock m clR
+     , Clock m (Out clL), Clock m (Out clR)
+     , GetClockProxy clL, GetClockProxy clR
      , Time clL ~ Time clR
      , Time clL ~ Time (Out clL), Time clL ~ Time (In clL)
      , Time clR ~ Time (Out clR), Time clR ~ Time (In clR)
@@ -75,6 +79,8 @@
 --   dependent on which constituent clock has ticked.
 (++++)
   :: ( Monad m, Clock m clL, Clock m clR
+     , Clock m (Out clL), Clock m (Out clR)
+     , GetClockProxy clL, GetClockProxy clR
      , Time clL ~ Time clR
      , Time clL ~ Time (Out clL), Time clL ~ Time (In clL)
      , Time clR ~ Time (Out clR), Time clR ~ Time (In clR)
diff --git a/src/FRP/Rhine/Type.hs b/src/FRP/Rhine/Type.hs
--- a/src/FRP/Rhine/Type.hs
+++ b/src/FRP/Rhine/Type.hs
@@ -3,19 +3,52 @@
 A signal network together with a matching clock value.
 -}
 
+{-# LANGUAGE Arrows #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE TypeFamilies #-}
 module FRP.Rhine.Type where
 
+-- dunai
+import Data.MonadicStreamFunction
+
+-- rhine
+import FRP.Rhine.Reactimation.ClockErasure
+import FRP.Rhine.Clock
+import FRP.Rhine.Clock.Proxy
 import FRP.Rhine.SN
 
 {- |
 A 'Rhine' consists of a 'SN' together with a clock of matching type 'cl'.
+
 It is a reactive program, possibly with open inputs and outputs.
 If the input and output types 'a' and 'b' are both '()',
 that is, the 'Rhine' is "closed",
 then it is a standalone reactive program
 that can be run with the function 'flow'.
+
+Otherwise, one can start the clock and the signal network jointly as a monadic stream function,
+using 'eraseClock'.
 -}
 data Rhine m cl a b = Rhine
   { sn    :: SN m cl a b
   , clock :: cl
   }
+
+instance GetClockProxy cl => ToClockProxy (Rhine m cl a b) where
+  type Cl (Rhine m cl a b) = cl
+
+
+{- |
+Start the clock and the signal network,
+effectively hiding the clock type from the outside.
+-}
+eraseClock
+  :: (Monad m, Clock m cl, GetClockProxy cl)
+  => Rhine  m cl a        b
+  -> m (MSF m    a (Maybe b))
+eraseClock Rhine {..} = do
+  (runningClock, initTime) <- initClock clock
+  -- Run the main loop
+  return $ proc a -> do
+    (time, tag) <- runningClock -< ()
+    eraseClockSN initTime sn -< (time, tag, a <$ inTag (toClockProxy sn) tag)
