diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,3 @@
+# Changelog for dyna-processing
+
+## Unreleased changes
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Author name here (c) 2022
+
+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 Author name here 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/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,9 @@
+# dyna-processing
+
+FRP bindings for library `processing-for-haskell`.
+
+## Tutorial
+
+* [Introduction](https://github.com/anton-k/dyna-frp/blob/main/dyna-processing/tutorial/00-intro.md)
+* [Example](https://github.com/anton-k/dyna-frp/blob/main/dyna-processing/tutorial/01-example.md)
+* [Quick reference](https://github.com/anton-k/dyna-frp/blob/main/dyna-processing/tutorial/101-reference.md)
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/dyna-processing.cabal b/dyna-processing.cabal
new file mode 100644
--- /dev/null
+++ b/dyna-processing.cabal
@@ -0,0 +1,82 @@
+cabal-version: 1.12
+
+-- This file has been generated from package.yaml by hpack version 0.34.4.
+--
+-- see: https://github.com/sol/hpack
+
+name:           dyna-processing
+version:        0.1.0.0
+synopsis:       FRP library for processing-for-haskell package
+description:    Defines FRP library for processing-for-haskell package (see examples in the repo's source)
+homepage:       https://github.com/githubuser/dyna-processing#readme
+bug-reports:    https://github.com/githubuser/dyna-processing/issues
+author:         Author name here
+maintainer:     example@example.com
+copyright:      2022 Author name here
+license:        BSD3
+license-file:   LICENSE
+category:       Graphics, FRP
+build-type:     Simple
+extra-source-files:
+    README.md
+    ChangeLog.md
+
+source-repository head
+  type: git
+  location: https://github.com/githubuser/dyna-processing
+
+library
+  exposed-modules:
+      Dyna.Proc
+      Dyna.Proc.Frp
+      Dyna.Proc.Run
+      Dyna.Proc.Types
+  other-modules:
+      Paths_dyna_processing
+  hs-source-dirs:
+      src
+  build-depends:
+      base >=4.7 && <5
+    , Boolean
+    , dyna
+    , processing-for-haskell
+    , lifted-base
+    , monad-control
+    , mtl
+    , stm
+    , temporal-media
+    , time
+    , transformers-base
+    , vector-space
+    , unagi-chan
+    , random
+    , MonadRandom
+  default-language: Haskell2010
+  default-extensions:
+    GADTs
+    RankNTypes
+    LambdaCase
+    FlexibleContexts
+    FlexibleInstances
+    ExistentialQuantification
+    MultiParamTypeClasses
+    TypeApplications
+    TypeFamilies
+    TupleSections
+    GeneralizedNewtypeDeriving
+    UndecidableInstances
+    ImportQualifiedPost
+    RecordWildCards
+
+test-suite dyna-processing-test
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  other-modules:
+      Paths_dyna_processing
+  hs-source-dirs:
+      test
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      base >=4.7 && <5
+    , dyna-processing
+  default-language: Haskell2010
diff --git a/src/Dyna/Proc.hs b/src/Dyna/Proc.hs
new file mode 100644
--- /dev/null
+++ b/src/Dyna/Proc.hs
@@ -0,0 +1,24 @@
+module Dyna.Proc(
+  module X
+) where
+
+import Dyna.Proc.Frp   as X
+import Dyna.Proc.Run   as X
+import Dyna.Proc.Types as X
+
+import Graphics.Proc   as X hiding
+  (mouse, relMouse, runProc, Proc, Sum(..), getSum, loop)
+
+import Data.AdditiveGroup as X
+import Data.AffineSpace as X
+import Data.Basis as X
+import Data.Cross as X
+import Data.VectorSpace as X
+import Temporal.Class as X
+
+instance Semigroup Draw where
+  (<>) = (>>)
+
+instance Monoid Draw where
+  mempty = pure ()
+
diff --git a/src/Dyna/Proc/Frp.hs b/src/Dyna/Proc/Frp.hs
new file mode 100644
--- /dev/null
+++ b/src/Dyna/Proc/Frp.hs
@@ -0,0 +1,656 @@
+-- | FRP main types and combinators
+module Dyna.Proc.Frp(
+  -- * Events
+  Evt,
+  once,
+  never,
+  -- * Dynamics
+  Dyn,
+  constDyn,
+  -- ** Observing the Dyn
+  DynRef,
+  runDyn,
+  readDyn,
+  cancelDyn,
+
+  -- * Control
+  newEvt,
+  newDyn,
+
+  -- * API
+  -- * Event API
+  scan,
+  scanMay,
+  mapMay,
+  foldMaps,
+  accum,
+  accumB,
+  accumMay,
+  filters,
+  filterJust,
+  whens,
+  splits,
+  lefts,
+  rights,
+  iterates,
+  withIterates,
+
+  fix1,
+  fix2,
+  fix3,
+  fix4,
+  switch,
+  joins,
+
+  delay,
+  delayFork,
+
+  sums,
+  sumD,
+  integrate,
+  integrate2,
+  products,
+  count,
+  withCount,
+  appends,
+  takes,
+  drops,
+  takesWhile,
+  dropsWhile,
+  cycles,
+  listAt,
+  toToggle,
+
+  races,
+  forks,
+  forevers,
+  folds,
+  foldls,
+  foldls',
+  foldrs,
+  foldrs',
+  prints,
+  putStrLns,
+  -- * Event/Dynamic interaction
+  hold,
+  unhold,
+  scanD,
+  scanMayD,
+  switchD,
+  switchDyn,
+  apply,
+  applyMay,
+  snap,
+  attach,
+  attachWith,
+  attachWithMay,
+  (<@>),
+  (<@),
+  -- * Effectful API
+  RunFunctor(..),
+  foreach,
+  posteach,
+  iterates',
+  scan',
+  scanMay',
+  accum',
+  accumMay',
+  filters',
+  mapMay',
+  apply',
+  applyMay',
+  -- * Utilities
+
+  -- **  Channels (interaction with the world)
+  mchanEvt,
+  tchanEvt,
+  uchanEvt,
+
+  -- ** Clock
+  clock,
+  pulse,
+  ticks,
+  timer,
+  timerD,
+  -- ** Random
+  toRandom,
+  toRandomR,
+  withRandom,
+  withRandomR,
+  oneOf,
+  withOneOf,
+  freqOf,
+  withFreqOf,
+  randSkip,
+  randSkipBy,
+
+  -- * Re-exports
+  liftA2,
+  liftA3,
+) where
+
+import Control.Applicative (liftA2, liftA3)
+import Control.Concurrent.Chan qualified as M
+import Control.Concurrent.STM
+import Control.Concurrent.Chan.Unagi (InChan)
+import Data.Bifunctor
+import Data.Time
+import System.Random
+import Data.VectorSpace
+
+import Dyna.Proc.Types
+import Dyna qualified as D
+
+infixl 4 <@>
+infixl 4 <@
+
+-- | Reference to running dynamic process by which we can query values (@readDyn@).
+-- Also note that we no longer need the reference we should release the resources
+-- by calling @cancelDyn@.
+newtype DynRef a = DynRef { unDynRef :: D.DynRef Run a }
+
+-- | Runs dynamic within the scope of the function.
+-- It provides a callback with dyn getter as argument and after
+-- callback finishes it shutdowns the dyn process.
+withDyn :: Dyn a -> (Run a -> Run b) -> Run b
+withDyn (Dyn dyn) f = D.withDyn dyn f
+
+-- | Dyn that is constructed from effectful callback.
+constDyn :: Run a -> Dyn a
+constDyn act = Dyn $ D.constDyn act
+
+-- | Executes dynamic for observation. The dynamic is step-wise constant
+-- function that is driven by some event stream. The function runs the event stream
+-- process in background and samples the updated state.
+--
+-- We can observe the value with @readDyn@. We need to shut down the stream when
+-- we no longer need it with @cancelDyn@ function.
+runDyn :: Dyn a -> Run (DynRef a)
+runDyn (Dyn d) = DynRef <$> D.runDyn d
+--
+-- | Event that happens only once and happens right away.
+once :: Run a -> Evt a
+once ask = Evt $ D.once ask
+
+-- | Event that never happens. Callback function is ignored.
+never :: Evt a
+never = Evt D.never
+
+-- | Runs the argument event stream as background process
+-- and produces event stream that is fed with events over channel (unagi-channel package).
+-- When result event stream shuts down the background process also shuts down.
+newEvt :: Evt a -> Run (Evt a)
+newEvt (Evt evt) = Evt <$> D.newEvt evt
+
+-- | Runs the dynamic process in background and returns dynamic
+-- that just samples the background proces with @readDyn@.
+newDyn :: Dyn a -> Run (Dyn a)
+newDyn (Dyn dyn) = Dyn <$> D.newDyn dyn
+
+-- | Accumulate over event stream.
+accum :: (a -> s -> (b, s)) -> s -> Evt a -> Evt b
+accum f s (Evt evt) = Evt $ D.accum f s evt
+
+-- | Accumulate over event stream.
+accum' :: (a -> s -> Run (b, s)) -> s -> Evt a -> Evt b
+accum' f s (Evt evt) = Evt $ D.accum' f s evt
+
+-- | Accumulate over event stream.
+accumMay :: (a -> s -> Maybe (b, s)) -> s -> Evt a -> Evt b
+accumMay f s (Evt evt) = Evt $ D.accumMay f s evt
+
+-- | Accumulate over event stream.
+accumMay' :: (a -> s -> Run (Maybe (b, s))) -> s -> Evt a -> Evt b
+accumMay' f s (Evt evt) = Evt $ D.accumMay' f s evt
+
+-- | scan over event stream. Example:
+--
+-- > naturals = scan (+) 0 pulse
+scan :: (a -> b -> b) -> b -> Evt a -> Evt b
+scan f s (Evt evt) = Evt $ D.scan f s  evt
+
+-- | scan over event stream with effectful function.
+scan' :: (a -> b -> Run b) -> b -> Evt a -> Evt b
+scan' f s (Evt evt) = Evt $ D.scan' f s evt
+
+-- | scan combined with filter. If accumulator function produces @Nothing@ on event then
+-- that event is ignored and state is kept to previous state.
+scanMay :: (a -> b -> Maybe b) -> b -> Evt a -> Evt b
+scanMay f s (Evt evt) = Evt $ D.scanMay f s evt
+
+-- | scan combined with filter for effectful function. See @scanMayE@ for details.
+scanMay' :: (a -> b -> Run (Maybe b)) -> b -> Evt a -> Evt b
+scanMay' f s (Evt evt) = Evt $ D.scanMay' f s evt
+
+-- | Combo of @fmap@ and @appendE@
+foldMaps :: Monoid b => (a -> b) -> Evt a -> Evt b
+foldMaps f evt = appends (fmap f evt)
+
+-- | Repeatedly executes the same event stream
+races :: Evt a -> Evt a -> Evt a
+races (Evt evtA) (Evt evtB) = Evt (D.races evtA evtB)
+
+-- | Repeatedly executes the same event stream
+forks :: Evt a -> Evt a
+forks (Evt evt) = Evt (D.forks evt)
+
+-- | Repeatedly executes the same event stream
+forevers :: Evt a -> Evt a
+forevers (Evt evt) = Evt (D.forevers evt)
+
+-- | Iterates over event stream. It's like scan but it ignores the values of underying stream
+-- and starts with initial value as first element.
+iterates :: (a -> a) -> a -> Evt b -> Evt a
+iterates f val (Evt evt) = Evt $ D.iterates f val evt
+
+withIterates :: (a -> a) -> a -> Evt b -> Evt (a, b)
+withIterates f val (Evt evt) = Evt $ D.withIterates f val evt
+
+-- | Effectful version for @iterateE@.
+iterates' :: (a -> Run a) -> a -> Evt b -> Evt a
+iterates' f val (Evt evt) = Evt $ D.iterates' f val evt
+
+-- | Reads current dynamic value.
+readDyn :: DynRef  a -> Run a
+readDyn (DynRef ref) = D.readDyn ref
+
+-- | Shuts down the background process for dynamic and releases resulrces for
+-- event stream that drives the dynamic.
+cancelDyn :: DynRef a -> Run ()
+cancelDyn (DynRef ref) = D.cancelDyn ref
+
+-- | Turns event stream to dynamic. It holds the values of
+-- events until the next event happen. It starts with initial value.
+--
+-- > hold initVal events = ...
+hold :: a -> Evt a -> Dyn a
+hold s (Evt evt) = Dyn $ D.hold s evt
+
+-- | Counts how many events accured so far on the stream.
+count :: Evt a -> Evt Int
+count (Evt evt) = Evt (D.count evt)
+
+withCount :: Evt a -> Evt (Int, a)
+withCount (Evt evt) = Evt (D.withCount evt)
+
+-- | Turns dynamic into event stream of underlying events
+-- that trigger dynamic updates.
+unhold :: Dyn a -> Evt a
+unhold (Dyn dyn) = Evt $ D.unhold dyn
+
+-- | scans over event stream and converts it to dynamic.
+scanD :: (a -> b -> b) -> b -> Evt a -> Dyn b
+scanD f s (Evt evt) = Dyn $ D.scanD f s evt
+
+-- | Accumulates the values with event stream that produce functions.
+accumB :: a -> Evt (a -> a) -> Dyn a
+accumB a (Evt evt) = Dyn $ D.accumB a evt
+
+-- | Dynamic scan that can also filter out events. If Nothing is produced then the event is skipped.
+scanMayD :: (a -> b -> Maybe b) -> b -> Evt a -> Dyn b
+scanMayD f s (Evt evt) = Dyn $ D.scanMayD f s evt
+
+-- | Adds some procedure to callback. Procedure is called prior to callback execution.
+foreach :: (a -> Run ()) -> Evt a -> Evt a
+foreach call (Evt evt) = Evt $ D.foreach call evt
+
+-- | Adds some procedure to callback. Procedure is called after callback execution.
+posteach :: (a -> Run ()) -> Evt a -> Evt a
+posteach call (Evt evt) = Evt $ D.posteach call evt
+
+--------------------------------------------------------------------------------
+-- applications
+
+-- | Applies a function to event stream value. The function is sampled
+-- from dynamic process.
+apply :: Dyn (a -> b) -> Evt a -> Evt b
+apply (Dyn dyn) (Evt evt) = Evt $ D.apply dyn evt
+
+-- | Effectful variant of @apply@.
+apply' :: Dyn (a -> Run b) -> Evt a -> Evt b
+apply' (Dyn dyn) (Evt evt) = Evt $ D.apply' dyn evt
+
+-- | Infix variant of @apply@
+(<@>) :: Dyn (a -> b) -> Evt a -> Evt b
+(<@>) = apply
+
+-- | Infix variant of @snap@.
+(<@) :: Dyn a -> Evt b -> Evt a
+(<@) = snap
+
+-- | Apply combined with filter.
+applyMay :: Dyn (a -> Maybe b) -> Evt a -> Evt b
+applyMay (Dyn dyn) (Evt evt) = Evt $ D.applyMay dyn evt
+
+-- | Effectful @applyMay@.
+applyMay' :: Dyn (a -> Run (Maybe b)) -> Evt a -> Evt b
+applyMay' (Dyn dyn) (Evt evt) = Evt $ D.applyMay' dyn evt
+
+-- | Snapshot of dynamic process with event stream. All values
+-- in the event stream are substituted with current value of dynamic.
+snap :: Dyn a -> Evt b -> Evt a
+snap (Dyn dyn) (Evt evt) = Evt (D.snap dyn evt)
+
+-- | Kind of @zipWith@ function for dynamics and event streams.
+attach :: Dyn a -> Evt b -> Evt (a, b)
+attach (Dyn dyn) (Evt evt) = Evt $ D.attach dyn evt
+
+-- | Kind of @zipWith@ function for dynamics and event streams.
+attachWith :: (a -> b -> c) -> Dyn a -> Evt b -> Evt c
+attachWith f (Dyn dyn) (Evt evt) = Evt $ D.attachWith f dyn evt
+
+-- | Attach with filtering. When @Nothing@ is produced event is omitted from the stream.
+attachWithMay :: (a -> b -> Maybe c) -> Dyn a -> Evt b -> Evt c
+attachWithMay f (Dyn dyn) (Evt evt) = Evt $ D.attachWithMay f dyn evt
+
+-- | Map with filtering. When @Nothing@ is produced event is omitted from the stream.
+mapMay :: (a -> Maybe b) -> Evt a -> Evt b
+mapMay f (Evt evt) = Evt $ D.mapMay f evt
+
+-- | Map with filtering. When @Nothing@ is produced event is omitted from the stream.
+mapMay' :: (a -> Run (Maybe b)) -> Evt a -> Evt b
+mapMay' f (Evt evt) = Evt $ D.mapMay' f evt
+
+--------------------------------------------------------------------------------
+-- filters
+
+-- | Filtering of the event strewams. Only events that produce True remain in the stream.
+filters :: (a -> Bool) -> Evt a -> Evt a
+filters f (Evt evt) = Evt $ D.filters f evt
+
+-- | Effectful filtering for event streams.
+filters' :: (a -> Run Bool) -> Evt a -> Evt a
+filters' f (Evt evt) = Evt $ D.filters' f evt
+
+-- | Filters based on Maybe. If @Nothing@ is produced forthe event it is omitted from the stream.
+filterJust :: Evt (Maybe a) -> Evt a
+filterJust (Evt evt) = Evt $ D.filterJust evt
+
+-- | Filters with dynamic. When dynamic is true events pass through and when it's false
+-- events are omitted.
+whens :: Dyn Bool -> Evt a -> Evt a
+whens (Dyn dyn) (Evt evt) = Evt $ D.whens dyn evt
+
+-- | Splits the either event stream.
+splits :: Evt (Either a b) -> (Evt a, Evt b)
+splits evt = (lefts evt, rights evt)
+
+-- | Gets all left events from the stream
+lefts :: Evt (Either a b) -> Evt a
+lefts (Evt evt) = Evt $ D.lefts evt
+
+-- | Gets all right events from the stream
+rights :: Evt (Either a b) -> Evt b
+rights (Evt evt) = Evt $ D.rights evt
+
+-- | Takes only so many events from the stream
+takes :: Int -> Evt a -> Evt a
+takes n (Evt evt) = Evt $ D.takes n evt
+
+-- | Takes only so many events from the stream
+drops :: Int -> Evt a -> Evt a
+drops n (Evt evt) = Evt $ D.drops n evt
+
+-- | Takes events only while predicate is true.
+takesWhile :: (a -> Bool) -> Evt a -> Evt a
+takesWhile pred (Evt evt) = Evt $ D.takesWhile pred evt
+
+-- | Takes events only while predicate is true.
+dropsWhile :: (a -> Bool) -> Evt a -> Evt a
+dropsWhile pred (Evt evt) = Evt $ D.dropsWhile pred evt
+
+-- | Takes elements from the list by index. If index is out of bounds the event is omitted.
+listAt :: [a] -> Evt Int -> Evt a
+listAt vals (Evt evt) = Evt $ D.listAt vals evt
+
+-- | Turns event stream to toggle stream. It produce cyclic sequence of [True, False]
+toToggle :: Evt a -> Evt Bool
+toToggle (Evt evt) = Evt (D.toToggle evt)
+
+-- | Cycles the values in the list over event sream.
+cycles :: [a] -> Evt b -> Evt a
+cycles vals (Evt evt) = Evt (D.cycles vals evt)
+
+-- | Sums all the elements in the event stream
+sums :: (Num a) => Evt a -> Evt a
+sums = scan (+) 0
+
+-- | Sums all the elements in the event stream
+sumD :: (Num a) => Float -> Dyn a -> Dyn a
+sumD dt (Dyn dyn) = Dyn (D.sumD (realToFrac dt) dyn)
+
+integrate :: (VectorSpace v, Scalar v ~ Float) => Float -> Dyn v -> Dyn v
+integrate dt (Dyn dyn) = Dyn $ D.integrate dt dyn
+
+integrate2 :: (VectorSpace v, Scalar v ~ Float) => Float -> Dyn v -> Dyn v
+integrate2 dt (Dyn dyn) = Dyn $ D.integrate2 dt dyn
+
+-- | Finds the product of all elements in the event stream.
+products :: (Num a) => Evt a -> Evt a
+products = scan (*) 1
+
+-- | Monoidal append of all elements in the stream
+appends :: (Monoid a) => Evt a -> Evt a
+appends (Evt evt) = Evt (D.appends evt)
+
+-- | Monoidal fold for event streams, note that stream have to be finite for
+-- the function to complete
+folds :: Monoid a => Evt a -> Run a
+folds = foldls (<>) mempty
+
+-- | Left fold for event streams, note that stream have to be finite for
+-- the function to complete
+foldls :: (b -> a -> b) -> b -> Evt a -> Run b
+foldls f s (Evt evt) = D.foldls f s evt
+
+-- | Effectful left fold
+foldls' :: (b -> a -> Run b) -> b -> Evt a -> Run b
+foldls' f s (Evt evt) = D.foldls' f s evt
+
+-- | Right fold for event streams, note that stream have to be finite for
+-- the function to complete
+foldrs :: (a -> b -> b) -> b -> Evt a -> Run b
+foldrs f s (Evt evt) = D.foldrs f s evt
+
+-- | Effectful right fold
+foldrs' :: (a -> b -> Run b) -> b -> Evt a -> Run b
+foldrs' f s (Evt evt) = D.foldrs' f s evt
+
+-- | Starts event stream process and as callback prints it values.
+prints :: (Show a) => Evt a -> Run ()
+prints (Evt evt) = D.prints evt
+
+-- | Starts event stream process and as callback prints it values.
+putStrLns :: Evt String -> Run ()
+putStrLns (Evt evt) = D.putStrLns evt
+
+-- | Queries the event stream form dynamic and runs it all next event streams are ignored.
+switchDyn :: Dyn (Evt a) -> Evt a
+switchDyn (Dyn dyn) = Evt $ D.switchDyn $ fmap unEvt dyn
+
+-- | Joins event stream of streams. If stream is started it runs until the end.
+joins :: Evt (Evt a) -> Evt a
+joins (Evt evt) = Evt $ D.joins $ fmap unEvt evt
+
+-- | Recursion on event streams. As event streams are functions we can not use
+-- normal recursion that haskell provides. It will stuck the execution.
+-- But we can use @fix1@ to create event stream that feeds back the events to itself.
+--
+-- Note that any sort of recursion can be implemented with @fix1@.
+-- For example if we need 3-recursive event stream:
+--
+--  > fix3 ::
+--  >      (Evt a -> Evt b -> Evt c -> (Evt a, Evt b, Evt c))
+--  >   -> (Evt a, Evt b, Evt c)
+--
+-- we can use sum tpye tags
+-- to join it to single stream:
+--
+-- > data Tag a b c = TagA a | TagB b | TagC c
+--
+-- > fix3 f = unwrap $ fix1 g
+-- >   where
+-- >      g x = wrap (f (unwrapA x) (unwrapB x) (unwrapC x))
+-- >
+-- >      wrap a b c = mconcat [TagA <$> a, TagB <$> b, TagC <$> c]
+-- >      unwrap evt = (unwrapA evt, unwrapB evt, unwrapC evt)
+-- >
+-- >      unwrapA = flip mapMay $ \x -> case x of
+-- >                                  TagA a -> Just a
+-- >                                  _      -> Nothing
+--
+-- We can use this trck with any number of streams. There are helper functions: @fix2@, @fix3@, @fix4@
+fix1 :: (Evt a -> Run (Evt a)) -> Evt a
+fix1 f = Evt $ D.fix1 (fmap unEvt . f . Evt)
+
+-- | Recursion for binary functions
+fix2 :: (Evt a -> Evt b -> Run (Evt a, Evt b)) -> (Evt a, Evt b)
+fix2 f = bimap Evt Evt $ D.fix2 (\a b -> bimap unEvt unEvt <$> f (Evt a) (Evt b))
+
+-- | Recursion for ternary functions
+fix3 ::
+     (Evt a -> Evt b -> Evt c -> Run (Evt a, Evt b, Evt c))
+  -> (Evt a, Evt b, Evt c)
+fix3 f = wrap $ D.fix3 (\a b c -> unwrap <$> f (Evt a) (Evt b) (Evt c))
+  where
+    wrap (a, b, c) = (Evt a, Evt b, Evt c)
+    unwrap (a, b, c) = (unEvt a, unEvt b, unEvt c)
+
+-- | Recursion for functions of four arguments
+fix4 ::
+     (Evt a -> Evt b -> Evt c -> Evt d -> Run (Evt a, Evt b, Evt c, Evt d))
+  -> (Evt a, Evt b, Evt c, Evt d)
+fix4 f = wrap $ D.fix4 (\a b c d -> unwrap <$> f (Evt a) (Evt b) (Evt c) (Evt d))
+  where
+    wrap (a, b, c, d) = (Evt a, Evt b, Evt c, Evt d)
+    unwrap (a, b, c, d) = (unEvt a, unEvt b, unEvt c, unEvt d)
+
+-- | Flattens event stream producer by switching between event streams.
+-- When next event stream happens it shuts down the previous one.
+switch :: Evt (Evt a) -> Evt a
+switch (Evt evt) = Evt $ D.switch $ fmap unEvt evt
+
+-- | Switches between dynamic producers.
+switchD :: Dyn a -> Evt (Dyn a) -> Dyn a
+switchD (Dyn d) (Evt evts) = Dyn $ D.switchD d (fmap unDyn evts)
+
+---------------------------------------------------------
+-- channels
+
+-- | Creates the event stream that listens to MVar based channel.
+-- If any value is put chan the event stream fires the callback.
+mchanEvt :: M.Chan a -> Evt a
+mchanEvt mchan = Evt $ D.mchanEvt mchan
+
+-- | Creates the event stream that listens to MVar based channel.
+-- If any value is put chan the event stream fires the callback.
+tchanEvt :: TChan a -> Evt a
+tchanEvt tchan = Evt $ D.tchanEvt tchan
+
+-- | Creates the event stream that listens to unagi channel (package @unagi-chan@).
+-- If any value is put chan the event stream fires the callback.
+uchanEvt :: InChan a -> Evt a
+uchanEvt uchan = Evt $ D.uchanEvt uchan
+
+---------------------------------------------------------------------------
+-- utilities
+
+-- | Queries current time periodically with given period in seconds.
+clock :: NominalDiffTime -> Evt UTCTime
+clock t = Evt $ D.clock t
+
+-- | Produces pulse events with given period in seconds.
+pulse :: NominalDiffTime -> Evt ()
+pulse t = Evt $ D.pulse t
+
+-- | Produces pulse events with given period in seconds
+-- and also tells how many seconds exactly has passed.
+-- It can be useful for simulations of models that are based on differential equations.
+-- As event streams carries how much time has passed between simulation steps.
+ticks :: Float -> Evt Float
+ticks t = Evt $ realToFrac <$> D.ticks (realToFrac t)
+
+-- | Timer behaves like tocks only it produces accumulated time since beginning
+-- of the process. It calculates them by querying current time and suntracting start time from it.
+--
+-- It can be though of as:
+--
+-- > sumE ticks
+timer :: Float -> Evt Float
+timer t = Evt $ realToFrac <$> D.timer (realToFrac t)
+
+-- | Continuous timeline updated at given interval.
+timerD :: Float -> Dyn Float
+timerD t = Dyn $ realToFrac <$> D.timerD (realToFrac t)
+
+--------------------------------------------------------------------------------
+
+-- | Substitutes values in event stream with random values.
+toRandom :: (Random b) => Evt a -> Evt b
+toRandom (Evt evt) = Evt $ D.toRandom evt
+
+-- | Substitutes values in event stream with random values from the given range.
+toRandomR :: (Random b) => (b, b) -> Evt a -> Evt b
+toRandomR range (Evt evt) = Evt $ D.toRandomR range evt
+
+-- | Substitutes values in event stream with random values.
+withRandom :: (Random b) => Evt a -> Evt (b, a)
+withRandom (Evt evt) = Evt $ D.withRandom evt
+
+-- | Substitutes values in event stream with random values from the given range.
+withRandomR :: Random b => (b, b) -> Evt a -> Evt (b, a)
+withRandomR range (Evt evt) = Evt $ D.withRandomR range evt
+
+-- | Picks at random one element from the list
+oneOf :: [a] -> Evt b -> Evt a
+oneOf xs (Evt evt) = Evt $ D.oneOf xs evt
+
+-- | Picks at random one element from the list
+withOneOf :: [a] -> Evt b -> Evt (a, b)
+withOneOf xs (Evt evt) = Evt (D.withOneOf xs evt)
+
+-- | Picks at random one element from the list. We also provide distribution of events.
+-- Probability to pick up the element. Sum of probabilities should equal to 1.
+freqOf :: Dyn [(a, Rational)] -> Evt b -> Evt a
+freqOf (Dyn dynVals) (Evt evts) = Evt $ D.freqOf dynVals evts
+
+-- | Picks at random one element from the list. We also provide distribution of events.
+-- Probability to pick up the element. Sum of probabilities should equal to 1.
+withFreqOf :: Dyn [(a, Rational)] -> Evt b -> Evt (a, b)
+withFreqOf (Dyn dynVals) (Evt evts) = Evt $ D.withFreqOf dynVals evts
+
+-- | Skips at random elements from the list. We provide frequency to skip events with dynamic first argument.
+randSkip :: Dyn Double -> Evt a -> Evt a
+randSkip (Dyn prob) (Evt evt) = Evt $ D.randSkip prob evt
+
+-- | Skips elements at random. The probability to skip element depends on the element itself.
+randSkipBy :: Dyn (a -> Double) -> Evt a -> Evt a
+randSkipBy  (Dyn prob) (Evt evt) = Evt $ D.randSkipBy prob evt
+
+--------------------------------------------------------------------------------
+
+-- | Delays in the thread of execution. Note that it can interfere
+-- and screw up functions like clock, timer, pulse, ticks
+delay :: NominalDiffTime -> Evt a -> Evt a
+delay dt (Evt evt) = Evt $ D.delay dt evt
+
+-- | Delays in background by forking on each event.
+-- Note tht if delayed event was put into background prior
+-- to stopping of the main event stream it will fire anyway.
+-- There is no way to stop it.
+delayFork :: NominalDiffTime -> Evt a -> Evt a
+delayFork dt (Evt evt) = Evt $ D.delayFork dt evt
+
+--------------------------------------------------------------------------------
+-- effectful functor
+
+class RunFunctor f where
+  fmap' :: (a -> Run b) -> f a -> f b
+
+instance RunFunctor Evt where
+  fmap' f (Evt evt) = Evt $ D.fmap' f evt
+
+instance RunFunctor Dyn where
+  fmap' f (Dyn dyn) = Dyn $ D.fmap' f dyn
+
+
diff --git a/src/Dyna/Proc/Run.hs b/src/Dyna/Proc/Run.hs
new file mode 100644
--- /dev/null
+++ b/src/Dyna/Proc/Run.hs
@@ -0,0 +1,197 @@
+module Dyna.Proc.Run(
+  -- * Run the app
+  runApp,
+  Spec(..),
+  -- * User interaction
+  timeInterval,
+  getClicks,
+  mouse,
+  mouseV,
+  mouseA,
+  relMouse,
+  mouseRight,
+  mouseLeft,
+  mouseMiddle,
+  mouseAdditional,
+  mouseWheel,
+  isDrag,
+  drag,
+  dragV,
+  keyUp,
+  keyDown,
+  charUp,
+  charDown,
+) where
+
+import Control.Monad
+import Control.Monad.Reader
+import Control.Monad.IO.Class
+import Data.IORef
+import qualified Dyna as D
+import Dyna.Proc.Types
+import Graphics.Proc (
+  runProc, Proc(..), Default(..), Pio, Draw,
+  P2, MouseButton(..), Key(..), Modifiers
+ )
+import qualified Graphics.Proc as P
+import Control.Concurrent.Chan.Unagi qualified as U
+
+-- | Initialisation of the application.
+-- It's processing setup procedure that is called
+-- once at the app start up.
+newtype Spec = Spec
+  { spec'setup :: Pio ()
+  }
+
+runApp :: Spec -> Run (Dyn Draw) -> IO ()
+runApp Spec{..} mDyn = do
+  env <- newEnv
+  ref <- runRun (D.runDyn . unDyn =<< mDyn) env
+  runProc def
+    { procSetup  = spec'setup >> pure ref
+    , procDraw   = draw env
+    , procUpdate = pure
+    , procUpdateTime    = \t -> through $ updateTime env t
+    , procMousePressed  = through $ mouseBy Down env
+    , procMouseReleased = through $ mouseBy Up env
+    , procKeyPressed    = through $ keyBy Down env
+    , procKeyReleased   = through $ keyBy Up env
+    }
+  where
+    through act st = act >> pure st
+
+    draw env ref = join $ liftIO $ runRun (D.readDyn ref) env
+
+    updateTime Env{..} t = do
+      -- mouse input
+      pos      <- P.mouse
+      relPos   <- P.relMouse
+      liftIO $ do
+        prevPos  <- readIORef env'mousePos
+        prevDef1 <- readIORef env'mouseDif1
+        let dif1 = pos - prevPos
+        writeIORef env'mousePos pos
+        writeIORef env'mouseDif1 dif1
+        writeIORef env'mouseDif2 (dif1 - prevDef1)
+        writeIORef env'relMousePos relPos
+        writeIORef env'time t
+
+    mouseBy keySt Env{..} = do
+      mBut <- P.mouseButton
+      forM_ mBut $ \but -> do
+        mods <- P.modifiers
+        pos  <- P.mouse
+        let ev = Click (Right but) keySt mods pos
+        liftIO $ U.writeChan (fst env'clicks) ev
+
+    keyBy keySt Env{..} = do
+      k <- P.key
+      mods <- P.modifiers
+      pos  <- P.mouse
+      let ev = Click (Left k) keySt mods pos
+      liftIO $ U.writeChan (fst env'clicks) ev
+
+-------------------------------------------------------------------------------------
+-- user input
+
+-- | Time that has passed since previous step of simulation
+timeInterval :: Dyn Float
+timeInterval = Dyn $ D.constDyn (liftIO . readIORef =<< asks env'time)
+
+-- | Mouse position
+mouse :: Dyn P2
+mouse = Dyn $ D.constDyn (liftIO . readIORef =<< asks env'mousePos)
+
+-- | Relative mouse position (if the whole screen is in the range [0, 1])
+relMouse :: Dyn P2
+relMouse = Dyn $ D.constDyn (liftIO . readIORef =<< asks env'relMousePos)
+
+-- | Mouse velocity
+mouseV :: Dyn P2
+mouseV = Dyn $ D.constDyn (liftIO . readIORef =<< asks env'mouseDif1)
+
+-- | Mouse acceleration
+mouseA :: Dyn P2
+mouseA = Dyn $ D.constDyn (liftIO . readIORef =<< asks env'mouseDif2)
+
+-- | Reads generic click events
+getClicks :: Evt Click
+getClicks = Evt $ D.Evt $ \go -> do
+  clickChan <- asks env'clicks
+  D.runEvt (D.uchanEvt $ fst clickChan) go
+
+-- | Generic mouse click event
+mouseButton :: MouseButton -> Evt P2
+mouseButton btn' = Evt $ D.mapMay go $ unEvt getClicks
+  where
+    go = \case
+      Click (Right btn) Down mods pos | btn == btn' -> Just pos
+      _                                             -> Nothing
+
+-- | Event stream of clicks of the mouse right button
+mouseRight :: Evt P2
+mouseRight = mouseButton RightButton
+
+-- | Event stream of clicks of the mouse left button
+mouseLeft :: Evt P2
+mouseLeft = mouseButton LeftButton
+
+-- | Event stream of clicks of the mouse middle button
+mouseMiddle :: Evt P2
+mouseMiddle = mouseButton MiddleButton
+
+-- | Event stream of clicks of the mouse additional button
+mouseAdditional :: Int -> Evt P2
+mouseAdditional n = mouseButton (AdditionalButton n)
+
+-- | Mouse wheel displacement.
+-- If positive then it goes up, if negative then it goes down.
+mouseWheel :: MouseButton -> Evt Float
+mouseWheel btn' = Evt $ D.mapMay go $ unEvt getClicks
+  where
+    go = \case
+      Click (Right WheelUp)   Down _ _ -> Just 1
+      Click (Right WheelDown) Down _ _ -> Just (-1)
+      _                                -> Nothing
+
+-- | Event stream of key up actions
+keyUp :: Key -> Evt Modifiers
+keyUp = keyBy Up
+
+-- | Event stream of key down actions
+keyDown :: Key -> Evt Modifiers
+keyDown = keyBy Down
+
+-- | Event stream of char press up actions
+charUp :: Char -> Evt Modifiers
+charUp = keyUp . Char
+
+-- | Event stream of char press down actions
+charDown :: Char -> Evt Modifiers
+charDown = keyDown . Char
+
+keyBy :: KeyState -> Key -> Evt Modifiers
+keyBy st' key' = Evt $ D.mapMay go $ unEvt getClicks
+  where
+    go (Click (Left key) st mods _)
+      | key == key' && st == st' = Just mods
+      | otherwise                = Nothing
+
+isDrag :: MouseButton -> Dyn Bool
+isDrag btn = Dyn $ D.scanD collect False $ D.mapMay go $ unEvt getClicks
+  where
+    go = \case
+      Click (Right mbtn) st _ _ | mbtn == btn -> Just st
+      _                                       -> Nothing
+
+    collect a st = case a of
+      Up   -> False
+      Down -> True
+
+-- | Displacement on drag, if no drag it becomes zero
+dragV :: MouseButton -> Dyn P2
+dragV btn = (\x -> if x then id else const 0) <$> isDrag btn <*> mouseV
+
+-- | Position of the mouse during drag, if no drag it becomes zero
+drag :: MouseButton -> Dyn P2
+drag btn = (\x -> if x then id else const 0) <$> isDrag btn <*> mouse
diff --git a/src/Dyna/Proc/Types.hs b/src/Dyna/Proc/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/Dyna/Proc/Types.hs
@@ -0,0 +1,106 @@
+module Dyna.Proc.Types where
+
+import Prelude hiding ((<*))
+import Control.Applicative (liftA2, liftA3)
+import Control.Concurrent.Chan.Unagi
+import Control.Monad.Reader
+import Control.Monad.Base
+import Control.Monad.Random.Class
+import Control.Monad.Trans.Control (MonadBaseControl(..))
+import Data.IORef
+import Data.String
+import Data.Boolean
+import Data.AdditiveGroup
+import Data.AffineSpace
+import Data.VectorSpace
+import Data.Basis
+import Data.Cross
+import Temporal.Class
+
+import qualified Dyna as D
+import Graphics.Proc hiding ((<*))
+
+-- | Monad that drives the application
+newtype Run a = Run { unRun :: ReaderT Env IO a }
+  deriving (Functor, Applicative, Monad, MonadReader Env,
+            MonadIO, MonadBase IO, MonadRandom)
+
+newtype StMRun a = StMRun { unStMRun :: StM (ReaderT Env IO) a }
+
+instance MonadBaseControl IO Run where
+    type StM Run a = StMRun a
+    liftBaseWith f = Run $ liftBaseWith $ \q -> f (fmap StMRun . q . unRun)
+    restoreM = Run . restoreM . unStMRun
+
+instance D.Frp Run where
+  type Ref Run = IORef
+
+runRun :: Run a -> Env -> IO a
+runRun (Run act) env = runReaderT act env
+
+------------------------------------------------------------------------------------
+
+-- | Event streams
+newtype Evt a = Evt { unEvt :: D.Evt Run a }
+  deriving (Functor, Semigroup, Monoid, Applicative, Monad,
+            Melody, Harmony, Compose, Loop)
+
+type instance DurOf (Evt a) = Float
+
+instance Limit (Evt a) where
+  lim t (Evt evt) = Evt $ lim (realToFrac t) evt
+
+-- | Dynamic values (step-wise continuous process)
+newtype Dyn a = Dyn { unDyn :: D.Dyn Run a }
+  deriving (Functor, Applicative, Num, Fractional, Semigroup, Monoid, IsString,
+            Boolean, AdditiveGroup, VectorSpace, HasNormal, HasCross2, HasCross3,
+            AffineSpace, HasBasis)
+
+type instance BooleanOf (Dyn a) = Dyn (BooleanOf a)
+
+instance (IfB a) => IfB (Dyn a) where
+  ifB = liftA3 ifB
+
+instance (EqB a) => EqB (Dyn a) where
+  (==*) = liftA2 (==*)
+
+instance (OrdB a) => OrdB (Dyn a) where
+  (<*) = liftA2 (<*)
+  (>*) = liftA2 (>*)
+  (<=*) = liftA2 (<=*)
+  (>=*) = liftA2 (>=*)
+
+------------------------------------------------------------------------------------
+-- environment
+
+-- | All sorts of clicks
+data Click = Click (Either Key MouseButton) KeyState Modifiers P2
+
+data KeyState = Up | Down
+  deriving (Show, Eq)
+
+-- | Applicaition environment
+data Env = Env
+  { env'resizeChan  :: D.UChan (Int, Int)
+  , env'keyChan     :: D.UChan Key
+  , env'relMousePos :: IORef P2
+  , env'mousePos    :: IORef P2
+  , env'mouseDif1   :: IORef P2
+  , env'mouseDif2   :: IORef P2
+  , env'time        :: IORef Float
+  , env'clicks      :: D.UChan Click
+  }
+
+-- | Create new environment
+newEnv :: IO Env
+newEnv = do
+  env'resizeChan <- newChan
+  env'clicks <- newChan
+  env'keyChan <- newChan
+  env'time <- newIORef 0
+  env'relMousePos <- newIORef (P2 0 0)
+  env'mousePos <- newIORef (P2 0 0)
+  env'mouseDif1 <- newIORef (P2 0 0)
+  env'mouseDif2 <- newIORef (P2 0 0)
+  pure Env{..}
+
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,2 @@
+main :: IO ()
+main = putStrLn "Test suite not yet implemented"
