diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,12 @@
+# Changelog
+
+`embla` uses [PVP Versioning][1].
+The changelog is available [on GitHub][2].
+
+0.0.0
+=====
+
+* Initially created.
+
+[1]: https://pvp.haskell.org
+[2]: https://github.com/chessai/embla/releases
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2019, chessai
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+2. 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.
+
+3. Neither the name of the copyright holder nor the names of its
+   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 HOLDER 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,6 @@
+# embla
+
+[![Hackage](https://img.shields.io/hackage/v/embla.svg)](https://hackage.haskell.org/package/embla)
+[![BSD3 license](https://img.shields.io/badge/license-BSD3-blue.svg)](LICENSE)
+
+execute actions periodically while avoiding drift
diff --git a/embla.cabal b/embla.cabal
new file mode 100644
--- /dev/null
+++ b/embla.cabal
@@ -0,0 +1,53 @@
+cabal-version: 2.2
+name:
+  embla
+version:
+  0.1
+synopsis:
+  execute actions periodically while avoiding drift
+description:
+  This package is intended for use cases when 'cron'
+  is overkill, but the importance of avoiding drift
+  is still there. No care is taken w.r.t. GC times.
+homepage:
+  https://github.com/chessai/embla
+bug-reports:
+  https://github.com/chessai/embla/issues
+license:
+  BSD-3-Clause
+license-file:
+  LICENSE
+author:
+  chessai
+maintainer:
+  chessai <chessai1996@gmail.com>
+copyright:
+  © 2019 chessai
+category:
+  Utility
+build-type:
+  Simple
+extra-doc-files:
+    README.md
+  , CHANGELOG.md
+tested-with:
+  GHC == 8.4.4, GHC == 8.6.3
+
+library
+  hs-source-dirs:
+    src
+  exposed-modules:
+    Embla
+  build-depends:
+    , base >= 4.11 && < 4.14
+    , chronos >= 1.0.4 && < 1.1
+  ghc-options:
+    -Wall
+  default-language:
+    Haskell2010
+
+source-repository head
+  type:
+    git
+  location:
+    https://github.com/chessai/embla.git
diff --git a/src/Embla.hs b/src/Embla.hs
new file mode 100644
--- /dev/null
+++ b/src/Embla.hs
@@ -0,0 +1,40 @@
+-- | This package is intended for use cases when 'cron' is overkill,
+--   but the importance of avoiding drift is still there. No care is
+--   taken w.r.t. GC times.
+module Embla
+  ( embla
+  ) where
+
+import Control.Concurrent (threadDelay)
+import GHC.Clock (getMonotonicTimeNSec)
+import qualified Chronos as C
+
+-- | Given an 'IO' action and an interval at which to perform it,
+--   perform the 'IO' action repeatedly at every interval. This function
+--   is useful when you have an 'IO' action that can take a significant/variable
+--   amount of time, and you want to avoid drift.
+--
+--   /Note/: This function will not work as expected if the given 'IO' action
+--   takes longer than the polling interval.
+--
+--   /Note/: No care is taken w.r.t. GC times.
+embla :: Int -> IO a -> IO a
+embla interval io = do
+  anchor <- fmap fromIntegral getMonotonicTimeNSec
+  let go = do
+        _ <- io
+        threadDelay =<< solveTime anchor interval
+        go
+  go
+ 
+solveTime :: ()
+  => Int
+  -> Int
+  -> IO Int
+solveTime t0 p = do
+  C.Time now' <- C.now
+  let now = fromIntegral now'
+  let p' = p * 1000000000
+  let untilNextTime = ((div (now - t0) p') + 1) * p'
+  let solved = div (t0 + untilNextTime - now) 1000
+  pure solved 
