diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,8 @@
+2024-12-21 Ivan Perez <ivan.perez@keera.co.uk>
+        * Version bump (0.14.0) (#460).
+        * Implement reactimate function matching the signature in Yampa (#454).
+        * Thanks to @solomon-b.
+
 2024-10-21 Ivan Perez <ivan.perez@keera.co.uk>
         * Version bump (0.14.11) (#442).
         * Offer all definitions from FRP.Yampa.Switches (#426).
diff --git a/bearriver.cabal b/bearriver.cabal
--- a/bearriver.cabal
+++ b/bearriver.cabal
@@ -30,7 +30,7 @@
 build-type:    Simple
 
 name:          bearriver
-version:       0.14.11
+version:       0.14.12
 author:        Ivan Perez, Manuel Bärenz
 maintainer:    ivan.perez@keera.co.uk
 homepage:      https://github.com/ivanperez-keera/dunai
@@ -102,7 +102,7 @@
   build-depends:
       base >= 4.6 && <5
     , deepseq             >= 1.3.0.0 && < 1.6
-    , dunai >= 0.6.0 && < 0.14
+    , dunai >= 0.6.0 && < 0.15
     , mtl                 >= 2.1.2 && < 2.4
     , random              >= 1.1   && < 1.3
     , simple-affine-space >= 0.1   && < 0.3
diff --git a/src/FRP/Yampa.hs b/src/FRP/Yampa.hs
--- a/src/FRP/Yampa.hs
+++ b/src/FRP/Yampa.hs
@@ -3,13 +3,15 @@
 --              (c) Ivan Perez and Manuel Baerenz, 2016-2018
 -- License    : BSD3
 -- Maintainer : ivan.perez@keera.co.uk
-module FRP.Yampa (module X, SF, FutureSF, embed) where
+module FRP.Yampa (module X, SF, FutureSF, embed, reactimate) where
 
 -- External imports
+import Control.Monad.Reader  (mapReaderT)
 import Data.Functor.Identity (Identity, runIdentity)
 
 -- Internal imports
-import           FRP.BearRiver      as X hiding (FutureSF, SF, embed, loopPre)
+import           FRP.BearRiver      as X hiding (FutureSF, SF, embed, loopPre,
+                                          reactimate)
 import qualified FRP.BearRiver      as BR
 import           FRP.BearRiver.Loop as X
 
@@ -30,3 +32,40 @@
 -- This is a simplified, purely-functional version of 'reactimate'.
 embed :: SF a b -> (a, [(DTime, Maybe a)]) -> [b]
 embed sf = runIdentity . BR.embed sf
+
+-- * Reactimation
+
+-- | Convenience function to run a signal function indefinitely, using actions
+-- to obtain new input and process the output.
+--
+-- This function first runs the initialization action, which provides the
+-- initial input for the signal transformer at time 0.
+--
+-- Afterwards, an input sensing action is used to obtain new input (if any) and
+-- the time since the last iteration. The argument to the input sensing function
+-- indicates if it can block. If no new input is received, it is assumed to be
+-- the same as in the last iteration.
+--
+-- After applying the signal function to the input, the actuation action is
+-- executed. The first argument indicates if the output has changed, the second
+-- gives the actual output). Actuation functions may choose to ignore the first
+-- argument altogether. This action should return True if the reactimation must
+-- stop, and False if it should continue.
+--
+-- Note that this becomes the program's /main loop/, which makes using this
+-- function incompatible with GLUT, Gtk and other graphics libraries. It may
+-- also impose a sizeable constraint in larger projects in which different
+-- subparts run at different time steps. If you need to control the main loop
+-- yourself for these or other reasons, use 'reactInit' and 'react'.
+reactimate :: Monad m
+           => m a                          -- ^ Initialization action
+           -> (Bool -> m (DTime, Maybe a)) -- ^ Input sensing action
+           -> (Bool -> b -> m Bool)        -- ^ Actuation (output processing)
+                                           --   action
+           -> SF a b                       -- ^ Signal function
+           -> m ()
+reactimate senseI sense actuate sf =
+    BR.reactimate senseI sense actuate (foldIdentity sf)
+  where
+    foldIdentity :: Monad m => SF a b -> BR.SF m a b
+    foldIdentity = morphS $ mapReaderT $ return . runIdentity
