diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,3 @@
+# Changelog for freer-simple-random
+
+## Unreleased changes
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 Co—Star Astrology, Ben Weitzman
+
+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/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,1 @@
+# freer-simple-random
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/freer-simple-random.cabal b/freer-simple-random.cabal
new file mode 100644
--- /dev/null
+++ b/freer-simple-random.cabal
@@ -0,0 +1,59 @@
+-- This file has been generated from package.yaml by hpack version 0.28.2.
+--
+-- see: https://github.com/sol/hpack
+--
+-- hash: d04c208e5903df12659399a5efdc671f2a0c0bb3917cd0b6b625f002a4c8885f
+
+name:           freer-simple-random
+version:        0.1.0.0
+synopsis:       Random number generators using freer-simple
+description:    Please see the README on Gitlab at <https://gitlab.com/costar-astrology/freer-simple-contrib/tree/master/freer-simple-random>
+category:       Control, Random
+author:         Ben Weitzman
+maintainer:     ben@costarastrology.com
+copyright:      2018 Ben Weitzman
+license:        MIT
+license-file:   LICENSE
+build-type:     Simple
+cabal-version:  >= 1.10
+extra-source-files:
+    ChangeLog.md
+    README.md
+
+source-repository head
+  type: git
+  location: https://gitlab.com/costar-astrology/freer-simple-contrib/tree/master/freer-simple-random
+
+library
+  exposed-modules:
+      Control.Monad.Freer.Random
+  other-modules:
+      Paths_freer_simple_random
+  hs-source-dirs:
+      src
+  default-extensions: GADTs FlexibleContexts TypeOperators DataKinds StandaloneDeriving DeriveDataTypeable MultiParamTypeClasses FlexibleInstances UndecidableInstances TypeApplications ScopedTypeVariables TypeFamilies RankNTypes DeriveAnyClass OverloadedStrings FunctionalDependencies ConstraintKinds EmptyCase BangPatterns
+  build-depends:
+      base >=4.7 && <5
+    , containers >=0.5 && <0.6
+    , freer-simple >=1.1 && <1.2
+    , random >=1.1 && <1.2
+  default-language: Haskell2010
+
+test-suite freer-simple-random-test
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  other-modules:
+      Control.Monad.Freer.RandomSpec
+      Paths_freer_simple_random
+  hs-source-dirs:
+      test
+  default-extensions: GADTs FlexibleContexts TypeOperators DataKinds StandaloneDeriving DeriveDataTypeable MultiParamTypeClasses FlexibleInstances UndecidableInstances TypeApplications ScopedTypeVariables TypeFamilies RankNTypes DeriveAnyClass OverloadedStrings FunctionalDependencies ConstraintKinds EmptyCase BangPatterns
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      base >=4.7 && <5
+    , containers >=0.5 && <0.6
+    , freer-simple >=1.1 && <1.2
+    , freer-simple-random
+    , hspec
+    , random >=1.1 && <1.2
+  default-language: Haskell2010
diff --git a/src/Control/Monad/Freer/Random.hs b/src/Control/Monad/Freer/Random.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Monad/Freer/Random.hs
@@ -0,0 +1,124 @@
+{-# LANGUAGE AllowAmbiguousTypes #-}
+
+{-|
+Module      : Control.Monad.Freer.Random
+Description : Effect for generating random values
+Copyright   : (c) Ben Weitzman 2018
+License     : MIT
+Maintainer  : ben@costarastrolgoy.com
+Stability   : experimental
+Portability : POSIX
+-}
+module Control.Monad.Freer.Random
+  (Random
+  ,random
+  ,randomR
+  ,runRandom
+  ,knownRandom
+  ,runRandomWithSeed
+  ,silentRandom
+  ,FindInList
+  )
+where
+
+import Control.Monad.Freer
+import Control.Monad.Freer.Internal
+import qualified System.Random as R
+import Data.Typeable ((:~:)(..))
+import Data.Maybe
+import Data.Set (Set)
+import qualified Data.Set as S
+
+data Elem a as where
+  Here :: Elem a (a ': as)
+  There :: Elem a as -> Elem a (b ': as)
+
+-- | Find a type inside of a type level list. Used to write code that generates multiple types of random values
+-- that are polymorphic over the set of all random values that are generated.
+--
+-- @
+-- getARandomNumber :: ('FindInList' 'Int' typs) => 'Eff' ('Random' typs ': r) 'Int'
+-- getARandomNumber = random
+--
+-- getARandomBool :: ('FindInList' 'Bool' typs) => 'Eff' ('Random' typs ': r) 'Bool'
+-- getARandomBool = random
+--
+-- getABoolAndInt :: ('FindInList' 'Bool' typs, 'FindInList' 'Int' typs) => 'Eff' ('Random' typs ': r) 'Int'
+-- getABoolAndInt = do
+--   rInt <- getARandomNumber
+--   rBool <- getARandomBool
+--   if rBool
+--     then return rInt
+--     else return $ rInt + 1
+-- @
+class FindInList a as where
+  find :: Elem a as
+
+instance {-# OVERLAPPING #-} FindInList a (a ': as) where
+  find = Here
+
+instance FindInList a as => FindInList a (b ': as) where
+  find = There find
+
+-- | The 'Random' effect generates a random value for you. It is parametrized by the set of values (@typs@)
+-- that it generates. The 'Random' effect needs to come at the head of the effect list in order to make type
+-- in order to make type inference of the set of types possible.
+data Random typs a where
+  Random :: (R.Random a) => Elem a typs -> Random typs a
+  RandomR :: (R.Random a) => Elem a typs -> (a, a) -> Random typs a
+
+-- | Generate a single random value
+random :: forall a typs r p. (R.Random a, FindInList a typs) => Eff (Random typs ': r) a
+random = send $ Random (find @a @typs)
+
+-- | Generate a single random value in a range
+randomR :: forall a typs r p. (R.Random a, FindInList a typs) =>  (a, a) -> Eff (Random typs ': r) a
+randomR range = send $ RandomR (find @a @typs) range
+
+-- | Use the 'IO' effect to handle generation of random values
+runRandom :: forall t r a . Member IO r => Eff (Random t ': r) a -> Eff r a
+runRandom = interpret $ \r -> case r of
+  Random _ -> send R.randomIO
+  RandomR _ range -> send $ R.randomRIO range
+
+-- | Eliminate a 'Random' effect that doesn't generate any values
+silentRandom :: Eff (Random '[] ': r) v -> Eff r v
+silentRandom = interpret $ \r -> case r of 
+
+-- | Use a single given value as the "random" value. The given value is always used, even if it's
+-- outside the range given to 'randomR'  
+knownRandom :: forall typ a r v . a -> Eff (Random (a ': typ) ': r) v -> Eff (Random typ ': r) v
+knownRandom known = reinterpret $ \r -> case r of
+  Random Here -> return known
+  Random (There q) -> send $ Random q
+  RandomR Here _ -> return known
+  RandomR (There q) _ -> send $ Random q
+
+-- | Use a seed + a PRNG to handle generation of random values. 
+runRandomWithSeed :: forall typ r v . Int -> Eff (Random typ ': r) v -> Eff r v
+runRandomWithSeed seed = handleRelayS (R.mkStdGen seed) (\gen val -> return val) handler
+  where
+    handler :: R.StdGen -> Random typ a -> (R.StdGen -> a -> Eff r b) -> Eff r b
+    handler gen (Random _) next =
+      let r = R.random gen
+      in next (snd r) (fst r)
+    handler gen (RandomR _ range) next = 
+      let r = R.randomR range gen 
+      in next (snd r) (fst r)
+
+-- | Pick a random value from a list
+pickRandom :: (FindInList Int typ) => [a] -> Eff (Random typ ': r) (Maybe a)
+pickRandom vals = do
+  let max' = length vals
+  idx <- randomR (0, max' - 1)
+  return . listToMaybe $ drop idx vals
+
+-- | Shuffle a set of values into a sequence
+shuffle :: (FindInList Int typ) => Set a -> Eff (Random typ ': r) [a]
+shuffle s
+  | S.null s = return []
+  | otherwise = do
+      idx <- randomR (0, S.size s - 1)
+      let val = S.elemAt idx s
+      rest <- shuffle $ S.deleteAt idx s
+      return $ val : rest
diff --git a/test/Control/Monad/Freer/RandomSpec.hs b/test/Control/Monad/Freer/RandomSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Control/Monad/Freer/RandomSpec.hs
@@ -0,0 +1,62 @@
+{-# LANGUAGE AllowAmbiguousTypes #-}
+
+module Control.Monad.Freer.RandomSpec where
+ 
+import Control.Monad.Freer.Random
+
+import Control.Monad.Freer
+import Test.Hspec
+import qualified System.Random as R
+
+spec :: Spec
+spec = do
+  describe "random" $ do
+    
+    it "should return a random number" $ do         
+      (a :: Int, b) <- runM $ runRandom @'[Int] getTwoRandoms
+      a `shouldNotBe` b
+
+  describe "known random" $ do
+    
+    it "should give known random" $ do
+      let (a :: Int, b) = run . silentRandom . knownRandom knownInt $ getTwoRandoms 
+      a `shouldBe` b
+      a `shouldBe` knownInt
+      
+    it "should not affect other randoms" $ do
+      (a :: Int, b :: Int, c :: Double, d :: Double) <- runM . runRandom @'[Double] . knownRandom  knownInt $ do
+        (a, b) <- getTwoRandoms
+        (c, d) <- getTwoRandoms
+        return (a, b, c, d)
+      a `shouldBe` knownInt
+      b `shouldBe` knownInt 
+      c `shouldNotBe` d 
+ 
+    it "can provide two known randoms" $ do
+      let (a, b, c, d) = run . silentRandom . knownRandom knownInt . knownRandom knownDouble $ do
+            (a, b) <- getTwoRandoms
+            (c, d) <- getTwoRandoms
+            return (a, b, c, d)
+      a `shouldBe` knownInt
+      b `shouldBe` knownInt
+      c `shouldBe` knownDouble
+      d `shouldBe` knownDouble
+
+  describe "random with seed" $ do
+    it "should be reproducible" $ do
+      let (a :: Int, b) = run $ runRandomWithSeed @'[Int] 0 getTwoRandoms
+      let (c, d) = run $ runRandomWithSeed @'[Int] 0 getTwoRandoms
+      a `shouldNotBe` b
+      c `shouldNotBe` d
+      a `shouldBe` c
+      b `shouldBe` d
+      
+        
+getTwoRandoms :: forall a rs effs . (R.Random a, FindInList a rs) => Eff (Random rs ': effs) (a, a) 
+getTwoRandoms = (,) <$> random <*> random
+
+knownInt :: Int
+knownInt = 3
+
+knownDouble :: Double
+knownDouble = pi
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
