diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Revision history for changeset
+
+## 0.1.0.0 -- YYYY-mm-dd
+
+* First version. Released on an unsuspecting world.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2023 Manuel Bärenz
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/changeset-reflex.cabal b/changeset-reflex.cabal
new file mode 100644
--- /dev/null
+++ b/changeset-reflex.cabal
@@ -0,0 +1,69 @@
+cabal-version: 3.0
+name: changeset-reflex
+-- PVP summary:     +-+------- breaking API changes
+--                  | | +----- non-breaking API additions
+--                  | | | +--- code changes with no API change
+version: 0.1.0.0
+synopsis: Stateful monad transformer based on monoidal actions
+description:
+  A general state monad transformer with separate types for the state and the possible changes.
+  It can be defined for any monoid action.
+  The monoid represents "changes", "updates", "edits" or "diffs" on the state.
+
+  This package exposes utilities to propagate changes along @reflex@ events.
+
+license: MIT
+license-file: LICENSE
+author: Manuel Bärenz
+maintainer: programming@manuelbaerenz.de
+copyright: MIT
+category: Control
+build-type: Simple
+extra-doc-files: CHANGELOG.md
+
+source-repository head
+  type: git
+  location: https://github.com/turion/changeset
+
+flag dev
+  description: Enable warnings as errors. Active on ci.
+  default: False
+  manual: True
+
+common opts
+  ghc-options:
+    -Wall
+
+  if flag(dev)
+    ghc-options:
+      -Werror
+  default-extensions:
+    BangPatterns
+    DeriveFunctor
+    FlexibleInstances
+    FunctionalDependencies
+    GADTs
+    KindSignatures
+    MultiParamTypeClasses
+    NamedFieldPuns
+    RankNTypes
+    ScopedTypeVariables
+    TupleSections
+    TypeOperators
+
+library
+  import: opts
+  exposed-modules:
+    Control.Monad.Changeset.Reflex
+
+  build-depends:
+    base >=4.12 && <4.22,
+    changeset ==0.1.0.0,
+    containers >=0.6 && <0.8,
+    dependent-map ^>=0.4,
+    monoid-extras ^>=0.6,
+    reflex >=0.8.2.1 && <0.10,
+    some ^>=1.0.1,
+
+  hs-source-dirs: src
+  default-language: Haskell2010
diff --git a/src/Control/Monad/Changeset/Reflex.hs b/src/Control/Monad/Changeset/Reflex.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Monad/Changeset/Reflex.hs
@@ -0,0 +1,78 @@
+module Control.Monad.Changeset.Reflex where
+
+-- base
+import Data.Bifunctor (Bifunctor (second))
+import Data.Functor ((<&>))
+import Data.Functor.Compose
+import Data.Functor.Identity (Identity (Identity))
+
+-- monoid-extras
+import Data.Monoid.Action
+
+-- reflex
+import Reflex.Class
+
+-- dependent-map
+import Data.Dependent.Map (
+  DMap,
+  delete,
+  insert,
+  traverseWithKey,
+ )
+
+-- changeset
+import Control.Monad.Trans.Changeset (ChangesetT (..))
+import Data.Function ((&))
+import Data.Functor.Misc (dmapToIntMap, dmapToMap, intMapWithFunctorToDMap, mapWithFunctorToDMap)
+import Data.GADT.Compare (GCompare)
+import Data.IntMap (IntMap)
+import qualified Data.IntMap as IM
+import Data.List.NonEmpty (NonEmpty, fromList)
+import Data.Map (Map)
+
+-- | A change to a dependent map 'DMap'.
+data DMapChange k f v
+  = Insert (k v) (f v)
+  | Delete (k v)
+
+instance (GCompare k) => Action (DMapChange k f v) (DMap k f) where
+  act (Insert k v) = insert k v
+  act (Delete k) = delete k
+
+{- | Merge changes from a 'DMap' of 'ChangesetT' computations performing events.
+
+The initial state is the same for every change.
+
+If several change events occur simultaneously, their changes are combined.
+-}
+mergeChangesetEvent :: (Reflex t, GCompare k, Monoid w) => DMap k (ChangesetT s w (Event t)) -> ChangesetT s w (Event t) (DMap k Identity)
+mergeChangesetEvent dmap = ChangesetT $ \s -> traverseWithKey (const (second Identity)) <$> mergeG (`getChangesetT` s) dmap
+
+-- | A functor that creates changes, and performs side effects in @m@ to create an 'Event'.
+type ChangesetEventT t s w m = ChangesetT s w (Compose m (Event t))
+
+-- | Like 'mergeChangesetEvent', but generalised to include @m@ effects.
+mergeChangesetEventT :: (Reflex t, GCompare k, Monoid w, Applicative m) => DMap k (ChangesetEventT t s w m) -> ChangesetEventT t s w m (DMap k Identity)
+mergeChangesetEventT dmap = ChangesetT $ \s ->
+  Compose $
+    fmap (traverseWithKey (const (second Identity)))
+      . mergeG getCompose
+      <$> traverseWithKey (const (fmap Compose . getCompose . flip getChangesetT s)) dmap
+
+-- | Merge a 'Map' of changes
+mergeChangesetMap :: (Reflex t, Monoid w, Applicative m, Ord k) => Map k (ChangesetEventT t s w m a) -> ChangesetEventT t s w m (Map k a)
+mergeChangesetMap = fmap dmapToMap . mergeChangesetEventT . mapWithFunctorToDMap
+
+-- | Merge an 'IntMap' of changes
+mergeChangesetIntMap :: (Reflex t, Monoid w, Applicative m) => IntMap (ChangesetEventT t s w m a) -> ChangesetEventT t s w m (IntMap a)
+mergeChangesetIntMap = fmap dmapToIntMap . mergeChangesetEventT . intMapWithFunctorToDMap
+
+-- | Merge a list of changes
+mergeChangesetEventTs :: (Reflex t, Monoid w, Applicative m) => [ChangesetEventT t s w m a] -> ChangesetEventT t s w m (NonEmpty a)
+mergeChangesetEventTs [] = ChangesetT $ const $ Compose $ pure never
+mergeChangesetEventTs cs =
+  cs
+    & zip [0 ..]
+    & IM.fromDistinctAscList
+    & mergeChangesetIntMap
+    <&> (fromList . IM.elems)
