diff --git a/src/Sound/Tidal/Control.hs b/src/Sound/Tidal/Control.hs
--- a/src/Sound/Tidal/Control.hs
+++ b/src/Sound/Tidal/Control.hs
@@ -106,7 +106,7 @@
 chopArc (Arc s e) n = map (\i -> Arc (s + (e - s) * (fromIntegral i / fromIntegral n)) (s + (e - s) * (fromIntegral (i + 1) / fromIntegral n))) [0 .. n - 1]
 
 _chop :: Int -> ControlPattern -> ControlPattern
-_chop n pat = keepTactus (withTactus (* toRational n) pat) $ squeezeJoin $ f <$> pat
+_chop n pat = keepSteps (withSteps (* toRational n) pat) $ squeezeJoin $ f <$> pat
   where
     f v = fastcat $ map (pure . rangemap v) slices
     rangemap v (b, e) = Map.union (fromMaybe (makeMap (b, e)) $ merge v (b, e)) v
@@ -143,7 +143,7 @@
 striate = patternify _striate
 
 _striate :: Int -> ControlPattern -> ControlPattern
-_striate n p = keepTactus (withTactus (* toRational n) p) $ fastcat $ map offset [0 .. n - 1]
+_striate n p = keepSteps (withSteps (* toRational n) p) $ fastcat $ map offset [0 .. n - 1]
   where
     offset i = mergePlayRange (fromIntegral i / fromIntegral n, fromIntegral (i + 1) / fromIntegral n) <$> p
 
@@ -175,7 +175,7 @@
 striate' = striateBy
 
 _striateBy :: Int -> Double -> ControlPattern -> ControlPattern
-_striateBy n f p = keepTactus (withTactus (* toRational n) p) $ fastcat $ map (offset . fromIntegral) [0 .. n - 1]
+_striateBy n f p = keepSteps (withSteps (* toRational n) p) $ fastcat $ map (offset . fromIntegral) [0 .. n - 1]
   where
     offset i = mergePlayRange (slot * i, (slot * i) + f) <$> p
     slot = (1 - f) / fromIntegral (n - 1)
@@ -320,7 +320,7 @@
 --
 --  > d1 $ fast 4 $ randslice 32 $ sound "bev"
 randslice :: Pattern Int -> ControlPattern -> ControlPattern
-randslice = patternify $ \n p -> keepTactus (withTactus (* (toRational n)) $ p) $ innerJoin $ (\i -> _slice n i p) <$> _irand n
+randslice = patternify $ \n p -> keepSteps (withSteps (* (toRational n)) $ p) $ innerJoin $ (\i -> _slice n i p) <$> _irand n
 
 _splice :: Int -> Pattern Int -> ControlPattern -> Pattern (Map.Map String Value)
 _splice bits ipat pat = withEvent f (slice (pure bits) ipat pat) # P.unit (pure "c")
@@ -338,7 +338,7 @@
 --
 --  > d1 $ splice 8 "[<0*8 0*2> 3*4 2 4] [4 .. 7]" $ sound "breaks165"
 splice :: Pattern Int -> Pattern Int -> ControlPattern -> Pattern (Map.Map String Value)
-splice bitpat ipat pat = setTactusFrom bitpat $ innerJoin $ (\bits -> _splice bits ipat pat) <$> bitpat
+splice bitpat ipat pat = setStepsFrom bitpat $ innerJoin $ (\bits -> _splice bits ipat pat) <$> bitpat
 
 -- |
 --  @loopAt@ makes a sample fit the given number of cycles. Internally, it
diff --git a/src/Sound/Tidal/Core.hs b/src/Sound/Tidal/Core.hs
--- a/src/Sound/Tidal/Core.hs
+++ b/src/Sound/Tidal/Core.hs
@@ -302,8 +302,8 @@
 --  The first parameter to run can be given as a pattern:
 --
 --  > d1 $ n (run "<4 8 4 6>") # sound "amencutup"
-run :: (Enum a, Num a) => Pattern a -> Pattern a
-run = (>>= _run)
+run :: (Enum a, Num a, Real a) => Pattern a -> Pattern a
+run npat = setSteps (toRational <$> pureValue npat) $ npat >>= _run
 
 _run :: (Enum a, Num a) => a -> Pattern a
 _run n = fastFromList [0 .. n - 1]
@@ -377,12 +377,12 @@
 --  > d1 $ fastcat [sound "bd*2 sn", sound "arpy jvbass*2", sound "drum*2"]
 --  > d1 $ fastcat [sound "bd*2 sn", sound "jvbass*3", sound "drum*2", sound "ht mt"]
 fastCat :: [Pattern a] -> Pattern a
-fastCat (p : []) = p
-fastCat ps = setTactus t $ _fast (toTime $ length ps) $ cat ps
+fastCat [p] = p
+fastCat ps = setSteps t $ _fast (toTime $ length ps) $ cat ps
   where
-    t = fastCat <$> (sequence $ map tactus ps)
+    t = sum <$> mapM steps ps
 
---  where t = fromMaybe (toRational $ length ps) $ ((* (toRational $ length ps)) . foldl1 lcmr) <$> (sequence $ map tactus ps)
+--  where t = fromMaybe (toRational $ length ps) $ ((* (toRational $ length ps)) . foldl1 lcmr) <$> (sequence $ map steps ps)
 
 -- | Alias for @fastCat@
 fastcat :: [Pattern a] -> Pattern a
@@ -402,8 +402,8 @@
 --  >              , (1, s "superpiano" # n 0)
 --  >              ]
 timeCat :: [(Time, Pattern a)] -> Pattern a
-timeCat ((_, p) : []) = p
-timeCat tps = setTactus (Just $ pure total) $ stack $ map (\(s, e, p) -> compressArc (Arc (s / total) (e / total)) p) $ arrange 0 $ filter (\(t, _) -> t > 0) $ tps
+timeCat [(_, p)] = p
+timeCat tps = setSteps (Just total) $ stack $ map (\(s, e, p) -> compressArc (Arc (s / total) (e / total)) p) $ arrange 0 $ filter (\(t, _) -> t > 0) $ tps
   where
     total = sum $ map fst tps
     arrange :: Time -> [(Time, Pattern a)] -> [(Time, Time, Pattern a)]
@@ -468,14 +468,11 @@
 -- >  sound "arpy" +| n "0 .. 15"
 -- > ] # speed "[[1 0.8], [1.5 2]*2]/3"
 stack :: [Pattern a] -> Pattern a
-stack pats = (foldr overlay silence pats) {tactus = t}
+stack pats = (foldr overlay silence pats) {steps = t}
   where
     t
-      | length pats == 0 = Nothing
-      -- TODO - something cleverer..
-      | otherwise = (mono . stack) <$> (sequence $ map tactus pats)
-
---          | otherwise = foldl1 lcmr <$> (sequence $ map tactus pats)
+      | null pats = Nothing
+      | otherwise = foldl1 lcmr <$> mapM steps pats
 
 -- ** Manipulating time
 
@@ -538,7 +535,7 @@
 zoomArc (Arc s e) p
   | s >= e = nothing
   | otherwise =
-      withTactus (* d) $
+      withSteps (* d) $
         splitQueries $
           withResultArc (mapCycle ((/ d) . subtract s)) $
             withQueryArc (mapCycle ((+ s) . (* d))) p
diff --git a/src/Sound/Tidal/Params.hs b/src/Sound/Tidal/Params.hs
--- a/src/Sound/Tidal/Params.hs
+++ b/src/Sound/Tidal/Params.hs
@@ -1,8 +1,7 @@
 module Sound.Tidal.Params where
 
--- Please note, this file is generated by bin/generate-params.hs
--- Submit any pull requests against that file and/or params-header.hs
--- in the same folder, thanks.
+-- Please note, this file used to be generated by bin/generate-params.hs
+-- We are currently updating it directly.
 
 {-
     Params.hs - Provides the basic control patterns available to TidalCycles by default
@@ -352,6 +351,13 @@
 bandqrecv :: Pattern Int -> ControlPattern
 bandqrecv busid = pI "^bandq" busid
 
+-- | A pattern of strings. When sent to SuperDirt, will be prepended to sample
+-- folder names, separated by an underscore. This allows sample sets to be
+-- organised into separate banks. See
+-- https://github.com/musikinformatik/SuperDirt/pull/312
+bank :: Pattern String -> ControlPattern
+bank = pS "bank"
+
 -- | @begin@ receives a pattern of numbers from 0 to 1 and skips the beginning
 -- of each sample by the indicated proportion. I.e., 0 would play the sample from
 -- the start, 1 would skip the whole sample, and 0.25 would cut off the first
@@ -1708,6 +1714,9 @@
 
 legatobus :: Pattern Int -> Pattern Double -> ControlPattern
 legatobus _ _ = error $ "Control parameter 'legato' can't be sent to a bus."
+
+clip :: Pattern Double -> ControlPattern
+clip = legato
 
 leslie :: Pattern Double -> ControlPattern
 leslie = pF "leslie"
diff --git a/src/Sound/Tidal/ParseBP.hs b/src/Sound/Tidal/ParseBP.hs
--- a/src/Sound/Tidal/ParseBP.hs
+++ b/src/Sound/Tidal/ParseBP.hs
@@ -2,6 +2,7 @@
 {-# LANGUAGE DeriveFunctor #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE GADTs #-}
+{-# LANGUAGE InstanceSigs #-}
 {-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE StandaloneDeriving #-}
@@ -31,28 +32,68 @@
 import Control.Applicative ()
 import qualified Control.Exception as E
 import Data.Bifunctor (first)
-import Data.Colour
-import Data.Colour.Names
+import Data.Colour (Colour)
+import Data.Colour.Names (readColourName)
 import Data.Functor (($>))
 import Data.Functor.Identity (Identity)
 import Data.List (intercalate)
-import Data.Maybe
-import Data.Ratio
+import Data.Maybe (fromMaybe)
+import Data.Ratio ((%))
+import Data.String (IsString (..))
 import Data.Typeable (Typeable)
-import GHC.Exts (IsString (..))
 import Sound.Tidal.Chords
+  ( Modifier (..),
+    chordTable,
+    chordToPatSeq,
+  )
 import Sound.Tidal.Core
-import Sound.Tidal.Pattern hiding ((*>), (<*))
+  ( cB_,
+    cF_,
+    cI_,
+    cN_,
+    cR_,
+    cS_,
+    fastFromList,
+    stack,
+    timeCat,
+    _cX_,
+  )
+import Sound.Tidal.Pattern
+  ( Context (Context),
+    Note (Note),
+    Pattern,
+    Time,
+    fast,
+    getS,
+    innerJoin,
+    rotL,
+    setContext,
+    silence,
+    slow,
+    unwrap,
+  )
 import Sound.Tidal.UI
+  ( chooseBy,
+    euclidOff,
+    euclidOffBool,
+    rand,
+    segment,
+    _degradeByUsing,
+  )
 import Sound.Tidal.Utils (fromRight)
 import Text.Parsec.Error
+  ( ParseError,
+    errorMessages,
+    errorPos,
+    showErrorMessages,
+  )
 import qualified Text.Parsec.Prim
 import Text.ParserCombinators.Parsec
 import Text.ParserCombinators.Parsec.Language (haskellDef)
 import qualified Text.ParserCombinators.Parsec.Token as P
 
 data TidalParseError = TidalParseError
-  { parsecError :: ParseError,
+  { parsecError :: Text.Parsec.Error.ParseError,
     code :: String
   }
   deriving (Eq, Typeable)
@@ -60,10 +101,11 @@
 instance E.Exception TidalParseError
 
 instance Show TidalParseError where
+  show :: TidalParseError -> String
   show err = "Syntax error in sequence:\n  \"" ++ code err ++ "\"\n  " ++ pointer ++ "  " ++ message
     where
-      pointer = replicate (sourceColumn $ errorPos perr) ' ' ++ "^"
-      message = showErrorMessages "or" "unknown parse error" "expecting" "unexpected" "end of input" $ errorMessages perr
+      pointer = replicate (sourceColumn $ Text.Parsec.Error.errorPos perr) ' ' ++ "^"
+      message = Text.Parsec.Error.showErrorMessages "or" "unknown parse error" "expecting" "unexpected" "end of input" $ Text.Parsec.Error.errorMessages perr
       perr = parsecError err
 
 type MyParser = Text.Parsec.Prim.Parsec String Int
@@ -103,7 +145,7 @@
   show (TPat_Repeat r v) = "TPat_Repeat (" ++ show r ++ ") (" ++ show v ++ ")"
   show (TPat_EnumFromTo a b) = "TPat_EnumFromTo (" ++ show a ++ ") (" ++ show b ++ ")"
   show (TPat_Var s) = "TPat_Var " ++ show s
-  show (TPat_Chord g iP nP msP) = "TPat_Chord (" ++ (show $ fmap g iP) ++ ") (" ++ show nP ++ ") (" ++ show msP ++ ")"
+  show (TPat_Chord g iP nP msP) = "TPat_Chord (" ++ show (fmap g iP) ++ ") (" ++ show nP ++ ") (" ++ show msP ++ ")"
 
 instance Functor TPat where
   fmap f (TPat_Atom c v) = TPat_Atom c (f v)
@@ -149,7 +191,7 @@
 tShow TPat_Silence = "silence"
 tShow (TPat_EnumFromTo a b) = "unwrap $ fromTo <$> (" ++ tShow a ++ ") <*> (" ++ tShow b ++ ")"
 tShow (TPat_Var s) = "getControl " ++ s
-tShow (TPat_Chord f n name mods) = "chord (" ++ (tShow $ fmap f n) ++ ") (" ++ tShow name ++ ")" ++ tShowList mods
+tShow (TPat_Chord f n name mods) = "chord (" ++ tShow (fmap f n) ++ ") (" ++ tShow name ++ ")" ++ tShowList mods
 tShow a = "can't happen? " ++ show a
 
 toPat :: (Parseable a, Enumerable a) => TPat a -> Pattern a
@@ -211,7 +253,7 @@
 steps_size ((TPat_Repeat n p) : ps) = replicate n (1, tShow p) ++ steps_size ps
 steps_size (p : ps) = (1, tShow p) : steps_size ps
 
-parseBP :: (Enumerable a, Parseable a) => String -> Either ParseError (Pattern a)
+parseBP :: (Enumerable a, Parseable a) => String -> Either Text.Parsec.Error.ParseError (Pattern a)
 parseBP s = toPat <$> parseTPat s
 
 parseBP_E :: (Enumerable a, Parseable a) => String -> Pattern a
@@ -222,7 +264,7 @@
     toE (Left e) = E.throw $ TidalParseError {parsecError = e, code = s}
     toE (Right tp) = toPat tp
 
-parseTPat :: (Parseable a) => String -> Either ParseError (TPat a)
+parseTPat :: (Parseable a) => String -> Either Text.Parsec.Error.ParseError (TPat a)
 parseTPat = runParser (pTidal parseRest <* eof) (0 :: Int) ""
 
 -- | a '-' is a negative sign if followed anything but another dash
diff --git a/src/Sound/Tidal/Pattern.hs b/src/Sound/Tidal/Pattern.hs
--- a/src/Sound/Tidal/Pattern.hs
+++ b/src/Sound/Tidal/Pattern.hs
@@ -56,7 +56,7 @@
   }
 
 -- | A datatype representing events taking place over time
-data Pattern a = Pattern {query :: State -> [Event a], tactus :: Maybe (Pattern Rational), pureValue :: Maybe a}
+data Pattern a = Pattern {query :: State -> [Event a], steps :: Maybe (Rational), pureValue :: Maybe a}
   deriving (Generic, Functor)
 
 instance (NFData a) => NFData (Pattern a)
@@ -64,28 +64,25 @@
 pattern :: (State -> [Event a]) -> Pattern a
 pattern f = Pattern f Nothing Nothing
 
-setTactus :: Maybe (Pattern Rational) -> Pattern a -> Pattern a
-setTactus r p = p {tactus = r}
+setSteps :: Maybe Rational -> Pattern a -> Pattern a
+setSteps r p = p {steps = r}
 
-setTactusFrom :: Pattern b -> Pattern a -> Pattern a
-setTactusFrom a b = b {tactus = tactus a}
+setStepsFrom :: Pattern b -> Pattern a -> Pattern a
+setStepsFrom a b = b {steps = steps a}
 
-withTactus :: (Rational -> Rational) -> Pattern a -> Pattern a
-withTactus f p = p {tactus = fmap (fmap f) $ tactus p}
+withSteps :: (Rational -> Rational) -> Pattern a -> Pattern a
+withSteps f p = p {steps = fmap f $ steps p}
 
-steps :: Pattern Rational -> Pattern a -> Pattern a
-steps target p@(Pattern _ (Just t) _) = setTactus (Just target) $ fast (target / t) p
+pace :: Rational -> Pattern a -> Pattern a
+pace target p@(Pattern _ (Just t) _) = setSteps (Just target) $ _fast (target / t) p
 -- raise error?
-steps _ p = p
-
--- _steps :: Pattern Rational -> Pattern a -> Pattern a
--- _steps = patternify _steps
+pace _ p = p
 
 keepMeta :: Pattern a -> Pattern a -> Pattern a
-keepMeta from to = to {tactus = tactus from, pureValue = pureValue from}
+keepMeta from to = to {steps = steps from, pureValue = pureValue from}
 
-keepTactus :: Pattern a -> Pattern b -> Pattern b
-keepTactus from to = to {tactus = tactus from}
+keepSteps :: Pattern a -> Pattern b -> Pattern b
+keepSteps from to = to {steps = steps from}
 
 -- type StateMap = Map.Map String (Pattern Value)
 type ControlPattern = Pattern ValueMap
@@ -131,19 +128,19 @@
   -- > (⅓>½)-⅔|11
   -- > ⅓-(½>⅔)|12
   -- >   (⅔>1)|102
-  (<*>) a b = (applyPatToPatBoth a b) {tactus = (\a' b' -> lcmr <$> a' <*> b') <$> tactus a <*> tactus b}
+  (<*>) a b = (applyPatToPatBoth a b) {steps = (\a' b' -> lcmr a' b') <$> steps a <*> steps b}
 
 -- | Like @<*>@, but the "wholes" come from the left
 (<*) :: Pattern (a -> b) -> Pattern a -> Pattern b
-(<*) a b = keepTactus a $ applyPatToPatLeft a b
+(<*) a b = keepSteps a $ applyPatToPatLeft a b
 
 -- | Like @<*>@, but the "wholes" come from the right
 (*>) :: Pattern (a -> b) -> Pattern a -> Pattern b
-(*>) a b = keepTactus b $ applyPatToPatRight a b
+(*>) a b = keepSteps b $ applyPatToPatRight a b
 
 -- | Like @<*>@, but the "wholes" come from the left
 (<<*) :: Pattern (a -> b) -> Pattern a -> Pattern b
-(<<*) a b = (applyPatToPatSqueeze a b) {tactus = (*) <$> tactus a <*> tactus b}
+(<<*) a b = (applyPatToPatSqueeze a b) {steps = (*) <$> steps a <*> steps b}
 
 infixl 4 <*, *>, <<*
 
@@ -244,23 +241,19 @@
 
 -- | Turns a pattern of patterns into a single pattern. Like @unwrap@,
 -- but structure only comes from the inner pattern.
-innerJoin :: Pattern (Pattern a) -> Pattern a
-innerJoin pp = setTactus (Just $ innerJoin' $ filterJust $ tactus <$> pp) $ innerJoin' pp
+innerJoin :: Pattern (Pattern b) -> Pattern b
+innerJoin pp' = pp' {query = q, pureValue = Nothing}
   where
-    -- \| innerJoin but without tactus manipulation (to avoid recursion)
-    innerJoin' :: Pattern (Pattern b) -> Pattern b
-    innerJoin' pp' = pp' {query = q, pureValue = Nothing}
+    q st =
+      concatMap
+        (\(Event oc _ op v) -> mapMaybe (munge oc) $ query v st {arc = op})
+        (query pp' st)
       where
-        q st =
-          concatMap
-            (\(Event oc _ op v) -> mapMaybe (munge oc) $ query v st {arc = op})
-            (query pp' st)
-          where
-            munge oc (Event ic iw ip v) =
-              do
-                p <- subArc (arc st) ip
-                p' <- subArc p (arc st)
-                return (Event (combineContexts [ic, oc]) iw p' v)
+        munge oc (Event ic iw ip v) =
+          do
+            p <- subArc (arc st) ip
+            p' <- subArc p (arc st)
+            return (Event (combineContexts [ic, oc]) iw p' v)
 
 -- | Turns a pattern of patterns into a single pattern. Like @unwrap@,
 -- but structure only comes from the outer pattern.
@@ -282,7 +275,7 @@
 -- | Like @unwrap@, but cycles of the inner patterns are compressed to fit the
 -- timespan of the outer whole (or the original query if it's a continuous pattern?)
 -- TODO - what if a continuous pattern contains a discrete one, or vice-versa?
--- TODO - tactus
+-- TODO - steps
 squeezeJoin :: Pattern (Pattern a) -> Pattern a
 squeezeJoin pp = pp {query = q, pureValue = Nothing}
   where
@@ -567,13 +560,13 @@
 -- * Internal/fundamental functions
 
 empty :: Pattern a
-empty = Pattern {query = const [], tactus = Just 1, pureValue = Nothing}
+empty = Pattern {query = const [], steps = Just 1, pureValue = Nothing}
 
 silence :: Pattern a
 silence = empty
 
 nothing :: Pattern a
-nothing = empty {tactus = Just 0}
+nothing = empty {steps = Just 0}
 
 queryArc :: Pattern a -> Arc -> [Event a]
 queryArc p a = query p $ State a Map.empty
@@ -734,7 +727,7 @@
 _fast rate pat
   | rate == 0 = silence
   | rate < 0 = rev $ _fast (negate rate) pat
-  | otherwise = keepTactus pat $ withResultTime (/ rate) $ withQueryTime (* rate) pat
+  | otherwise = keepSteps pat $ withResultTime (/ rate) $ withQueryTime (* rate) pat
 
 -- | Slow down a pattern by the given time pattern.
 --
@@ -902,9 +895,9 @@
 patternify f (Pattern _ _ (Just a)) b = f a b
 patternify f pa p = innerJoin $ (`f` p) <$> pa
 
--- versions that preserve the tactus
+-- versions that preserve the steps
 patternify' :: (b -> Pattern c -> Pattern a) -> Pattern b -> Pattern c -> Pattern a
-patternify' f pa p = (patternify f pa p) {tactus = tactus p}
+patternify' f pa p = (patternify f pa p) {steps = steps p}
 
 patternify2 :: (a -> b -> c -> Pattern d) -> Pattern a -> Pattern b -> c -> Pattern d
 patternify2 f (Pattern _ _ (Just a)) (Pattern _ _ (Just b)) c = f a b c
@@ -918,7 +911,7 @@
 patternify3 f a b c p = innerJoin $ (\x y z -> f x y z p) <$> a <*> b <*> c
 
 patternify3' :: (a -> b -> c -> Pattern d -> Pattern e) -> (Pattern a -> Pattern b -> Pattern c -> Pattern d -> Pattern e)
-patternify3' f a b c p = keepTactus p $ patternify3 f a b c p
+patternify3' f a b c p = keepSteps p $ patternify3 f a b c p
 
 patternifySqueeze :: (a -> Pattern b -> Pattern c) -> (Pattern a -> Pattern b -> Pattern c)
 patternifySqueeze f tv p = squeezeJoin $ (`f` p) <$> tv
diff --git a/src/Sound/Tidal/Scales.hs b/src/Sound/Tidal/Scales.hs
--- a/src/Sound/Tidal/Scales.hs
+++ b/src/Sound/Tidal/Scales.hs
@@ -308,7 +308,7 @@
       noteInScale (fromMaybe [0] $ lookup scaleName table) n
   )
     <$> p
-    <* sp
+      <* sp
   where
     octave s x = x `div` length s
     noteInScale s x = (s !!! x) + fromIntegral (12 * octave s x)
@@ -337,7 +337,7 @@
       noteInScale (uniq $ f $ fromMaybe [0] $ lookup scaleName table) n
   )
     <$> p
-    <* sp
+      <* sp
   where
     octave s x = x `div` length s
     noteInScale s x = (s !!! x) + fromIntegral (12 * octave s x)
diff --git a/src/Sound/Tidal/Simple.hs b/src/Sound/Tidal/Simple.hs
--- a/src/Sound/Tidal/Simple.hs
+++ b/src/Sound/Tidal/Simple.hs
@@ -21,7 +21,7 @@
 
 module Sound.Tidal.Simple where
 
-import GHC.Exts (IsString (..))
+import Data.String (IsString (..))
 import Sound.Tidal.Control (chop, hurry)
 import Sound.Tidal.Core ((#), (<~), (|*))
 import Sound.Tidal.Params (crush, gain, pan, s, speed)
diff --git a/src/Sound/Tidal/Stepwise.hs b/src/Sound/Tidal/Stepwise.hs
--- a/src/Sound/Tidal/Stepwise.hs
+++ b/src/Sound/Tidal/Stepwise.hs
@@ -1,5 +1,5 @@
 {-
-    Tactus.hs - Functions that deal with stepwise manipulation of pattern
+    Stepwise.hs - Functions that deal with stepwise manipulation of pattern
     Copyright (C) 2024, Alex McLean and contributors
 
     This library is free software: you can redistribute it and/or modify
@@ -18,14 +18,14 @@
 
 module Sound.Tidal.Stepwise where
 
-import Data.List (sort, sortOn)
-import Data.Maybe (fromJust, isJust, mapMaybe)
-import Sound.Tidal.Core (stack, timecat, zoompat)
+import Data.List (sort, sortOn, transpose)
+import Data.Maybe (catMaybes, fromJust, fromMaybe, isJust, mapMaybe)
+import Sound.Tidal.Core (stack, timecat, zoom, zoompat)
 import Sound.Tidal.Pattern
 import Sound.Tidal.Utils (enumerate, nubOrd, pairs)
 
--- _lcmtactus :: [Pattern a] -> Maybe Time
--- _lcmtactus pats = foldl1 lcmr <$> (sequence $ map tactus pats)
+-- _lcmsteps :: [Pattern a] -> Maybe Time
+-- _lcmsteps pats = foldl1 lcmr <$> (sequence $ map steps pats)
 
 s_patternify :: (a -> Pattern b -> Pattern c) -> (Pattern a -> Pattern b -> Pattern c)
 s_patternify f (Pattern _ _ (Just a)) b = f a b
@@ -34,35 +34,21 @@
 s_patternify2 :: (a -> b -> c -> Pattern d) -> Pattern a -> Pattern b -> c -> Pattern d
 s_patternify2 f a b p = stepJoin $ (\x y -> f x y p) <$> a <*> b
 
--- Breaks up pattern of patterns at event boundaries, then timecats them all together
 stepJoin :: Pattern (Pattern a) -> Pattern a
-stepJoin pp = splitQueries $ Pattern q t Nothing
+stepJoin pp = Pattern q first_t Nothing
   where
-    q st@(State a _) =
-      query
-        ( stepcat $
-            retime $
-              slices $
-                query (rotL (sam $ start a) pp) (st {arc = Arc 0 1})
-        )
-        st
-    -- TODO what's the tactus of the tactus and does it matter?
-    t :: Maybe (Pattern Rational)
-    t = Just $ Pattern t_q Nothing Nothing
-    t_q :: State -> [Event Rational]
-    t_q st@(State a' _) = maybe [] (`query` st) (tactus (stepcat $ retime $ slices $ query (rotL (sam $ start a') pp) (st {arc = Arc 0 1})))
-    -- retime each pattern slice
-    retime :: [(Time, Pattern a)] -> [Pattern a]
+    q st@(State a c) = query (timecat $ retime $ slices $ query (rotL (sam $ start a) pp) (st {arc = Arc 0 1})) st
+    first_t :: Maybe Rational
+    first_t = steps $ timecat $ retime $ slices $ queryArc pp (Arc 0 1)
+    retime :: [(Time, Pattern a)] -> [(Time, Pattern a)]
     retime xs = map (uncurry adjust) xs
       where
-        occupied_perc = sum $ map fst $ filter (isJust . tactus . snd) xs
-        occupied_tactus = sum $ mapMaybe (tactus . snd) xs
-        total_tactus = (/ occupied_perc) <$> occupied_tactus
-        adjust _ pat@(Pattern {tactus = Just _}) = pat
-        adjust dur pat = setTactus (Just $ (* dur) <$> total_tactus) pat
-    -- break up events at all start/end points, into groups
-    -- stacked into single patterns, with duration. Some patterns
-    -- will be have no events.
+        occupied_perc = sum $ map fst $ filter (isJust . steps . snd) xs
+        occupied_tactus = sum $ mapMaybe (steps . snd) xs
+        total_tactus = occupied_tactus / occupied_perc
+        adjust _ pat@(Pattern {steps = Just t}) = (t, pat)
+        adjust dur pat = (dur * total_tactus, pat)
+    -- break up events at all start/end points, into groups, including empty ones.
     slices :: [Event (Pattern a)] -> [(Time, Pattern a)]
     slices evs = map (\s -> (snd s - fst s, stack $ map (\x -> withContext (\c -> combineContexts [c, context x]) $ value x) $ fit s evs)) $ pairs $ sort $ nubOrd $ 0 : 1 : concatMap (\ev -> start (part ev) : stop (part ev) : []) evs
     -- list of slices of events within the given range
@@ -74,44 +60,47 @@
       a <- subArc (Arc b e) $ part ev
       return ev {part = a}
 
+-- stepcat :: [Pattern a] -> Pattern a
+-- stepcat pats = innerJoin $ timecat . map snd . sortOn fst <$> tpat (epats pats)
+--   where
+--     -- enumerated patterns, ignoring those without steps
+--     epats :: [Pattern a] -> [(Int, Pattern a)]
+--     epats = enumerate . filter (isJust . steps)
+--     --
+--     tpat :: [(Int, Pattern a)] -> Pattern [(Int, (Time, Pattern a))]
+--     tpat = mapM (\(i, pat) -> (\t -> (i, (t, pat))) <$> fromJust (steps pat))
+
 stepcat :: [Pattern a] -> Pattern a
-stepcat pats = innerJoin $ (timecat . map snd . sortOn fst) <$> (tpat $ epats pats)
-  where
-    -- enumerated patterns, ignoring those without tactus
-    epats :: [Pattern a] -> [(Int, Pattern a)]
-    epats = enumerate . filter (isJust . tactus)
-    --
-    tpat :: [(Int, Pattern a)] -> Pattern [(Int, (Time, Pattern a))]
-    tpat = mapM (\(i, pat) -> (\t -> (i, (t, pat))) <$> fromJust (tactus pat))
+stepcat pats = timecat $ map (\pat -> (fromMaybe 1 $ steps pat, pat)) pats
 
-_steptake :: Time -> Pattern a -> Pattern a
+_take :: Time -> Pattern a -> Pattern a
 -- raise error?
-_steptake _ pat@(Pattern _ Nothing _) = pat
-_steptake n pat@(Pattern _ (Just tpat) _) = setTactus (Just tpat') $ zoompat b e pat
+_take _ pat@(Pattern _ Nothing _) = pat
+_take n pat@(Pattern _ (Just t) _) = setSteps (Just t') $ zoom (b, e) pat
   where
-    b = (\t -> if n >= 0 then 0 else 1 - ((abs n) / t)) <$> tpat
-    e = (\t -> if n >= 0 then n / t else 1) <$> tpat
-    tpat' = (\t -> min (abs n) t) <$> tpat
+    b = if n >= 0 then 0 else 1 - (abs n / t)
+    e = if n >= 0 then n / t else 1
+    t' = min (abs n) t
 
 steptake :: Pattern Time -> Pattern a -> Pattern a
-steptake = s_patternify _steptake
+steptake = s_patternify _take
 
 _stepdrop :: Time -> Pattern a -> Pattern a
 _stepdrop _ pat@(Pattern _ Nothing _) = pat
-_stepdrop n pat@(Pattern _ (Just tpat) _) = steptake (f <$> tpat) pat
+_stepdrop n pat@(Pattern _ (Just t) _) = steptake (pure $ f t) pat
   where
     f t
       | n >= 0 = t - n
-      | otherwise = 0 - (t + n)
+      | otherwise = negate (t + n)
 
 stepdrop :: Pattern Time -> Pattern a -> Pattern a
 stepdrop = s_patternify _stepdrop
 
 _expand :: Rational -> Pattern a -> Pattern a
-_expand factor pat = withTactus (* factor) pat
+_expand factor pat = withSteps (* factor) pat
 
 _contract :: Rational -> Pattern a -> Pattern a
-_contract factor pat = withTactus (/ factor) pat
+_contract factor pat = withSteps (/ factor) pat
 
 expand :: Pattern Rational -> Pattern a -> Pattern a
 expand = s_patternify _expand
@@ -119,6 +108,18 @@
 contract :: Pattern Rational -> Pattern a -> Pattern a
 contract = s_patternify _contract
 
+_extend :: Rational -> Pattern a -> Pattern a
+_extend factor pat = _expand factor $ _fast factor pat
+
+extend :: Pattern Rational -> Pattern a -> Pattern a
+extend = s_patternify _extend
+
+-- | Successively plays a pattern from each group in turn
+stepalt :: [[Pattern a]] -> Pattern a
+stepalt groups = stepcat $ concat $ take (fromIntegral $ c * length groups) $ transpose $ map cycle groups
+  where
+    c = foldl1 lcm $ map length groups
+
 {-
 s_while :: Pattern Bool -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 s_while patb f pat@(Pattern _ (Just t) _) = while (_steps t patb) f pat
@@ -131,7 +132,7 @@
   | otherwise = applyWhen stepwise (_fast t) $ s_cat $ applyWhen lastone reverse $ (f $ head cycles) : tail cycles
   where
     cycles = applyWhen lastone reverse $ separateCycles n $ applyWhen stepwise (_slow t) pat
-    t = fromMaybe 1 $ tactus pat
+    t = fromMaybe 1 $ steps pat
 
 s_nthcycle :: Pattern Int -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 s_nthcycle (Pattern _ _ (Just i)) f pat = _s_nth True False i f pat
@@ -186,11 +187,4 @@
 -- | Plays one fewer step from the pattern each repetition, down to nothing
 s_taperBy :: Pattern Int -> Pattern Int -> Pattern a -> Pattern a
 s_taperBy = s_patternify2 _s_taperBy
-
--- | Successively plays a pattern from each group in turn
-s_alt :: [[Pattern a]] -> Pattern a
-s_alt groups = s_cat $ concat $ take (c * length groups) $ transpose $ map cycle groups
-  where
-    c = foldl1 lcm $ map length groups
-
 -}
diff --git a/src/Sound/Tidal/UI.hs b/src/Sound/Tidal/UI.hs
--- a/src/Sound/Tidal/UI.hs
+++ b/src/Sound/Tidal/UI.hs
@@ -1,6 +1,5 @@
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE TypeSynonymInstances #-}
 
 {-
     UI.hs - Tidal's main 'user interface' functions, for transforming
@@ -36,9 +35,9 @@
 import Data.Bool (bool)
 import Data.Char (digitToInt, isDigit, ord)
 import Data.Fixed (mod')
+import qualified Data.IntMap.Strict as IM
 import Data.List
   ( elemIndex,
-    findIndex,
     findIndices,
     groupBy,
     intercalate,
@@ -46,10 +45,10 @@
     sortOn,
     transpose,
   )
+import qualified Data.List as L
 import qualified Data.Map.Strict as Map
 import Data.Maybe
-  ( catMaybes,
-    fromJust,
+  ( fromJust,
     fromMaybe,
     isJust,
     mapMaybe,
@@ -97,7 +96,9 @@
 intSeedToRand = (/ 536870912) . realToFrac . (`mod` 536870912)
 
 timeToRand :: (RealFrac a, Fractional b) => a -> b
-timeToRand = intSeedToRand . timeToIntSeed
+-- Otherwise the value would be 0 at time 0.. Let's start in the middle.
+timeToRand 0 = 0.5
+timeToRand t = intSeedToRand $ timeToIntSeed t
 
 timeToRands :: (RealFrac a, Fractional b) => a -> Int -> [b]
 timeToRands 0 n = timeToRands' (timeToIntSeed (9999999 :: Double)) n
@@ -139,7 +140,7 @@
 --
 -- > jux (# ((1024 <~) $ gain rand)) $ sound "sn sn ~ sn" # gain rand
 rand :: (Fractional a) => Pattern a
-rand = pattern (\(State a@(Arc s e) _) -> [Event (Context []) Nothing a (realToFrac $ (timeToRand ((e + s) / 2) :: Double))])
+rand = pattern (\(State a@(Arc s _) _) -> [Event (Context []) Nothing a (realToFrac (timeToRand s :: Double))])
 
 -- | Boolean rand - a continuous stream of true\/false values, with a 50\/50 chance.
 brand :: Pattern Bool
@@ -175,7 +176,7 @@
 --
 -- The `perlin` function uses the cycle count as input and can be used much like @rand@.
 perlinWith :: (Fractional a) => Pattern Double -> Pattern a
-perlinWith p = fmap realToFrac $ (interp) <$> (p - pa) <*> (timeToRand <$> pa) <*> (timeToRand <$> pb)
+perlinWith p = fmap realToFrac $ interp <$> (p - pa) <*> (timeToRand <$> pa) <*> (timeToRand <$> pb)
   where
     pa = (fromIntegral :: Int -> Double) . floor <$> p
     pb = (fromIntegral :: Int -> Double) . (+ 1) . floor <$> p
@@ -293,19 +294,19 @@
 -- play as the "e" note, and half as likely to play as the "g" note.
 --
 -- > wchoose = 'wchooseBy' 'rand'
-wchoose :: [(a, Double)] -> Pattern a
+wchoose :: [(a, Pattern Double)] -> Pattern a
 wchoose = wchooseBy rand
 
 -- | Given a pattern of probabilities and a list of @(value, weight)@ pairs,
 -- @wchooseBy@ creates a @'Pattern' value@ by choosing values based on those
 -- probabilities and weighted appropriately by the weights in the list of pairs.
-wchooseBy :: Pattern Double -> [(a, Double)] -> Pattern a
-wchooseBy pat pairs = match <$> pat
+wchooseBy :: Pattern Double -> [(a, Pattern Double)] -> Pattern a
+wchooseBy pat pairs = match <$> pat <* cweightpat <* totalpat
   where
-    match r = values !! head (findIndices (> (r * total)) cweights)
-    cweights = scanl1 (+) (map snd pairs)
+    match r cweights total = values !! head (findIndices (> (r * total)) cweights)
+    cweightpat = sequence $ scanl1 (+) (map snd pairs)
     values = map fst pairs
-    total = sum $ map snd pairs
+    totalpat = sum $ map snd pairs
 
 -- | @randcat ps@: does a @slowcat@ on the list of patterns @ps@ but
 --  randomises the order in which they are played.
@@ -321,7 +322,7 @@
 --  > d1 $ sound
 --  >    $ wrandcat
 --  >        [ ("bd*2 sn", 5), ("jvbass*3", 2), ("drum*2", 2), ("ht mt", 1) ]
-wrandcat :: [(Pattern a, Double)] -> Pattern a
+wrandcat :: [(Pattern a, Pattern Double)] -> Pattern a
 wrandcat ps = unwrap $ wchooseBy (segment 1 rand) ps
 
 -- | @degrade@ randomly removes events from a pattern 50% of the time:
@@ -365,7 +366,7 @@
 
 -- Useful for manipulating random stream, e.g. to change 'seed'
 _degradeByUsing :: Pattern Double -> Double -> Pattern a -> Pattern a
-_degradeByUsing prand x p = fmap fst $ filterValues ((> x) . snd) $ (,) <$> p <* prand
+_degradeByUsing prand x p = fmap fst $ filterValues ((>= x) . snd) $ (,) <$> p <* prand
 
 -- |
 -- As 'degradeBy', but the pattern of probabilities represents the chances to retain rather
@@ -374,7 +375,7 @@
 unDegradeBy = patternify' _unDegradeBy
 
 _unDegradeBy :: Double -> Pattern a -> Pattern a
-_unDegradeBy x p = fmap fst $ filterValues ((<= x) . snd) $ (,) <$> p <* rand
+_unDegradeBy x p = fmap fst $ filterValues ((< x) . snd) $ (,) <$> p <* rand
 
 degradeOverBy :: Int -> Pattern Double -> Pattern a -> Pattern a
 degradeOverBy i tx p = unwrap $ (\x -> fmap fst $ filterValues ((> x) . snd) $ (,) <$> p <* fastRepeatCycles i rand) <$> slow (fromIntegral i) tx
@@ -455,10 +456,8 @@
 
 -- |
 -- Never apply a transformation, returning the pattern unmodified.
---
--- @never = flip const@
 never :: (Pattern a -> Pattern a) -> Pattern a -> Pattern a
-never = flip const
+never _ x = x
 
 -- |
 -- Apply the transformation to the pattern unconditionally.
@@ -536,7 +535,7 @@
 --
 -- There is also `iter'`, which shifts the pattern in the opposite direction.
 iter :: Pattern Int -> Pattern c -> Pattern c
-iter a pat = keepTactus pat $ patternify' _iter a pat
+iter a pat = keepSteps pat $ patternify' _iter a pat
 
 _iter :: Int -> Pattern a -> Pattern a
 _iter n p = slowcat $ map (\i -> (fromIntegral i % fromIntegral n) `rotL` p) [0 .. (n - 1)]
@@ -720,7 +719,7 @@
 whenmod a b f pat = innerJoin $ (\a' b' -> _whenmod a' b' f pat) <$> a <*> b
 
 _whenmod :: Time -> Time -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a
-_whenmod a b = whenT (\t -> ((t `mod'` a) >= b))
+_whenmod a b = whenT (\t -> (t `mod'` a) >= b)
 
 -- |
 -- > superimpose f p = stack [p, f p]
@@ -927,8 +926,8 @@
 
 _euclid :: Int -> Int -> Pattern a -> Pattern a
 _euclid n k a
-  | n >= 0 = fastcat $ fmap (bool silence a) $ bjorklund (n, k)
-  | otherwise = fastcat $ fmap (bool a silence) $ bjorklund (-n, k)
+  | n >= 0 = setSteps (Just $ toRational k) $ fastcat $ fmap (bool silence a) $ bjorklund (n, k)
+  | otherwise = setSteps (Just $ toRational k) $ fastcat $ fmap (bool a silence) $ bjorklund (-n, k)
 
 -- |
 --
@@ -941,16 +940,16 @@
 --
 -- > d1 $ euclidFull 3 7 "realclaps" ("realclaps" # gain 0.8)
 euclidFull :: Pattern Int -> Pattern Int -> Pattern a -> Pattern a -> Pattern a
-euclidFull n k pa pb = stack [euclid n k pa, euclidInv n k pb]
+euclidFull n k pa pb = setSteps (toRational <$> pureValue k) $ stack [euclid n k pa, euclidInv n k pb]
 
 -- | Less expressive than 'euclid' due to its constrained types, but may be more efficient.
 _euclidBool :: Int -> Int -> Pattern Bool -- TODO: add 'euclidBool'?
 _euclidBool n k
-  | n >= 0 = fastFromList $ bjorklund (n, k)
-  | otherwise = fastFromList $ fmap (not) $ bjorklund (-n, k)
+  | n >= 0 = setSteps (Just $ toRational k) $ fastFromList $ bjorklund (n, k)
+  | otherwise = setSteps (Just $ toRational k) $ fastFromList $ fmap not $ bjorklund (-n, k)
 
 _euclid' :: Int -> Int -> Pattern a -> Pattern a
-_euclid' n k p = fastcat $ map (\x -> if x then p else silence) (bjorklund (n, k))
+_euclid' n k p = setSteps (Just $ toRational k) $ fastcat $ map (\x -> if x then p else silence) (bjorklund (n, k))
 
 -- |
 -- As 'euclid', but taking a third rotational parameter corresponding to the onset
@@ -964,7 +963,7 @@
 
 _euclidOff :: Int -> Int -> Int -> Pattern a -> Pattern a
 _euclidOff _ 0 _ _ = silence
-_euclidOff n k s p = (rotL $ fromIntegral s % fromIntegral k) (_euclid n k p)
+_euclidOff n k s p = setSteps (Just $ toRational k) $ (rotL $ fromIntegral s % fromIntegral k) (_euclid n k p)
 
 -- | As 'euclidOff', but specialized to 'Bool'. May be more efficient than 'euclidOff'.
 euclidOffBool :: Pattern Int -> Pattern Int -> Pattern Int -> Pattern Bool -> Pattern Bool
@@ -972,7 +971,7 @@
 
 _euclidOffBool :: Int -> Int -> Int -> Pattern Bool -> Pattern Bool
 _euclidOffBool _ 0 _ _ = silence
-_euclidOffBool n k s p = ((fromIntegral s % fromIntegral k) `rotL`) ((\a b -> if b then a else not a) <$> _euclidBool n k <*> p)
+_euclidOffBool n k s p = setSteps (Just $ toRational k) $ rotL (fromIntegral s % fromIntegral k) ((\a b -> if b then a else not a) <$> _euclidBool n k <*> p)
 
 distrib :: [Pattern Int] -> Pattern a -> Pattern a
 distrib ps p = do
@@ -1185,7 +1184,7 @@
 segment = patternify _segment
 
 _segment :: Time -> Pattern a -> Pattern a
-_segment n p = setTactus (Just $ pure n) $ _fast n (pure id) <* p
+_segment n p = setSteps (Just $ n) $ _fast n (pure id) <* p
 
 -- | @discretise@: the old (deprecated) name for 'segment'
 discretise :: Pattern Time -> Pattern a -> Pattern a
@@ -1237,7 +1236,7 @@
 -- @"arpy:1"@, @"casio"@ and @"bd"@, giving the pattern
 -- @"arpy:1 [~ casio] bd casio"@ (note that the list wraps round here).
 fit :: Pattern Int -> [a] -> Pattern Int -> Pattern a
-fit pint xs p = (patternify func) pint (xs, p)
+fit pint xs p = patternify func pint (xs, p)
   where
     func i (xs', p') = _fit i xs' p'
 
@@ -1247,7 +1246,7 @@
     pos e = perCycle * floor (start $ part e)
 
 permstep :: (RealFrac b) => Int -> [a] -> Pattern b -> Pattern a
-permstep nSteps things p = unwrap $ (\n -> fastFromList $ concatMap (\x -> replicate (fst x) (snd x)) $ zip (ps !! floor (n * fromIntegral (length ps - 1))) things) <$> _segment 1 p
+permstep nSteps things p = unwrap $ (\n -> fastFromList $ concatMap (uncurry replicate) $ zip (ps !! floor (n * fromIntegral (length ps - 1))) things) <$> _segment 1 p
   where
     ps = permsort (length things) nSteps
     deviance avg xs = sum $ map (abs . (avg -) . fromIntegral) xs
@@ -1429,12 +1428,16 @@
 -- transition probability from state 0->0 is 2/5, 0->1 is 3/5, 1->0 is 1/4, and
 -- 1->1 is 3/4.
 runMarkov :: Int -> [[Double]] -> Int -> Time -> [Int]
-runMarkov n tp xi seed = reverse $ (iterate (markovStep $ renorm) [xi]) !! (n - 1)
+runMarkov n tp xi seed = take n $ map fst $ L.iterate' (markovStep $ renorm) (xi, seed + delta)
   where
-    markovStep tp' xs = (fromJust $ findIndex (r <=) $ scanl1 (+) (tp' !! (head xs))) : xs
+    markovStep tp' (x, seed') = (let v = tp' IM.! x in findIndex (r * fst (Map.findMax v)) v, seed' + delta)
       where
-        r = timeToRand $ seed + (fromIntegral . length) xs / fromIntegral n
-    renorm = [map (/ sum x) x | x <- tp]
+        r = timeToRand seed'
+    renorm :: IM.IntMap (Map.Map Double Int)
+    renorm = IM.fromList $ zip [0 ..] [Map.fromList $ zip (tail $ scanl (+) 0 x) [0 ..] | x <- tp]
+    findIndex :: Double -> Map.Map Double Int -> Int
+    findIndex x v = maybe 0 snd (Map.lookupGE x v)
+    delta = 1 / fromIntegral n
 
 -- | @markovPat n xi tp@ generates a one-cycle pattern of @n@ steps in a Markov
 -- chain starting from state @xi@ with transition matrix @tp@. Each row of the
@@ -1454,7 +1457,7 @@
 
 _markovPat :: Int -> Int -> [[Double]] -> Pattern Int
 _markovPat n xi tp =
-  setTactus (Just $ pure $ toRational n) $
+  setSteps (Just $ toRational n) $
     splitQueries $
       pattern
         ( \(State a@(Arc s _) _) ->
@@ -2220,8 +2223,9 @@
 sew :: Pattern Bool -> Pattern a -> Pattern a -> Pattern a
 -- Replaced with more efficient version below
 -- sew pb a b = overlay (mask pb a) (mask (inv pb) b)
-sew pb a b = Pattern pf Nothing Nothing
+sew pb a b = Pattern pf psteps Nothing
   where
+    psteps = pureValue pb >>= \v -> if v then steps a else steps b
     pf st = concatMap match evs
       where
         evs = query pb st
@@ -2230,7 +2234,7 @@
         match ev
           | value ev = find (query a st {arc = subarc}) ev
           | otherwise = find (query b st {arc = subarc}) ev
-        find evs' ev = catMaybes $ map (check ev) evs'
+        find evs' ev = mapMaybe (check ev) evs'
         check bev xev = do
           newarc <- subArc (part bev) (part xev)
           return $ xev {part = newarc}
@@ -2251,7 +2255,7 @@
 -- value is active. No events are let through where no binary values
 -- are active.
 while :: Pattern Bool -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a
-while b f pat = keepTactus pat $ sew b (f pat) pat
+while b f pat = sew b (f pat) pat
 
 -- |
 -- @stutter n t pat@ repeats each event in @pat@ @n@ times, separated by @t@ time (in fractions of a cycle).
@@ -2348,8 +2352,8 @@
   (Pattern ValueMap -> Pattern ValueMap) ->
   Pattern ValueMap ->
   Pattern ValueMap
--- TODO: lcm tactus of p and f p?
-juxBy n f p = keepTactus p $ stack [p |+ P.pan 0.5 |- P.pan (n / 2), f $ p |+ P.pan 0.5 |+ P.pan (n / 2)]
+-- TODO: lcm steps of p and f p?
+juxBy n f p = keepSteps p $ stack [p |+ P.pan 0.5 |- P.pan (n / 2), f $ p |+ P.pan 0.5 |+ P.pan (n / 2)]
 
 -- |
 -- Given a sample's directory name and number, this generates a string
@@ -2428,7 +2432,7 @@
 -- > d1 $ jux (iter 4) $ sound "arpy arpy:2*2"
 -- >   |+ speed (slow 4 $ sine1 * 0.5 + 1)
 range :: (Num a) => Pattern a -> Pattern a -> Pattern a -> Pattern a
-range fromP toP p = (\from to v -> ((v * (to - from)) + from)) <$> fromP *> toP *> p
+range fromP toP p = (\from to v -> (v * (to - from)) + from) <$> fromP *> toP *> p
 
 _range :: (Functor f, Num b) => b -> b -> f b -> f b
 _range from to p = (+ from) . (* (to - from)) <$> p
@@ -2891,7 +2895,7 @@
 _chew :: Int -> Pattern Int -> ControlPattern -> ControlPattern
 _chew n ipat pat = squeezeJoinUp (zoomslice <$> ipat) |/ P.speed (pure $ fromIntegral n)
   where
-    zoomslice i = zoom (i' / (fromIntegral n), (i' + 1) / (fromIntegral n)) (pat)
+    zoomslice i = zoom (i' / fromIntegral n, (i' + 1) / fromIntegral n) pat
       where
         i' = fromIntegral $ i `mod` n
 
@@ -2913,7 +2917,7 @@
 _binary n num = listToPat $ __binary n num
 
 _binaryN :: Int -> Pattern Int -> Pattern Bool
-_binaryN n p = setTactus (Just $ pure $ toRational n) $ squeezeJoin $ _binary n <$> p
+_binaryN n p = setSteps (Just $ toRational n) $ squeezeJoin $ _binary n <$> p
 
 binaryN :: Pattern Int -> Pattern Int -> Pattern Bool
 binaryN n p = patternify _binaryN n p
diff --git a/test/Sound/Tidal/ChordsTest.hs b/test/Sound/Tidal/ChordsTest.hs
--- a/test/Sound/Tidal/ChordsTest.hs
+++ b/test/Sound/Tidal/ChordsTest.hs
@@ -3,11 +3,11 @@
 module Sound.Tidal.ChordsTest where
 
 import Sound.Tidal.Pattern
-import Test.Microspec
+import Test.Hspec
 import TestUtils
 import Prelude hiding ((*>), (<*))
 
-run :: Microspec ()
+run :: Spec
 run =
   describe "Sound.Tidal.Chords" $ do
     describe "chord" $ do
diff --git a/test/Sound/Tidal/ControlTest.hs b/test/Sound/Tidal/ControlTest.hs
--- a/test/Sound/Tidal/ControlTest.hs
+++ b/test/Sound/Tidal/ControlTest.hs
@@ -6,11 +6,11 @@
 import Sound.Tidal.Core
 import Sound.Tidal.Params
 import Sound.Tidal.Pattern
-import Test.Microspec
+import Test.Hspec
 import TestUtils
 import Prelude hiding ((*>), (<*))
 
-run :: Microspec ()
+run :: Spec
 run =
   describe "Sound.Tidal.Control" $ do
     describe "echo" $ do
diff --git a/test/Sound/Tidal/CoreTest.hs b/test/Sound/Tidal/CoreTest.hs
--- a/test/Sound/Tidal/CoreTest.hs
+++ b/test/Sound/Tidal/CoreTest.hs
@@ -5,21 +5,15 @@
 import Data.List (sort)
 import qualified Data.Map as Map
 import Data.Ratio
-import Sound.Tidal.Control as C
 import Sound.Tidal.Core as C
-import Sound.Tidal.Params as C
-import Sound.Tidal.ParseBP as C
 import Sound.Tidal.Pattern as C
-import Sound.Tidal.Scales as C
-import Sound.Tidal.Show as C
-import Sound.Tidal.Simple as C
-import Sound.Tidal.Stepwise as C
 import Sound.Tidal.UI as C
-import Test.Microspec
+import Test.Hspec
+import Test.Hspec.QuickCheck
 import TestUtils
 import Prelude hiding ((*>), (<*))
 
-run :: Microspec ()
+run :: Spec
 run =
   describe "Sound.Tidal.Core" $ do
     describe "Elemental patterns" $ do
@@ -29,23 +23,23 @@
         let inNormalRange pat t = (y >= 0) && (y <= 1)
               where
                 y = sampleOf pat t
-        it "sine" $ inNormalRange sine
-        it "cosine" $ inNormalRange cosine
-        it "saw" $ inNormalRange saw
-        it "isaw" $ inNormalRange isaw
-        it "tri" $ inNormalRange tri
-        it "square" $ inNormalRange square
+        prop "sine" $ inNormalRange sine
+        prop "cosine" $ inNormalRange cosine
+        prop "saw" $ inNormalRange saw
+        prop "isaw" $ inNormalRange isaw
+        prop "tri" $ inNormalRange tri
+        prop "square" $ inNormalRange square
       describe "have correctly-scaled bipolar variants" $ do
         let areCorrectlyScaled pat pat2 t = (y * 2 - 1) ~== y2
               where
                 y = sampleOf pat t
                 y2 = sampleOf pat2 t
-        it "sine" $ areCorrectlyScaled sine sine2
-        it "cosine" $ areCorrectlyScaled cosine cosine2
-        it "saw" $ areCorrectlyScaled saw saw2
-        it "isaw" $ areCorrectlyScaled isaw isaw2
-        it "tri" $ areCorrectlyScaled tri tri2
-        it "square" $ areCorrectlyScaled square square2
+        prop "sine" $ areCorrectlyScaled sine sine2
+        prop "cosine" $ areCorrectlyScaled cosine cosine2
+        prop "saw" $ areCorrectlyScaled saw saw2
+        prop "isaw" $ areCorrectlyScaled isaw isaw2
+        prop "tri" $ areCorrectlyScaled tri tri2
+        prop "square" $ areCorrectlyScaled square square2
 
     describe "append" $
       it "can switch between the cycles from two pures" $ do
@@ -80,30 +74,28 @@
               (cat [rev a, rev b, rev c])
 
     describe "fastCat" $ do
-      it "can switch between the cycles from three pures inside one cycle" $ do
-        it "1" $
-          queryArc (fastCat [pure "a", pure "b", pure "c"]) (Arc 0 1)
-            `shouldBe` fmap
-              toEvent
-              [ (((0, 1 / 3), (0, 1 / 3)), "a" :: String),
-                (((1 / 3, 2 / 3), (1 / 3, 2 / 3)), "b"),
-                (((2 / 3, 1), (2 / 3, 1)), "c")
-              ]
-        it "5/3" $
-          queryArc (fastCat [pure "a", pure "b", pure "c"]) (Arc 0 (5 / 3))
-            `shouldBe` fmap
-              toEvent
-              [ (((0, 1 / 3), (0, 1 / 3)), "a" :: String),
-                (((1 / 3, 2 / 3), (1 / 3, 2 / 3)), "b"),
-                (((2 / 3, 1), (2 / 3, 1)), "c"),
-                (((1, 4 / 3), (1, 4 / 3)), "a"),
-                (((4 / 3, 5 / 3), (4 / 3, 5 / 3)), "b")
-              ]
-      it "works with zero-length queries" $ do
-        it "0" $
+      it "can switch between the cycles from three pures inside one cycle (1)" $ do
+        queryArc (fastCat [pure "a", pure "b", pure "c"]) (Arc 0 1)
+          `shouldBe` fmap
+            toEvent
+            [ (((0, 1 / 3), (0, 1 / 3)), "a" :: String),
+              (((1 / 3, 2 / 3), (1 / 3, 2 / 3)), "b"),
+              (((2 / 3, 1), (2 / 3, 1)), "c")
+            ]
+      it "can switch between the cycles from three pures inside one cycle (5/3)" $ do
+        queryArc (fastCat [pure "a", pure "b", pure "c"]) (Arc 0 (5 / 3))
+          `shouldBe` fmap
+            toEvent
+            [ (((0, 1 / 3), (0, 1 / 3)), "a" :: String),
+              (((1 / 3, 2 / 3), (1 / 3, 2 / 3)), "b"),
+              (((2 / 3, 1), (2 / 3, 1)), "c"),
+              (((1, 4 / 3), (1, 4 / 3)), "a"),
+              (((4 / 3, 5 / 3), (4 / 3, 5 / 3)), "b")
+            ]
+      it "works with zero-length queries (0)" $ do
           queryArc (fastCat [pure "a", pure "b"]) (Arc 0 0)
             `shouldBe` fmap toEvent [(((0, 0.5), (0, 0)), "a" :: String)]
-        it "1/3" $
+      it "works with zero-length queries (1/3)" $ do
           queryArc (fastCat [pure "a", pure "b"]) (Arc (1 % 3) (1 % 3))
             `shouldBe` fmap toEvent [(((0, 0.5), (1 % 3, 1 % 3)), "a" :: String)]
 
@@ -123,17 +115,16 @@
           b = "4 [5, 5] 6 7" :: Pattern Int
           c = "7 8 9 10" :: Pattern Int
           d = "7 [8, 9] 10 11" :: Pattern Int
-      it "creates silence when" $ do
-        it "first argument silent" $
-          comparePD
-            (Arc 0 1)
-            (silence |>| a)
-            silence
-        it "second argument silent" $
-          comparePD
-            (Arc 0 1)
-            (a |>| silence)
-            silence
+      it "creates silence when first argument silent" $
+        comparePD
+          (Arc 0 1)
+          (silence |>| a)
+          silence
+      it "creates silence when second argument silent" $
+        comparePD
+          (Arc 0 1)
+          (a |>| silence)
+          silence
       it "creates the same pattern when left argument has the same structure" $
         comparePD
           (Arc 0 1)
@@ -193,22 +184,21 @@
           (Arc 0 1)
           (fast 1 x)
           x
-      it "mutes, when there is" $ do
-        it "silence in first argument" $
-          comparePD
-            (Arc 0 1)
-            (fast silence x)
-            silence
-        it "silence in second argument" $
-          comparePD
-            (Arc 0 1)
-            (fast x silence :: Pattern Time)
-            silence
-        it "speedup by 0" $
-          comparePD
-            (Arc 0 1)
-            (fast 0 x)
-            silence
+      it "it mutes, when there is silence in first argument" $
+        comparePD
+          (Arc 0 1)
+          (fast silence x)
+          silence
+      it "it mute, when there is silence in second argument" $
+        comparePD
+          (Arc 0 1)
+          (fast x silence :: Pattern Time)
+          silence
+      it "it mute, when there is speedup by 0" $
+        comparePD
+          (Arc 0 1)
+          (fast 0 x)
+          silence
       it "is reciprocal to slow" $
         comparePD
           (Arc 0 1)
@@ -233,22 +223,21 @@
           (Arc 0 10)
           (slow 1 x)
           x
-      it "mutes, when there is" $ do
-        it "silence in first argument" $
-          comparePD
-            (Arc 0 10)
-            (slow silence x)
-            silence
-        it "silence in second argument" $
-          comparePD
-            (Arc 0 10)
-            (slow x silence :: Pattern Time)
-            silence
-        it "speedup by 0" $
-          comparePD
-            (Arc 0 10)
-            (slow 0 x)
-            silence
+      it "mutes, when there is silence in first argument" $
+        comparePD
+          (Arc 0 10)
+          (slow silence x)
+          silence
+      it "mutes, when there is silence in second argument" $
+        comparePD
+          (Arc 0 10)
+          (slow x silence :: Pattern Time)
+          silence
+      it "mutes, when it is speedup by 0" $
+        comparePD
+          (Arc 0 10)
+          (slow 0 x)
+          silence
       it "is reciprocal to fast" $
         comparePD
           (Arc 0 10)
@@ -291,16 +280,17 @@
               (((0.5, 0.75), (0.5, 0.75)), 8)
             ]
 
+    describe "saw goes from 0 up to 1 every cycle" $ do
+      it "0" $
+        queryArc saw (Arc 0 0) `shouldBe` [Event (Context []) Nothing (Arc 0 0) 0 :: Event Double]
+      it "0.25" $
+        queryArc saw (Arc 0.25 0.25) `shouldBe` [Event (Context []) Nothing (Arc 0.25 0.25) 0.25 :: Event Double]
+      it "0.5" $
+        queryArc saw (Arc 0.5 0.5) `shouldBe` [Event (Context []) Nothing (Arc 0.5 0.5) 0.5 :: Event Double]
+      it "0.75" $
+        queryArc saw (Arc 0.75 0.75) `shouldBe` [Event (Context []) Nothing (Arc 0.75 0.75) 0.75 :: Event Double]
+
     describe "saw" $ do
-      it "goes from 0 up to 1 every cycle" $ do
-        it "0" $
-          queryArc saw (Arc 0 0) `shouldBe` [Event (Context []) Nothing (Arc 0 0) 0 :: Event Double]
-        it "0.25" $
-          queryArc saw (Arc 0.25 0.25) `shouldBe` [Event (Context []) Nothing (Arc 0.25 0.25) 0.25 :: Event Double]
-        it "0.5" $
-          queryArc saw (Arc 0.5 0.5) `shouldBe` [Event (Context []) Nothing (Arc 0.5 0.5) 0.5 :: Event Double]
-        it "0.75" $
-          queryArc saw (Arc 0.75 0.75) `shouldBe` [Event (Context []) Nothing (Arc 0.75 0.75) 0.75 :: Event Double]
       it "can be added to" $
         map value (queryArc ((+ 1) <$> saw) (Arc 0.5 0.5)) `shouldBe` [1.5 :: Float]
       it "works on the left of <*>" $
@@ -313,16 +303,15 @@
                        Event (Context []) Nothing (Arc 0.5 0.75) 3,
                        Event (Context []) Nothing (Arc 0.75 1) 3
                      ]
-      it "can be reversed" $ do
-        it "works with whole cycles" $
-          queryArc (rev saw) (Arc 0 1)
-            `shouldBe` [Event (Context []) Nothing (Arc 0 1) 0 :: Event Double]
-        it "works with half cycles" $
-          queryArc (rev saw) (Arc 0.5 1)
-            `shouldBe` [Event (Context []) Nothing (Arc 0.5 1) 0 :: Event Double]
-        it "works with inset points" $
-          queryArc (rev saw) (Arc 0.25 0.25)
-            `shouldBe` [Event (Context []) Nothing (Arc 0.25 0.25) 0.75 :: Event Double]
+      it "works with whole cycles" $
+        queryArc (rev saw) (Arc 0 1)
+          `shouldBe` [Event (Context []) Nothing (Arc 0 1) 0 :: Event Double]
+      it "works with half cycles" $
+        queryArc (rev saw) (Arc 0.5 1)
+          `shouldBe` [Event (Context []) Nothing (Arc 0.5 1) 0 :: Event Double]
+      it "works with inset points" $
+        queryArc (rev saw) (Arc 0.25 0.25)
+          `shouldBe` [Event (Context []) Nothing (Arc 0.25 0.25) 0.75 :: Event Double]
 
     describe "tri" $ do
       it "goes from 0 up to 1 and back every cycle" $
diff --git a/test/Sound/Tidal/ExceptionsTest.hs b/test/Sound/Tidal/ExceptionsTest.hs
--- a/test/Sound/Tidal/ExceptionsTest.hs
+++ b/test/Sound/Tidal/ExceptionsTest.hs
@@ -7,10 +7,10 @@
 import Control.Exception
 import Data.Typeable ()
 import Sound.Tidal.Pattern
-import Test.Microspec
+import Test.Hspec
 import Prelude hiding ((*>), (<*))
 
-run :: Microspec ()
+run :: Spec
 run =
   describe "NFData, forcing and catching exceptions" $ do
     describe "instance NFData (Pattern a)" $ do
@@ -18,43 +18,6 @@
         evaluate (rnf (Pattern undefined Nothing Nothing :: Pattern ()))
           `shouldThrow` anyException
 
--- copied from http://hackage.haskell.org/package/hspec-expectations-0.8.2/docs/src/Test-Hspec-Expectations.html#shouldThrow
-
-shouldThrow :: (Exception e) => IO a -> Selector e -> Microspec ()
-action `shouldThrow` p = prop "shouldThrow" $
-  monadicIO $ do
-    r <- Test.Microspec.run $ try action
-    case r of
-      Right _ ->
-        -- "finished normally, but should throw exception: " ++ exceptionType
-        Test.Microspec.assert False
-      Left e ->
-        -- "threw exception that did not meet expectation")
-        Test.Microspec.assert $ p e
-
-shouldNotThrow :: (Exception e) => IO a -> Selector e -> Microspec ()
-action `shouldNotThrow` p = prop "shouldNotThrow" $
-  monadicIO $ do
-    r <- Test.Microspec.run $ try action
-    case r of
-      Right _ -> Test.Microspec.assert True
-      Left e -> Test.Microspec.assert $ p e
-
--- a string repsentation of the expected exception's type
-{-
-exceptionType = (show . typeOf . instanceOf) p
-  where
-    instanceOf :: Selector a -> a
-    instanceOf _ = error "Test.Hspec.Expectations.shouldThrow: broken Typeable instance"
--}
-
--- |
--- A @Selector@ is a predicate; it can simultaneously constrain the type and
--- value of an exception.
-type Selector a = (a -> Bool)
-
-anyException :: Selector SomeException
-anyException = const True
 
 anyErrorCall :: Selector ErrorCall
 anyErrorCall = const True
diff --git a/test/Sound/Tidal/ParamsTest.hs b/test/Sound/Tidal/ParamsTest.hs
--- a/test/Sound/Tidal/ParamsTest.hs
+++ b/test/Sound/Tidal/ParamsTest.hs
@@ -5,10 +5,10 @@
 import Sound.Tidal.Core
 import Sound.Tidal.Params
 import Sound.Tidal.Pattern
-import Test.Microspec
+import Test.Hspec
 import TestUtils
 
-run :: Microspec ()
+run :: Spec
 run =
   describe "Sound.Tidal.Params" $ do
     describe "VF params" $ do
diff --git a/test/Sound/Tidal/ParseTest.hs b/test/Sound/Tidal/ParseTest.hs
--- a/test/Sound/Tidal/ParseTest.hs
+++ b/test/Sound/Tidal/ParseTest.hs
@@ -4,14 +4,13 @@
 
 import Control.Exception
 import Sound.Tidal.Core
-import Sound.Tidal.ExceptionsTest (anyException, shouldThrow, shouldNotThrow)
 import Sound.Tidal.Pattern
 import Sound.Tidal.UI (_degradeBy)
-import Test.Microspec
+import Test.Hspec
 import TestUtils
 import Prelude hiding ((*>), (<*))
 
-run :: Microspec ()
+run :: Spec
 run =
   describe "Sound.Tidal.Parse" $ do
     describe "parseBP_E" $ do
@@ -342,14 +341,15 @@
           ("a a |b b b|c c c c" :: Pattern String)
           ("[a a|b b b|c c c c]" :: Pattern String)
       it "'|' in list first" $ do
-        evaluate ("1 2@3|4|-|5!6|[7!8 9] 10 . 11 12*2 13!2 . 1@1" :: Pattern String)
-          `shouldNotThrow` anyException
+        compareP
+          (Arc 0 1)
+          ("a|b b" :: Pattern String)
+          ("[a|b b]" :: Pattern String)
       it "'|' in list last" $ do
-        evaluate ("12@1|23@1|12|[1 3!21]" :: Pattern String)
-          `shouldNotThrow` anyException
-      it "toplevel '|'" $ do
-        evaluate ("121|23@1|12|[1 321]" :: Pattern String)
-          `shouldNotThrow` anyException
+        compareP
+          (Arc 0 1)
+          ("a b|c" :: Pattern String)
+          ("[a b|c]" :: Pattern String)
       it "list is same as stack" $ do
         compareP
           (Arc 0 1)
diff --git a/test/Sound/Tidal/PatternTest.hs b/test/Sound/Tidal/PatternTest.hs
--- a/test/Sound/Tidal/PatternTest.hs
+++ b/test/Sound/Tidal/PatternTest.hs
@@ -7,75 +7,56 @@
 import Sound.Tidal.Core
 import Sound.Tidal.Pattern
 import Sound.Tidal.UI
-import Test.Microspec
+import Test.Hspec
 import TestUtils
 import Prelude hiding ((*>), (<*))
 
-run :: Microspec ()
+run :: Spec
 run =
   describe "Sound.Tidal.Pattern" $ do
     describe "Arc" $ do
       it "Arc is a Functor: Apply a given function to the start and end values of an Arc" $ do
         let res = fmap (+ 1) (Arc 3 5)
-        property $ ((Arc 4 6) :: Arc) === res
-
-    {-
-      describe "Event" $ do
-        it "(Bifunctor) first: Apply a function to the Arc elements: whole and part" $ do
-          let res = Event (Context []) (Just $ Arc 1 2) (Arc 3 4) 5 :: Event (Context []) Int
-              f = (+1)
-          property $
-            first f res ===
-            Event (Context []) (Just $ Arc 2 3) (Arc 4 5) 5
-        it "(Bifunctor) second: Apply a function to the event element" $ do
-          let res = Event (Context []) (Just $ Arc 1 2) (Arc 3 4) 5 :: Event (Context []) Int
-              f = (+1)
-          property $
-            second f res ===
-            Event (Context []) (Just $ Arc 1 2) (Arc 3 4) 6-}
+        res `shouldBe` ((Arc 4 6) :: Arc)
 
     describe "whole" $ do
       it "returns the whole Arc in an Event" $ do
-        property $ (Just $ Arc 1 2) === whole (Event (Context []) (Just $ Arc 1 2) (Arc 3 4) 5 :: Event Int)
+        (Just $ Arc 1 2) `shouldBe` whole (Event (Context []) (Just $ Arc 1 2) (Arc 3 4) 5 :: Event Int)
 
     describe "part" $ do
       it "returns the part Arc in an Event" $ do
-        property $ (Arc 3 4) === part (Event (Context []) (Just $ Arc 1 2) (Arc 3 4) 5 :: Event Int)
+        (Arc 3 4) `shouldBe` part (Event (Context []) (Just $ Arc 1 2) (Arc 3 4) 5 :: Event Int)
 
     describe "value" $ do
       it "returns the event value in an Event" $ do
-        property $ 5 === value (Event (Context []) (Just $ Arc (1 :: Rational) 2) (Arc 3 4) (5 :: Int))
+        5 `shouldBe` value (Event (Context []) (Just $ Arc (1 :: Rational) 2) (Arc 3 4) (5 :: Int))
 
     describe "wholeStart" $ do
       it "retrieve the onset of an event: the start of the whole Arc" $ do
-        property $ 1 === wholeStart (Event (Context []) (Just $ Arc 1 2) (Arc 3 4) (5 :: Int))
+        1 `shouldBe` wholeStart (Event (Context []) (Just $ Arc 1 2) (Arc 3 4) (5 :: Int))
 
     describe "eventHasOnset" $ do
       it "return True when the start values of the two arcs in an event are equal" $ do
         let ev = (Event (Context []) (Just $ Arc 1 2) (Arc 1 3) (4 :: Int))
-        property $ True === eventHasOnset ev
+        True `shouldBe` eventHasOnset ev
       it "return False when the start values of the two arcs in an event are not equal" $ do
         let ev = (Event (Context []) (Just $ Arc 1 2) (Arc 3 4) (5 :: Int))
-        property $ False === eventHasOnset ev
+        False `shouldBe` eventHasOnset ev
 
     describe "pure" $ do
       it "fills a whole cycle" $ do
-        property $ queryArc (pure 0) (Arc 0 1) === [(Event (Context []) (Just $ Arc 0 1) (Arc 0 1) (0 :: Int))]
+        queryArc (pure 0) (Arc 0 1) `shouldBe` [(Event (Context []) (Just $ Arc 0 1) (Arc 0 1) (0 :: Int))]
       it "returns the part of an pure that you ask for, preserving the whole" $ do
-        property $ queryArc (pure 0) (Arc 0.25 0.75) === [(Event (Context []) (Just $ Arc 0 1) (Arc 0.25 0.75) (0 :: Int))]
+        queryArc (pure 0) (Arc 0.25 0.75) `shouldBe` [(Event (Context []) (Just $ Arc 0 1) (Arc 0.25 0.75) (0 :: Int))]
       it "gives correct fragments when you go over cycle boundaries" $ do
-        property $
-          queryArc (pure 0) (Arc 0.25 1.25)
-            === [ (Event (Context []) (Just $ Arc 0 1) (Arc 0.25 1) (0 :: Int)),
-                  (Event (Context []) (Just $ Arc 1 2) (Arc 1 1.25) 0)
-                ]
+        queryArc (pure 0) (Arc 0.25 1.25)
+          `shouldBe` [ (Event (Context []) (Just $ Arc 0 1) (Arc 0.25 1) (0 :: Int)),
+            (Event (Context []) (Just $ Arc 1 2) (Arc 1 1.25) 0)]
       it "works with zero-length queries" $ do
-        it "0" $
-          queryArc (pure "a") (Arc 0 0)
-            `shouldBe` fmap toEvent [(((0, 1), (0, 0)), "a" :: String)]
-        it "1/3" $
-          queryArc (pure "a") (Arc (1 % 3) (1 % 3))
-            `shouldBe` fmap toEvent [(((0, 1), (1 % 3, 1 % 3)), "a" :: String)]
+        queryArc (pure "a") (Arc 0 0)
+          `shouldBe` fmap toEvent [(((0, 1), (0, 0)), "a" :: String)]
+        queryArc (pure "a") (Arc (1 % 3) (1 % 3))
+          `shouldBe` fmap toEvent [(((0, 1), (1 % 3, 1 % 3)), "a" :: String)]
 
     describe "_fastGap" $ do
       it "copes with cross-cycle queries" $ do
@@ -105,22 +86,20 @@
               (((0.5, 1), (0.5, 1)), 9)
             ]
       it "can take structure from the both sides" $ do
-        it "one" $
-          queryArc ((fastCat [pure (+ 1), pure (+ 2)]) <*> (fastCat [pure 7, pure 8])) (Arc 0 1)
-            `shouldBe` fmap
-              toEvent
-              [ (((0, 0.5), (0, 0.5)), 8 :: Int),
-                (((0.5, 1), (0.5, 1)), 10)
-              ]
-        it "two" $
-          queryArc ((fastCat [pure (+ 1), pure (+ 2), pure (+ 3)]) <*> (fastCat [pure 7, pure 8])) (Arc 0 1)
-            `shouldBe` fmap
-              toEvent
-              [ (((0 % 1, 1 % 3), (0 % 1, 1 % 3)), 8 :: Int),
-                (((1 % 3, 1 % 2), (1 % 3, 1 % 2)), 9),
-                (((1 % 2, 2 % 3), (1 % 2, 2 % 3)), 10),
-                (((2 % 3, 1 % 1), (2 % 3, 1 % 1)), 11)
-              ]
+        queryArc ((fastCat [pure (+ 1), pure (+ 2)]) <*> (fastCat [pure 7, pure 8])) (Arc 0 1)
+          `shouldBe` fmap
+            toEvent
+            [ (((0, 0.5), (0, 0.5)), 8 :: Int),
+              (((0.5, 1), (0.5, 1)), 10)
+            ]
+        queryArc ((fastCat [pure (+ 1), pure (+ 2), pure (+ 3)]) <*> (fastCat [pure 7, pure 8])) (Arc 0 1)
+          `shouldBe` fmap
+            toEvent
+            [ (((0 % 1, 1 % 3), (0 % 1, 1 % 3)), 8 :: Int),
+              (((1 % 3, 1 % 2), (1 % 3, 1 % 2)), 9),
+              (((1 % 2, 2 % 3), (1 % 2, 2 % 3)), 10),
+              (((2 % 3, 1 % 1), (2 % 3, 1 % 1)), 11)
+            ]
       it "obeys pure id <*> v = v" $ do
         let v = (fastCat [fastCat [pure 7, pure 8], pure 9]) :: Pattern Int
         queryArc ((pure id <*> v)) (Arc 0 5) `shouldBe` queryArc v (Arc 0 5)
@@ -155,10 +134,9 @@
               (((0, 1), (0.5, 1)), 9 :: Int)
             ]
 
-    describe "*>" $ do
-      it "can apply a pattern of values to a pattern of functions" $ do
-        it "works within cycles" $ queryArc ((pure (+ 1)) *> (pure 3)) (Arc 0 1) `shouldBe` fmap toEvent [(((0, 1), (0, 1)), 4 :: Int)]
-        it "works across cycles" $ queryArc ((pure (+ 1)) *> (slow 2 $ pure 3)) (Arc 0 1) `shouldBe` fmap toEvent [(((0, 2), (0, 1)), 4 :: Int)]
+    describe "*> can apply a pattern of values to a pattern of functions" $ do
+      it "works within cycles" $ queryArc ((pure (+ 1)) *> (pure 3)) (Arc 0 1) `shouldBe` fmap toEvent [(((0, 1), (0, 1)), 4 :: Int)]
+      it "works across cycles" $ queryArc ((pure (+ 1)) *> (slow 2 $ pure 3)) (Arc 0 1) `shouldBe` fmap toEvent [(((0, 2), (0, 1)), 4 :: Int)]
       it "doesn't take structure from the left" $ do
         queryArc (pure (+ 1) *> (fastCat [pure 7, pure 8])) (Arc 0 1)
           `shouldBe` fmap
@@ -169,29 +147,24 @@
 
     describe "arcCycles" $ do
       it "leaves a unit cycle intact" $ do
-        it "(0,1)" $ arcCycles (Arc 0 1) `shouldBe` [(Arc 0 1)]
-        it "(3,4)" $ arcCycles (Arc 3 4) `shouldBe` [(Arc 3 4)]
+        arcCycles (Arc 0 1) `shouldBe` [(Arc 0 1)]
+        arcCycles (Arc 3 4) `shouldBe` [(Arc 3 4)]
       it "splits a cycle at cycle boundaries" $ do
-        it "(0,1.1)" $ arcCycles (Arc 0 1.1) `shouldBe` [(Arc 0 1), (Arc 1 1.1)]
-        it "(1,2,1)" $ arcCycles (Arc 1 2.1) `shouldBe` [(Arc 1 2), (Arc 2 2.1)]
-        it "(3 + (1%3),5.1)" $
-          arcCycles (Arc (3 + (1 % 3)) 5.1) `shouldBe` [(Arc (3 + (1 % 3)) 4), (Arc 4 5), (Arc 5 5.1)]
+        arcCycles (Arc 0 1.1) `shouldBe` [(Arc 0 1), (Arc 1 1.1)]
+        arcCycles (Arc 1 2.1) `shouldBe` [(Arc 1 2), (Arc 2 2.1)]
+        arcCycles (Arc (3 + (1 % 3)) 5.1) `shouldBe` [(Arc (3 + (1 % 3)) 4), (Arc 4 5), (Arc 5 5.1)]
 
     describe "unwrap" $ do
       it "preserves inner structure" $ do
-        it "one" $
-          (queryArc (unwrap $ pure (fastCat [pure "a", pure ("b" :: String)])) (Arc 0 1))
-            `shouldBe` (queryArc (fastCat [pure "a", pure "b"]) (Arc 0 1))
-        it "two" $
-          (queryArc (unwrap $ pure (fastCat [pure "a", pure "b", fastCat [pure "c", pure ("d" :: String)]])) (Arc 0 1))
-            `shouldBe` (queryArc (fastCat [pure "a", pure "b", fastCat [pure "c", pure "d"]]) (Arc 0 1))
+        (queryArc (unwrap $ pure (fastCat [pure "a", pure ("b" :: String)])) (Arc 0 1))
+          `shouldBe` (queryArc (fastCat [pure "a", pure "b"]) (Arc 0 1))
+        (queryArc (unwrap $ pure (fastCat [pure "a", pure "b", fastCat [pure "c", pure ("d" :: String)]])) (Arc 0 1))
+          `shouldBe` (queryArc (fastCat [pure "a", pure "b", fastCat [pure "c", pure "d"]]) (Arc 0 1))
       it "preserves outer structure" $ do
-        it "one" $
-          (queryArc (unwrap $ fastCat [pure $ pure "a", pure $ pure ("b" :: String)]) (Arc 0 1))
-            `shouldBe` (queryArc (fastCat [pure "a", pure "b"]) (Arc 0 1))
-        it "two" $
-          (queryArc (unwrap $ fastCat [pure $ pure "a", pure $ pure "b", fastCat [pure $ pure "c", pure $ pure ("d" :: String)]]) (Arc 0 1))
-            `shouldBe` (queryArc (fastCat [pure "a", pure "b", fastCat [pure "c", pure "d"]]) (Arc 0 1))
+        (queryArc (unwrap $ fastCat [pure $ pure "a", pure $ pure ("b" :: String)]) (Arc 0 1))
+          `shouldBe` (queryArc (fastCat [pure "a", pure "b"]) (Arc 0 1))
+        (queryArc (unwrap $ fastCat [pure $ pure "a", pure $ pure "b", fastCat [pure $ pure "c", pure $ pure ("d" :: String)]]) (Arc 0 1))
+          `shouldBe` (queryArc (fastCat [pure "a", pure "b", fastCat [pure "c", pure "d"]]) (Arc 0 1))
       it "gives events whole/part timespans that are an intersection of that of inner and outer events" $ do
         let a = fastCat [pure "a", pure "b"]
             b = fastCat [pure "c", pure "d", pure "e"]
@@ -247,22 +220,18 @@
 
     describe "rotR" $ do
       it "works over two cycles" $
-        property $
-          comparePD (Arc 0 2) (0.25 ~> pure "a") (0.25 `rotR` pure ("a" :: String))
+        comparePD (Arc 0 2) (0.25 ~> pure "a") (0.25 `rotR` pure ("a" :: String))
       it "works over one cycle" $
-        property $
-          compareP (Arc 0 1) (0.25 ~> pure "a") (0.25 `rotR` pure ("a" :: String))
+        compareP (Arc 0 1) (0.25 ~> pure "a") (0.25 `rotR` pure ("a" :: String))
       it "works with zero width queries" $
-        property $
-          compareP (Arc 0 0) (0.25 ~> pure "a") (0.25 `rotR` pure ("a" :: String))
+        compareP (Arc 0 0) (0.25 ~> pure "a") (0.25 `rotR` pure ("a" :: String))
 
     describe "comparePD" $ do
       it "allows split events to be compared" $
-        property $
-          comparePD
-            (Arc 0 2)
-            (splitQueries $ _slow 2 $ pure ("a" :: String))
-            (_slow 2 $ pure "a")
+        comparePD
+          (Arc 0 2)
+          (splitQueries $ _slow 2 $ pure ("a" :: String))
+          (_slow 2 $ pure "a")
 
     describe "controlI" $ do
       it "can retrieve values from state" $
@@ -271,290 +240,283 @@
 
     describe "wholeStart" $ do
       it "retrieve first element of a tuple, inside first element of a tuple, inside the first of another" $ do
-        property $ 1 === wholeStart (Event (Context []) (Just $ Arc 1 2) (Arc 3 4) (5 :: Int))
+         1 `shouldBe` wholeStart (Event (Context []) (Just $ Arc 1 2) (Arc 3 4) (5 :: Int))
 
     describe "wholeStop" $ do
       it "retrieve the end time from the first Arc in an Event" $ do
-        property $ 2 === wholeStop (Event (Context []) (Just $ Arc 1 2) (Arc 3 4) (5 :: Int))
+         2 `shouldBe` wholeStop (Event (Context []) (Just $ Arc 1 2) (Arc 3 4) (5 :: Int))
 
     describe "eventPartStart" $ do
       it "retrieve the start time of the second Arc in an Event" $ do
-        property $ 3 === eventPartStart (Event (Context []) (Just $ Arc 1 2) (Arc 3 4) (5 :: Int))
+         3 `shouldBe` eventPartStart (Event (Context []) (Just $ Arc 1 2) (Arc 3 4) (5 :: Int))
 
     describe "eventPartStop" $ do
       it "retrieve the end time of the second Arc in an Event" $ do
-        property $ 4 === eventPartStop (Event (Context []) (Just $ Arc 1 2) (Arc 3 4) (5 :: Int))
+         4 `shouldBe` eventPartStop (Event (Context []) (Just $ Arc 1 2) (Arc 3 4) (5 :: Int))
 
     describe "eventPart" $ do
       it "retrieve the second Arc in an Event" $ do
-        property $ Arc 3 4 === eventPart (Event (Context []) (Just $ Arc 1 2) (Arc 3 4) (5 :: Int))
+         Arc 3 4 `shouldBe` eventPart (Event (Context []) (Just $ Arc 1 2) (Arc 3 4) (5 :: Int))
 
     describe "eventValue" $ do
       it "retrieve the second value from a tuple" $ do
-        property $ 5 === eventValue (Event (Context []) (Just $ Arc 1 2) (Arc 3 4) (5 :: Int))
+         5 `shouldBe` eventValue (Event (Context []) (Just $ Arc 1 2) (Arc 3 4) (5 :: Int))
 
     describe "eventHasOnset" $ do
       it "return True when the start values of the two arcs in an event are equal" $ do
         let ev = (Event (Context []) (Just $ Arc 1 2) (Arc 1 3) (4 :: Int))
-        property $ True === eventHasOnset ev
+        True `shouldBe` eventHasOnset ev
       it "return False when the start values of the two arcs in an event are not equal" $ do
         let ev = (Event (Context []) (Just $ Arc 1 2) (Arc 3 4) (5 :: Int))
-        property $ False === eventHasOnset ev
+        False `shouldBe` eventHasOnset ev
 
     describe "sam" $ do
       it "start of a cycle, round down time value" $ do
         let res = sam (3.4 :: Time)
-        property $ (3.0 :: Time) === res
+        (3.0 :: Time) `shouldBe` res
 
     describe "nextSam" $ do
       it "the end point of the current cycle, and start of the next" $ do
         let res = nextSam (3.4 :: Time)
-        property $ (4.0 :: Time) === res
+        (4.0 :: Time) `shouldBe` res
 
     describe "arcCycles" $ do
       it "if start time is greater than end time return empty list" $ do
         let res = arcCycles (Arc 2.3 2.1)
-        property $ [] === res
+        [] `shouldBe` res
       it "if start time is equal to end time return empty list" $ do
         let res = arcCycles (Arc 3 3)
-        property $ [] === res
+        [] `shouldBe` res
       it "if start and end time round down to same value return list of (start, end)" $ do
         let res = arcCycles (Arc 2.1 2.3)
-        property $ [(Arc 2.1 2.3)] === res
+        [(Arc 2.1 2.3)] `shouldBe` res
       it "if start time is less than end time and start time does not round down to same value as end time" $ do
         let res = arcCycles (Arc 2.1 3.3)
-        property $ [(Arc 2.1 3.0), (Arc 3.0 3.3)] === res
+        [(Arc 2.1 3.0), (Arc 3.0 3.3)] `shouldBe` res
 
     describe "arcCyclesZW" $ do
       it "if start and end time are equal return list of (start, end)" $ do
         let res = arcCyclesZW (Arc 2.5 2.5)
-        property $ [(Arc 2.5 2.5)] === res
+        [(Arc 2.5 2.5)] `shouldBe` res
       it "if start and end time are not equal call arcCycles (start, end) with same rules as above" $ do
         let res = arcCyclesZW (Arc 2.3 2.1)
-        property $ [] === res
+        [] `shouldBe` res
       it "if start time is less than end time" $ do
         let res = arcCyclesZW (Arc 2.1 2.3)
-        property $ [(Arc 2.1 2.3)] === res
+        [(Arc 2.1 2.3)] `shouldBe` res
       it "if start time is greater than end time" $ do
         let res = arcCyclesZW (Arc 2.1 3.3)
-        property $ [(Arc 2.1 3.0), (Arc 3.0 3.3)] === res
+        [(Arc 2.1 3.0), (Arc 3.0 3.3)] `shouldBe` res
 
     describe "mapCycle" $ do
       it "Apply a function to the Arc values minus the start value rounded down (sam'), adding both results to sam' to obtain the new Arc value" $ do
         let res = mapCycle (* 2) (Arc 3.3 5)
-        property $ ((Arc 3.6 7.0) :: Arc) === res
+        ((Arc 3.6 7.0) :: Arc) `shouldBe` res
 
     describe "toTime" $ do
       it "Convert a number of type Real to a Time value of type Rational, Int test" $ do
         let res = toTime (3 :: Int)
-        property $ (3 % 1 :: Time) === res
+        (3 % 1 :: Time) `shouldBe` res
       it "Convert a number of type Double to a Time value of type Rational" $ do
         let res = toTime (3.2 :: Double)
-        property $ (3602879701896397 % 1125899906842624 :: Time) === res
+        (3602879701896397 % 1125899906842624 :: Time) `shouldBe` res
 
     describe "cyclePos" $ do
       it "Subtract a Time value from its value rounded down (the start of the cycle)" $ do
         let res = cyclePos 2.6
-        property $ (0.6 :: Time) === res
+        (0.6 :: Time) `shouldBe` res
       it "If no difference between a given Time and the start of the cycle" $ do
         let res = cyclePos 2
-        property $ (0.0 :: Time) === res
+        (0.0 :: Time) `shouldBe` res
 
     describe "isIn" $ do
       it "Check given Time is inside a given Arc value, Time is greater than start and less than end Arc values" $ do
         let res = isIn (Arc 2.0 2.8) 2.5
-        property $ True === res
+        True `shouldBe` res
       it "Given Time is equal to the Arc start value" $ do
         let res = isIn (Arc 2.0 2.8) 2.0
-        property $ True === res
+        True `shouldBe` res
       it "Given Time is less than the Arc start value" $ do
         let res = isIn (Arc 2.0 2.8) 1.4
-        property $ False === res
+        False `shouldBe` res
       it "Given Time is greater than the Arc end value" $ do
         let res = isIn (Arc 2.0 2.8) 3.2
-        property $ False === res
+        False `shouldBe` res
 
     describe "onsetIn" $ do
       it "If the beginning of an Event is within a given Arc, same rules as 'isIn'" $ do
         let res = onsetIn (Arc 2.0 2.8) (Event (Context []) (Just $ Arc 2.2 2.7) (Arc 3.3 3.8) (5 :: Int))
-        property $ True === res
+        True `shouldBe` res
       it "Beginning of Event is equal to beggining of given Arc" $ do
         let res = onsetIn (Arc 2.0 2.8) (Event (Context []) (Just $ Arc 2.0 2.7) (Arc 3.3 3.8) (5 :: Int))
-        property $ True === res
+        True `shouldBe` res
       it "Beginning of an Event is less than the start of the Arc" $ do
         let res = onsetIn (Arc 2.0 2.8) (Event (Context []) (Just $ Arc 1.2 1.7) (Arc 3.3 3.8) (5 :: Int))
-        property $ False === res
+        False `shouldBe` res
       it "Start of Event is greater than the start of the given Arc" $ do
         let res = onsetIn (Arc 2.0 2.8) (Event (Context []) (Just $ Arc 3.1 3.5) (Arc 4.0 4.6) (5 :: Int))
-        property $ False === res
+        False `shouldBe` res
 
     describe "subArc" $ do
       it "Checks if an Arc is within another, returns Just (max $ (fst a1) (fst a2), min $ (snd a1) (snd a2)) if so, otherwise Nothing" $ do
         let res = subArc (Arc 2.1 2.4) (Arc 2.4 2.8)
-        property $ Nothing === res
+        Nothing `shouldBe` res
       it "if max (fst arc1) (fst arc2) <= min (snd arc1) (snd arc2) return Just (max (fst arc1) (fst arc2), min...)" $ do
         let res = subArc (Arc 2 2.8) (Arc 2.4 2.9)
-        property $ Just (Arc 2.4 2.8) === res
+        Just (Arc 2.4 2.8) `shouldBe` res
 
     describe "timeToCycleArc" $ do
       it "given a Time value return the Arc in which it resides" $ do
         let res = timeToCycleArc 2.2
-        property $ (Arc 2.0 3.0) === res
+        (Arc 2.0 3.0) `shouldBe` res
 
     describe "cyclesInArc" $ do
       it "Return a list of cycles in a given arc, if start is greater than end return empty list" $ do
         let res = cyclesInArc (Arc 2.4 2.2)
-        property $ ([] :: [Int]) === res
+        ([] :: [Int]) `shouldBe` res
       it "If start value of Arc is equal to end value return list with start value rounded down" $ do
         let res = cyclesInArc (Arc 2.4 2.4)
-        property $ ([2] :: [Int]) === res
+        ([2] :: [Int]) `shouldBe` res
       it "if start of Arc is less than end return list of start rounded down to end rounded up minus one" $ do
         let res = cyclesInArc (Arc 2.2 4.5)
-        property $ ([2, 3, 4] :: [Int]) === res
+        ([2, 3, 4] :: [Int]) `shouldBe` res
 
     describe "cycleArcsInArc" $ do
       it "generates a list of Arcs based on the cycles found within a given a Arc" $ do
         let res = cycleArcsInArc (Arc 2.2 4.5)
-        property $ [(Arc 2.0 3.0), (Arc 3.0 4.0), (Arc 4.0 5.0)] === res
+        [(Arc 2.0 3.0), (Arc 3.0 4.0), (Arc 4.0 5.0)] `shouldBe` res
 
     describe "isAdjacent" $ do
       it "if the given Events are adjacent parts of the same whole" $ do
         let res = isAdjacent (Event (Context []) (Just $ Arc 1 2) (Arc 3 4) 5) (Event (Context []) (Just $ Arc 1 2) (Arc 4 3) (5 :: Int))
-        property $ True === res
+        True `shouldBe` res
       it "if first Arc of of first Event is not equal to first Arc of second Event" $ do
         let res = isAdjacent (Event (Context []) (Just $ Arc 1 2) (Arc 3 4) 5) (Event (Context []) (Just $ Arc 7 8) (Arc 4 3) (5 :: Int))
-        property $ False === res
+        False `shouldBe` res
       it "if the value of the first Event does not equal the value of the second Event" $ do
         let res = isAdjacent (Event (Context []) (Just $ Arc 1 2) (Arc 3 4) 5) (Event (Context []) (Just $ Arc 1 2) (Arc 4 3) (6 :: Int))
-        property $ False === res
+        False `shouldBe` res
       it "second value of second Arc of first Event not equal to first value of second Arc in second Event..." $ do
         let res = isAdjacent (Event (Context []) (Just $ Arc 1 2) (Arc 3 4) 5) (Event (Context []) (Just $ Arc 1 2) (Arc 3 4) (5 :: Int))
-        property $ False === res
+        False `shouldBe` res
 
     describe "defragParts" $ do
       it "if empty list with no events return empty list" $ do
         let res = defragParts ([] :: [Event Int])
-        property $ [] === res
+        [] `shouldBe` res
       it "if list consists of only one Event return it as is" $ do
         let res = defragParts [(Event (Context []) (Just $ Arc 1 2) (Arc 3 4) (5 :: Int))]
-        property $ [Event (Context []) (Just $ Arc 1 2) (Arc 3 4) (5 :: Int)] === res
+        [Event (Context []) (Just $ Arc 1 2) (Arc 3 4) (5 :: Int)] `shouldBe` res
       it "if list contains adjacent Events return list with Parts combined" $ do
         let res = defragParts [(Event (Context []) (Just $ Arc 1 2) (Arc 3 4) (5 :: Int)), (Event (Context []) (Just $ Arc 1 2) (Arc 4 3) (5 :: Int))]
-        property $ [(Event (Context []) (Just $ Arc 1 2) (Arc 3 4) 5)] === res
+        [(Event (Context []) (Just $ Arc 1 2) (Arc 3 4) 5)] `shouldBe` res
       it "if list contains more than one Event none of which are adjacent, return List as is" $ do
         let res = defragParts [(Event (Context []) (Just $ Arc 1 2) (Arc 3 4) 5), (Event (Context []) (Just $ Arc 7 8) (Arc 4 3) (5 :: Int))]
-        property $ [Event (Context []) (Just $ Arc 1 2) (Arc 3 4) 5, Event (Context []) (Just $ Arc 7 8) (Arc 4 3) (5 :: Int)] === res
+        [Event (Context []) (Just $ Arc 1 2) (Arc 3 4) 5, Event (Context []) (Just $ Arc 7 8) (Arc 4 3) (5 :: Int)] `shouldBe` res
 
     describe "sect" $ do
       it "take two Arcs and return - Arc (max of two starts) (min of two ends)" $ do
         let res = sect (Arc 2.2 3) (Arc 2 2.9)
-        property $ Arc 2.2 2.9 == res
+        Arc 2.2 2.9 == res
 
     describe "hull" $ do
       it "take two Arcs anre return - Arc (min of two starts) (max of two ends)" $ do
         let res = hull (Arc 2.2 3) (Arc 2 2.9)
-        property $ Arc 2 3 == res
+        Arc 2 3 == res
 
     describe "withResultArc" $ do
       it "apply given function to the Arcs" $ do
         let p = withResultArc (+ 5) (stripContext $ fast "1 2" "3 4" :: Pattern Int)
         let res = queryArc p (Arc 0 1)
-        property $ res === fmap toEvent [(((5, 11 % 2), (5, 11 % 2)), 3), (((11 % 2, 23 % 4), (11 % 2, 23 % 4)), 3), (((23 % 4, 6), (23 % 4, 6)), 4)]
+        res `shouldBe` fmap toEvent [(((5, 11 % 2), (5, 11 % 2)), 3), (((11 % 2, 23 % 4), (11 % 2, 23 % 4)), 3), (((23 % 4, 6), (23 % 4, 6)), 4)]
 
     describe "applyFIS" $ do
       it "apply Float function when value of type VF" $ do
         let res = applyFIS (+ 1) (+ 1) (++ "1") (VF 1)
-        property $ (VF 2.0) === res
+        (VF 2.0) `shouldBe` res
       it "apply Int function when value of type VI" $ do
         let res = applyFIS (+ 1) (+ 1) (++ "1") (VI 1)
-        property $ (VI 2) === res
+        (VI 2) `shouldBe` res
       it "apply String function when value of type VS" $ do
         let res = applyFIS (+ 1) (+ 1) (++ "1") (VS "1")
-        property $ (VS "11") === res
+        (VS "11") `shouldBe` res
 
     describe "fNum2" $ do
       it "apply Int function for two Int values" $ do
         let res = fNum2 (+) (+) (VI 2) (VI 3)
-        property $ (VI 5) === res
+        (VI 5) `shouldBe` res
       it "apply float function when given two float values" $ do
         let res = fNum2 (+) (+) (VF 2) (VF 3)
-        property $ (VF 5.0) === res
+        (VF 5.0) `shouldBe` res
       it "apply float function when one float and one int value given" $ do
         let res = fNum2 (+) (+) (VF 2) (VI 3)
-        property $ (VF 5.0) === res
+        (VF 5.0) `shouldBe` res
 
     describe "getI" $ do
       it "get Just value when Int value is supplied" $ do
         let res = getI (VI 3)
-        property $ (Just 3) === res
+        (Just 3) `shouldBe` res
       it "get floored value when float value is supplied" $ do
         let res = getI (VF 3.5)
-        property $ (Just 3) === res
+        (Just 3) `shouldBe` res
       it "get if String value is supplied" $ do
         let res = getI (VS "3")
-        property $ Nothing === res
+        Nothing `shouldBe` res
 
     describe "getF" $ do
       it "get Just value when Float value is supplied" $ do
         let res = getF (VF 3)
-        property $ (Just 3.0) === res
+        (Just 3.0) `shouldBe` res
       it "get converted value if Int value is supplied" $ do
         let res = getF (VI 3)
-        property $ (Just 3.0) === res
+        (Just 3.0) `shouldBe` res
 
     describe "getS" $ do
       it "get Just value when String value is supplied" $ do
         let res = getS (VS "Tidal")
-        property $ (Just "Tidal") === res
+        (Just "Tidal") `shouldBe` res
       it "get Nothing if Int value is not supplied" $ do
         let res = getS (VI 3)
-        property $ Nothing === res
+        Nothing `shouldBe` res
 
     describe "filterValues" $ do
       it "remove Events above given threshold" $ do
         let fil = filterValues (< 2) $ fastCat [pure 1, pure 2, pure 3] :: Pattern Time
         let res = queryArc fil (Arc 0.5 1.5)
-        property $ fmap toEvent [(((1, 4 % 3), (1, 4 % 3)), 1 % 1)] === res
+        fmap toEvent [(((1, 4 % 3), (1, 4 % 3)), 1 % 1)] `shouldBe` res
 
       it "remove Events below given threshold" $ do
         let fil = filterValues (> 2) $ fastCat [pure 1, pure 2, pure 3] :: Pattern Time
         let res = queryArc fil (Arc 0.5 1.5)
-        property $ fmap toEvent [(((2 % 3, 1), (2 % 3, 1)), 3 % 1)] === res
+        fmap toEvent [(((2 % 3, 1), (2 % 3, 1)), 3 % 1)] `shouldBe` res
 
     describe "filterWhen" $ do
       it "filter below given threshold" $ do
         let fil = filterWhen (< 0.5) $ struct "t*4" $ (tri :: Pattern Double) + 1
         let res = queryArc fil (Arc 0.5 1.5)
-        property $ [] === res
+        [] `shouldBe` res
 
       it "filter above given threshold" $ do
         let fil = stripContext $ filterWhen (> 0.5) $ struct "t*4" $ (tri :: Pattern Double) + 1
         let res = queryArc fil (Arc 0.5 1.5)
-        property $ fmap toEvent [(((3 % 4, 1), (3 % 4, 1)), 1.5), (((1, 5 % 4), (1, 5 % 4)), 1), (((5 % 4, 3 % 2), (5 % 4, 3 % 2)), 1.5)] === res
+        fmap toEvent [(((3 % 4, 1), (3 % 4, 1)), 1.5), (((1, 5 % 4), (1, 5 % 4)), 1), (((5 % 4, 3 % 2), (5 % 4, 3 % 2)), 1.5)] `shouldBe` res
 
     describe "compressArc" $ do
       it "return empty if start time is greater than end time" $ do
         let res = queryArc (compressArc (Arc 0.8 0.1) (fast "1 2" "3 4" :: Pattern Time)) (Arc 1 2)
-        property $ [] === res
+        [] `shouldBe` res
 
       it "return empty if start time or end time are greater than 1" $ do
         let res = queryArc (compressArc (Arc 0.1 2) (fast "1 2" "3 4" :: Pattern Time)) (Arc 1 2)
-        property $ [] === res
+        [] `shouldBe` res
 
       it "return empty if start or end are less than zero" $ do
         let res = queryArc (compressArc (Arc (-0.8) 0.1) (fast "1 2" "3 4" :: Pattern Time)) (Arc 1 2)
-        property $ [] === res
+        [] `shouldBe` res
 
       it "otherwise compress difference between start and end values of Arc" $ do
         let p = fast "1 2" "3 4" :: Pattern Time
         let res = queryArc (stripContext $ compressArc (Arc 0.2 0.8) p) (Arc 0 1)
         let expected = fmap toEvent [(((1 % 5, 1 % 2), (1 % 5, 1 % 2)), 3 % 1), (((1 % 2, 13 % 20), (1 % 2, 13 % 20)), 3 % 1), (((13 % 20, 4 % 5), (13 % 20, 4 % 5)), 4 % 1)]
-        property $ expected === res
+        expected `shouldBe` res
 
--- pending "Sound.Tidal.Pattern.eventL" $ do
---  it "succeeds if the first event 'whole' is shorter" $ do
---    property $ eventL (Event (Context []) (Just $ Arc 0,0),(Arc 0 1)),"x") (((0 0) (Arc 0 1.1)) "x")
---  it "fails if the events are the same length" $ do
---    property $ not $ eventL (Event (Context []) (Just $ Arc 0,0),(Arc 0 1)),"x") (((0 0) (Arc 0 1)) "x")
---  it "fails if the second event is shorter" $ do
---    property $ not $ eventL (Event (Context []) (Just $ Arc 0,0),(Arc 0 1)),"x") (((0 0) (Arc 0 0.5)) "x")
diff --git a/test/Sound/Tidal/ScalesTest.hs b/test/Sound/Tidal/ScalesTest.hs
--- a/test/Sound/Tidal/ScalesTest.hs
+++ b/test/Sound/Tidal/ScalesTest.hs
@@ -4,11 +4,11 @@
 
 import Sound.Tidal.Pattern
 import Sound.Tidal.Scales
-import Test.Microspec
+import Test.Hspec
 import TestUtils
 import Prelude hiding ((*>), (<*))
 
-run :: Microspec ()
+run :: Spec
 run =
   describe "Sound.Tidal.Scales" $ do
     describe "scale" $ do
diff --git a/test/Sound/Tidal/StepwiseTest.hs b/test/Sound/Tidal/StepwiseTest.hs
--- a/test/Sound/Tidal/StepwiseTest.hs
+++ b/test/Sound/Tidal/StepwiseTest.hs
@@ -8,22 +8,17 @@
 import Sound.Tidal.ParseBP ()
 import Sound.Tidal.Pattern
   ( ArcF (Arc),
-    Pattern (tactus),
+    Pattern (steps),
     fast,
     rev,
   )
 import Sound.Tidal.Stepwise (expand, stepcat, stepdrop, steptake)
 import Sound.Tidal.UI (inv, iter, linger, segment)
-import Test.Microspec
-  ( MTestable (describe),
-    Microspec,
-    it,
-    shouldBe,
-  )
-import TestUtils (compareP, firstCycleValues)
+import Test.Hspec (Spec, describe, it, shouldBe)
+import TestUtils (compareP)
 import Prelude hiding ((*>), (<*))
 
-run :: Microspec ()
+run :: Spec
 run =
   describe "Sound.Tidal.Stepwise" $ do
     describe "stepcat" $ do
@@ -33,133 +28,22 @@
       it "can pattern expands" $ do
         compareP (Arc 0 8) (expand "2 1" ("a b c" :: Pattern Char)) "a@2 b@2 c@2 a b c"
     describe "steptake" $ do
-      it "can pattern takes" $ do
+      it "can pattern steptakes" $ do
         compareP (Arc 0 8) (steptake "1 2 3 4" ("a b c d" :: Pattern Char)) "a a b a b c a b c d"
-      it "can pattern reverse takes" $ do
+      it "can pattern reverse steptakes" $ do
         compareP (Arc 0 8) (steptake "-1 -2 -3 -4" ("a b c d" :: Pattern Char)) "d c d b c d a b c d"
     describe "stepdrop" $ do
-      it "can pattern drops" $ do
+      it "can pattern stepdrops" $ do
         compareP (Arc 0 8) (stepdrop "0 1 2 3" ("a b c d" :: Pattern Char)) "a b c d a b c a b a"
-      it "can pattern reverse drops" $ do
+      it "can pattern reverse stepdrops" $ do
         compareP (Arc 0 8) (stepdrop "0 -1 -2 -3" ("a b c d" :: Pattern Char)) "a b c d b c d c d d"
-    describe "tactus" $ do
-      it "is correctly preserved/calculated through transformations" $ do
-        it "linger" $ (firstCycleValues <$> tactus (linger 4 "a b c" :: Pattern Char)) `shouldBe` Just [3]
-        it "iter" $ (firstCycleValues <$> tactus (iter 4 "a b c" :: Pattern Char)) `shouldBe` Just [3]
-        it "fast" $ (firstCycleValues <$> tactus (fast 4 "a b c" :: Pattern Char)) `shouldBe` Just [3]
-        it "hurry" $ (firstCycleValues <$> tactus (hurry 4 $ sound "a b c")) `shouldBe` Just [3]
-        it "rev" $ (firstCycleValues <$> tactus (rev "a b c" :: Pattern Char)) `shouldBe` Just [3]
-        it "segment" $ (firstCycleValues <$> tactus (segment 10 "a" :: Pattern Char)) `shouldBe` Just [10]
-        it "invert" $ (firstCycleValues <$> tactus (inv "1 0 1" :: Pattern Bool)) `shouldBe` Just [3]
-        it "chop" $ (firstCycleValues <$> tactus (chop 3 $ sound "a b")) `shouldBe` Just [6]
-        it "chop" $ (firstCycleValues <$> tactus (striate 3 $ sound "a b")) `shouldBe` Just [6]
-
---     expect(sequence({ s: 'bev' }, { s: 'amenbreak' }).chop(4).tactus).toStrictEqual(Fraction(8));
---     expect(sequence({ s: 'bev' }, { s: 'amenbreak' }).striate(4).tactus).toStrictEqual(Fraction(8));
---     expect(sequence({ s: 'bev' }, { s: 'amenbreak' }).slice(4, sequence(0, 1, 2, 3)).tactus).toStrictEqual(
---       Fraction(4),
---     );
---     expect(sequence({ s: 'bev' }, { s: 'amenbreak' }).splice(4, sequence(0, 1, 2, 3)).tactus).toStrictEqual(
---       Fraction(4),
---     );
---     expect(sequence({ n: 0 }, { n: 1 }, { n: 2 }).chop(4).tactus).toStrictEqual(Fraction(12));
---     expect(
---       pure((x) => x + 1)
---         .setTactus(3)
---         .appBoth(pure(1).setTactus(2)).tactus,
---     ).toStrictEqual(Fraction(6));
---     expect(
---       pure((x) => x + 1)
---         .setTactus(undefined)
---         .appBoth(pure(1).setTactus(2)).tactus,
---     ).toStrictEqual(Fraction(2));
---     expect(
---       pure((x) => x + 1)
---         .setTactus(3)
---         .appBoth(pure(1).setTactus(undefined)).tactus,
---     ).toStrictEqual(Fraction(3));
---     expect(stack(fastcat(0, 1, 2), fastcat(3, 4)).tactus).toStrictEqual(Fraction(6));
---     expect(stack(fastcat(0, 1, 2), fastcat(3, 4).setTactus(undefined)).tactus).toStrictEqual(Fraction(3));
---     expect(stackLeft(fastcat(0, 1, 2, 3), fastcat(3, 4)).tactus).toStrictEqual(Fraction(4));
---     expect(stackRight(fastcat(0, 1, 2), fastcat(3, 4)).tactus).toStrictEqual(Fraction(3));
---     // maybe this should double when they are either all even or all odd
---     expect(stackCentre(fastcat(0, 1, 2), fastcat(3, 4)).tactus).toStrictEqual(Fraction(3));
---     expect(fastcat(0, 1).ply(3).tactus).toStrictEqual(Fraction(6));
---     expect(fastcat(0, 1).setTactus(undefined).ply(3).tactus).toStrictEqual(undefined);
---     expect(fastcat(0, 1).fast(3).tactus).toStrictEqual(Fraction(2));
---     expect(fastcat(0, 1).setTactus(undefined).fast(3).tactus).toStrictEqual(undefined);
---   });
--- });
--- describe('stepcat', () => {
---   it('can cat', () => {
---     expect(sameFirst(stepcat(fastcat(0, 1, 2, 3), fastcat(4, 5)), fastcat(0, 1, 2, 3, 4, 5)));
---     expect(sameFirst(stepcat(pure(1), pure(2), pure(3)), fastcat(1, 2, 3)));
---   });
---   it('calculates undefined tactuses as the average', () => {
---     expect(sameFirst(stepcat(pure(1), pure(2), pure(3).setTactus(undefined)), fastcat(1, 2, 3)));
---   });
--- });
--- describe('taper', () => {
---   it('can taper', () => {
---     expect(sameFirst(sequence(0, 1, 2, 3, 4).taper(1, 5), sequence(0, 1, 2, 3, 4, 0, 1, 2, 3, 0, 1, 2, 0, 1, 0)));
---   });
---   it('can taper backwards', () => {
---     expect(sameFirst(sequence(0, 1, 2, 3, 4).taper(-1, 5), sequence(0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4)));
---   });
--- });
--- describe('increase and decrease', () => {
---   it('can increase from the left', () => {
---     expect(sameFirst(sequence(0, 1, 2, 3, 4).increase(2), sequence(0, 1)));
---   });
---   it('can decrease to the left', () => {
---     expect(sameFirst(sequence(0, 1, 2, 3, 4).decrease(2), sequence(0, 1, 2)));
---   });
---   it('can increase from the right', () => {
---     expect(sameFirst(sequence(0, 1, 2, 3, 4).increase(-2), sequence(3, 4)));
---   });
---   it('can decrease to the right', () => {
---     expect(sameFirst(sequence(0, 1, 2, 3, 4).decrease(-2), sequence(2, 3, 4)));
---   });
---   it('can decrease nothing', () => {
---     expect(sameFirst(pure('a').decrease(0), pure('a')));
---   });
---   it('can decrease nothing, repeatedly', () => {
---     expect(sameFirst(pure('a').decrease(0, 0), fastcat('a', 'a')));
---     for (var i = 0; i < 100; ++i) {
---       expect(sameFirst(pure('a').decrease(...Array(i).fill(0)), fastcat(...Array(i).fill('a'))));
---     }
---   });
--- });
--- describe('expand', () => {
---   it('can expand four things in half', () => {
---     expect(
---       sameFirst(sequence(0, 1, 2, 3).expand(1, 0.5), stepcat(sequence(0, 1, 2, 3), sequence(0, 1, 2, 3).expand(0.5))),
---     );
---   });
---   it('can expand five things in half', () => {
---     expect(
---       sameFirst(
---         sequence(0, 1, 2, 3, 4).expand(1, 0.5),
---         stepcat(sequence(0, 1, 2, 3, 4), sequence(0, 1, 2, 3, 4).expand(0.5)),
---       ),
---     );
---   });
--- });
--- describe('stepJoin', () => {
---   it('can join a pattern with a tactus of 2', () => {
---     expect(
---       sameFirst(
---         sequence(pure(pure('a')), pure(pure('b').setTactus(2))).stepJoin(),
---         stepcat(pure('a'), pure('b').setTactus(2)),
---       ),
---     );
---   });
---   it('can join a pattern with a tactus of 0.5', () => {
---     expect(
---       sameFirst(
---         sequence(pure(pure('a')), pure(pure('b').setTactus(0.5))).stepJoin(),
---         stepcat(pure('a'), pure('b').setTactus(0.5)),
---       ),
---     );
---   });
--- });
+    describe "step count is correctly preserved/calculated through transformations" $ do
+      it "linger" $ steps (linger 4 "a b c" :: Pattern Char) `shouldBe` Just 3
+      it "iter" $ steps (iter 4 "a b c" :: Pattern Char) `shouldBe` Just 3
+      it "fast" $ steps (fast 4 "a b c" :: Pattern Char) `shouldBe` Just 3
+      it "hurry" $ steps (hurry 4 $ sound "a b c") `shouldBe` Just 3
+      it "rev" $ steps (rev "a b c" :: Pattern Char) `shouldBe` Just 3
+      it "segment" $ steps (segment 10 "a" :: Pattern Char) `shouldBe` Just 10
+      it "invert" $ steps (inv "1 0 1" :: Pattern Bool) `shouldBe` Just 3
+      it "chop" $ steps (chop 3 $ sound "a b") `shouldBe` Just 6
+      it "chop" $ steps (striate 3 $ sound "a b") `shouldBe` Just 6
diff --git a/test/Sound/Tidal/UITest.hs b/test/Sound/Tidal/UITest.hs
--- a/test/Sound/Tidal/UITest.hs
+++ b/test/Sound/Tidal/UITest.hs
@@ -4,17 +4,18 @@
 
 import qualified Data.Map.Strict as Map
 -- import Sound.Tidal.Pattern
-import Sound.Tidal.Control
+import Sound.Tidal.Control ( _chop )
 import Sound.Tidal.Core
-import Sound.Tidal.Params
-import Sound.Tidal.ParseBP
+    ( sig, sine, saw, (|+), (>|), (#), run, cat, fastcat, (<~) )
+import Sound.Tidal.Params ( sound, begin, crush, end, n, speed, s )
+import Sound.Tidal.ParseBP ( parseBP_E )
 import Sound.Tidal.Pattern
 import Sound.Tidal.UI
-import Test.Microspec
-import TestUtils
+import Test.Hspec ( describe, it, shouldBe, Spec )
+import TestUtils ( compareP, comparePD, compareTol, ps )
 import Prelude hiding ((*>), (<*))
 
-run :: Microspec ()
+run :: Spec
 run =
   describe "Sound.Tidal.UI" $ do
     describe "_chop" $ do
@@ -27,10 +28,9 @@
         compareP
           (Arc 0 1)
           (slow 2 $ _chop 2 $ s (pure "a"))
-          (begin (pure 0) # end (pure 0.5) # (s (pure "a")))
+          (begin (pure 0) # end (pure 0.5) # s (pure "a"))
       it "can chop a chop" $
-        property $
-          compareTol (Arc 0 1) (_chop 6 $ s $ pure "a") (_chop 2 $ _chop 3 $ s $ pure "a")
+        compareTol (Arc 0 1) (_chop 6 $ s $ pure "a") (_chop 2 $ _chop 3 $ s $ pure "a")
 
     describe "segment" $ do
       it "can turn a single event into multiple events" $ do
@@ -104,39 +104,35 @@
          in compareP overTimeSpan testMe expectedResult
 
     describe "rand" $ do
-      it "generates a (pseudo-)random number between zero & one" $ do
-        it "at the start of a cycle" $
-          (queryArc rand (Arc 0 0)) `shouldBe` [Event (Context []) Nothing (Arc 0 0) (0 :: Float)]
-        it "at 1/4 of a cycle" $
-          (queryArc rand (Arc 0.25 0.25))
-            `shouldBe` [Event (Context []) Nothing (Arc 0.25 0.25) (0.6295689214020967 :: Float)]
-        it "at 3/4 of a cycle" $
-          (queryArc rand (Arc 0.75 0.75))
+      it "it generates a (pseudo-)random number between 0 and 1 at the start of a cycle" $
+        (queryArc rand (Arc 0 0)) `shouldBe` [Event (Context []) Nothing (Arc 0 0) (0.5 :: Float)]
+      it "it generates a (pseudo-)random number between 0 and 1 at 1/4 of a cycle" $
+        (queryArc rand (Arc 0.25 0.25))
+          `shouldBe` [Event (Context []) Nothing (Arc 0.25 0.25) (0.6295689214020967 :: Float)]
+      it "it generates a (pseudo-)random number between 0 and 1 at 3/4 of a cycle" $
+        (queryArc rand (Arc 0.75 0.75))
             `shouldBe` [Event (Context []) Nothing (Arc 0.75 0.75) (0.20052618719637394 :: Float)]
 
     describe "irand" $ do
-      it "generates a (pseudo-random) integer between zero & i" $ do
-        it "at the start of a cycle" $
-          (queryArc (irand 10) (Arc 0 0)) `shouldBe` [Event (Context []) Nothing (Arc 0 0) (0 :: Int)]
-        it "at 1/4 of a cycle" $
-          (queryArc (irand 10) (Arc 0.25 0.25)) `shouldBe` [Event (Context []) Nothing (Arc 0.25 0.25) (6 :: Int)]
-        it "is patternable" $
-          (queryArc (irand "10 2") (Arc 0 1))
-            `shouldBe` [ Event (Context [((1, 1), (3, 1))]) Nothing (Arc 0 0.5) (6 :: Int),
-                         Event (Context [((4, 1), (5, 1))]) Nothing (Arc 0.5 1) (0 :: Int)
-                       ]
+      -- it "generates a (pseudo-random) integer between zero & i" $ do
+      it "at the start of a cycle" $
+        (queryArc (irand 10) (Arc 0 0)) `shouldBe` [Event (Context []) Nothing (Arc 0 0) (5 :: Int)]
+      it "at 1/4 of a cycle" $
+        (queryArc (irand 10) (Arc 0.25 0.25)) `shouldBe` [Event (Context []) Nothing (Arc 0.25 0.25) (6 :: Int)]
+      it "is patternable" $
+        (queryArc (irand "10 2") (Arc 0 1))
+          `shouldBe` [ Event (Context [((1, 1), (3, 1))]) Nothing (Arc 0 0.5) (5 :: Int),
+                       Event (Context [((4, 1), (5, 1))]) Nothing (Arc 0.5 1) (0 :: Int)
+                     ]
 
     describe "normal" $ do
-      it "produces values within [0,1] in a bell curve" $ do
-        it "at the start of a cycle" $
-          queryArc normal (Arc 0 0.1)
-            `shouldBe` [Event (Context []) Nothing (Arc 0 0.1) (0.4614205864457064 :: Double)]
-        it "at 1/4 of a cycle" $
-          queryArc normal (Arc 0.25 0.25)
-            `shouldBe` [Event (Context []) Nothing (Arc 0.25 0.25) (0.5 :: Double)]
-        it "at 3/4 of a cycle" $
-          queryArc normal (Arc 0.75 0.75)
-            `shouldBe` [Event (Context []) Nothing (Arc 0.75 0.75) (0.5 :: Double)]
+      it "produces values within [0,1] in a bell curve at different parts of a cycle" $ do
+        queryArc normal (Arc 0 0.1)
+          `shouldBe` [Event (Context []) Nothing (Arc 0 0.1) (0.42461738824387246 :: Double)]
+        queryArc normal (Arc 0.25 0.25)
+          `shouldBe` [Event (Context []) Nothing (Arc 0.25 0.25) (0.5 :: Double)]
+        queryArc normal (Arc 0.75 0.75)
+          `shouldBe` [Event (Context []) Nothing (Arc 0.75 0.75) (0.5 :: Double)]
 
     describe "range" $ do
       describe "scales a pattern to the supplied range" $ do
@@ -174,19 +170,16 @@
 
     describe "rot" $ do
       it "rotates values in a pattern irrespective of structure" $
-        property $
           comparePD
             (Arc 0 2)
             (rot 1 "a ~ b c" :: Pattern String)
             ("b ~ c a" :: Pattern String)
       it "works with negative values" $
-        property $
           comparePD
             (Arc 0 2)
             (rot (-1) "a ~ b c" :: Pattern String)
             ("c ~ a b" :: Pattern String)
       it "works with complex patterns" $
-        property $
           comparePD
             (Arc 0 2)
             (rot (1) "a ~ [b [c ~ d]] [e <f g>]" :: Pattern String)
@@ -296,42 +289,40 @@
           (cat ["1 1" :: Pattern Int, "2 [3 3] 4" :: Pattern Int, "3 [5 5] 7" :: Pattern Int])
 
     describe "euclid" $ do
-      it "matches examples in Toussaint's paper" $ do
-        sequence_ $
-          map
-            (\(a, b) -> it b $ compareP (Arc 0 1) a (parseBP_E b))
-            ( [ (euclid 1 2 "x", "x ~"),
-                (euclid 1 3 "x", "x ~ ~"),
-                (euclid 1 4 "x", "x ~ ~ ~"),
-                (euclid 4 12 "x", "x ~ ~ x ~ ~ x ~ ~ x ~ ~"),
-                (euclid 2 5 "x", "x ~ x ~ ~"),
-                -- (euclid 3 4 "x", "x ~ x x"), -- Toussaint is wrong..
-                (euclid 3 4 "x", "x x x ~"), -- correction
-                (euclid 3 5 "x", "x ~ x ~ x"),
-                (euclid 3 7 "x", "x ~ x ~ x ~ ~"),
-                (euclid 3 8 "x", "x ~ ~ x ~ ~ x ~"),
-                (euclid 4 7 "x", "x ~ x ~ x ~ x"),
-                (euclid 4 9 "x", "x ~ x ~ x ~ x ~ ~"),
-                (euclid 4 11 "x", "x ~ ~ x ~ ~ x ~ ~ x ~"),
-                -- (euclid 5 6 "x", "x ~ x x x x"), -- Toussaint is wrong..
-                (euclid 5 6 "x", "x x x x x ~"), -- correction
-                (euclid 5 7 "x", "x ~ x x ~ x x"),
-                (euclid 5 8 "x", "x ~ x x ~ x x ~"),
-                (euclid 5 9 "x", "x ~ x ~ x ~ x ~ x"),
-                (euclid 5 11 "x", "x ~ x ~ x ~ x ~ x ~ ~"),
-                (euclid 5 12 "x", "x ~ ~ x ~ x ~ ~ x ~ x ~"),
-                -- (euclid 5 16 "x", "x ~ ~ x ~ ~ x ~ ~ x ~ ~ x ~ ~ ~ ~"),  -- Toussaint is wrong..
-                (euclid 5 16 "x", "x ~ ~ x ~ ~ x ~ ~ x ~ ~ x ~ ~ ~"), -- correction
-                -- (euclid 7 8 "x", "x ~ x x x x x x"), -- Toussaint is wrong..
-                (euclid 7 8 "x", "x x x x x x x ~"), -- Correction
-                (euclid 7 12 "x", "x ~ x x ~ x ~ x x ~ x ~"),
-                (euclid 7 16 "x", "x ~ ~ x ~ x ~ x ~ ~ x ~ x ~ x ~"),
-                (euclid 9 16 "x", "x ~ x x ~ x ~ x ~ x x ~ x ~ x ~"),
-                (euclid 11 24 "x", "x ~ ~ x ~ x ~ x ~ x ~ x ~ ~ x ~ x ~ x ~ x ~ x ~"),
-                (euclid 13 24 "x", "x ~ x x ~ x ~ x ~ x ~ x ~ x x ~ x ~ x ~ x ~ x ~")
-              ] ::
-                [(Pattern String, String)]
-            )
+      describe "matches examples in Toussaint's paper" $ do
+        mapM_ (\(a, b) -> it b $ compareP (Arc 0 1) a (parseBP_E b))
+          ( [ (euclid 1 2 "x", "x ~"),
+              (euclid 1 3 "x", "x ~ ~"),
+              (euclid 1 4 "x", "x ~ ~ ~"),
+              (euclid 4 12 "x", "x ~ ~ x ~ ~ x ~ ~ x ~ ~"),
+              (euclid 2 5 "x", "x ~ x ~ ~"),
+              -- (euclid 3 4 "x", "x ~ x x"), -- Toussaint is wrong..
+              (euclid 3 4 "x", "x x x ~"), -- correction
+              (euclid 3 5 "x", "x ~ x ~ x"),
+              (euclid 3 7 "x", "x ~ x ~ x ~ ~"),
+              (euclid 3 8 "x", "x ~ ~ x ~ ~ x ~"),
+              (euclid 4 7 "x", "x ~ x ~ x ~ x"),
+              (euclid 4 9 "x", "x ~ x ~ x ~ x ~ ~"),
+              (euclid 4 11 "x", "x ~ ~ x ~ ~ x ~ ~ x ~"),
+              -- (euclid 5 6 "x", "x ~ x x x x"), -- Toussaint is wrong..
+              (euclid 5 6 "x", "x x x x x ~"), -- correction
+              (euclid 5 7 "x", "x ~ x x ~ x x"),
+              (euclid 5 8 "x", "x ~ x x ~ x x ~"),
+              (euclid 5 9 "x", "x ~ x ~ x ~ x ~ x"),
+              (euclid 5 11 "x", "x ~ x ~ x ~ x ~ x ~ ~"),
+              (euclid 5 12 "x", "x ~ ~ x ~ x ~ ~ x ~ x ~"),
+              -- (euclid 5 16 "x", "x ~ ~ x ~ ~ x ~ ~ x ~ ~ x ~ ~ ~ ~"),  -- Toussaint is wrong..
+              (euclid 5 16 "x", "x ~ ~ x ~ ~ x ~ ~ x ~ ~ x ~ ~ ~"), -- correction
+              -- (euclid 7 8 "x", "x ~ x x x x x x"), -- Toussaint is wrong..
+              (euclid 7 8 "x", "x x x x x x x ~"), -- Correction
+              (euclid 7 12 "x", "x ~ x x ~ x ~ x x ~ x ~"),
+              (euclid 7 16 "x", "x ~ ~ x ~ x ~ x ~ ~ x ~ x ~ x ~"),
+              (euclid 9 16 "x", "x ~ x x ~ x ~ x ~ x x ~ x ~ x ~"),
+              (euclid 11 24 "x", "x ~ ~ x ~ x ~ x ~ x ~ x ~ ~ x ~ x ~ x ~ x ~ x ~"),
+              (euclid 13 24 "x", "x ~ x x ~ x ~ x ~ x ~ x ~ x x ~ x ~ x ~ x ~ x ~")
+            ] ::
+              [(Pattern String, String)]
+          )
       it "can be called with a negative first value to give the inverse" $ do
         compareP
           (Arc 0 1)
diff --git a/test/Sound/Tidal/UtilsTest.hs b/test/Sound/Tidal/UtilsTest.hs
--- a/test/Sound/Tidal/UtilsTest.hs
+++ b/test/Sound/Tidal/UtilsTest.hs
@@ -3,52 +3,51 @@
 module Sound.Tidal.UtilsTest where
 
 import Sound.Tidal.Utils
-import Test.Microspec
+import Test.Hspec
 import Prelude hiding ((*>), (<*))
 
-run :: Microspec ()
+run :: Spec
 run =
   describe "Sound.Tidal.Utils" $ do
-    describe "delta" $ do
-      it "subtracts the second element of a tuple from the first" $ do
-        property $ delta (3, 10) === (7 :: Int)
+    it "subtracts the second element of a tuple from the first" $ do
+        delta (3, 10) `shouldBe` (7 :: Int)
 
-    describe "applies function to both elements of tuple" $ do
-      let res = mapBoth (+ 1) (2, 5)
-      property $ ((3, 6) :: (Int, Int)) === res
+    it "applies function to both elements of tuple" $ do
+        let res = mapBoth (+ 1) (2, 5)
+        res `shouldBe` ((3, 6) :: (Int, Int))
 
-    describe "apply function to first element of tuple" $ do
-      let res = mapFst (+ 1) (2, 5)
-      property $ ((3, 5) :: (Int, Int)) === res
+    it "apply function to first element of tuple" $ do
+        let res = mapFst (+ 1) (2, 5)
+        res `shouldBe` ((3, 5) :: (Int, Int))
 
-    describe "apply function to second element of tuple" $ do
-      let res = mapSnd (+ 1) (2, 5)
-      property $ ((2, 6) :: (Int, Int)) === res
+    it "apply function to second element of tuple" $ do
+        let res = mapSnd (+ 1) (2, 5)
+        res `shouldBe` ((2, 6) :: (Int, Int))
 
-    describe "return midpoint between first and second tuple value" $ do
-      let res = mid (2, 5)
-      property $ (3.5 :: Double) === res
+    it "return midpoint between first and second tuple value" $ do
+        let res = mid (2, 5)
+        res `shouldBe` (3.5 :: Double)
 
-    describe "return of two lists, with unique values to each list" $ do
-      let res = removeCommon [1, 2, 5, 7, 12, 16] [2, 3, 4, 5, 15, 16]
-      property $ (([1, 7, 12], [3, 4, 15]) :: ([Int], [Int])) === res
+    it "return of two lists, with unique values to each list" $ do
+        let res = removeCommon [1, 2, 5, 7, 12, 16] [2, 3, 4, 5, 15, 16]
+        res `shouldBe` (([1, 7, 12], [3, 4, 15]) :: ([Int], [Int]))
 
-    describe "wrap around indexing" $ do
-      let res = (!!!) [1 .. 5] 7
-      property $ (3 :: Int) === res
+    it "wrap around indexing" $ do
+        let res = (!!!) [1 .. 5] 7
+        res `shouldBe` (3 :: Int)
 
-    describe "safe list indexing" $ do
-      let res = nth 2 ([] :: [Int])
-      property $ Nothing === res
+    it "safe list indexing" $ do
+        let res = nth 2 ([] :: [Int])
+        res `shouldBe` Nothing 
 
-    describe "list accumulation with given list elements" $ do
-      let res = accumulate ([1 .. 5] :: [Int])
-      property $ [1, 3, 6, 10, 15] === res
+    it "list accumulation with given list elements" $ do
+        let res = accumulate ([1 .. 5] :: [Int])
+        res `shouldBe` [1, 3, 6, 10, 15]
 
-    describe "index elements in list" $ do
-      let res = enumerate ['a', 'b', 'c']
-      property $ [(0, 'a'), (1, 'b'), (2, 'c')] === res
+    it "index elements in list" $ do
+        let res = enumerate ['a', 'b', 'c']
+        res `shouldBe` [(0, 'a'), (1, 'b'), (2, 'c')]
 
-    describe "split list by given pred" $ do
-      let res = wordsBy (== ':') "bd:3"
-      property $ ["bd", "3"] === res
+    it "split list by given pred" $ do
+        let res = wordsBy (== ':') "bd:3"
+        res `shouldBe` ["bd", "3"]
diff --git a/test/Test.hs b/test/Test.hs
--- a/test/Test.hs
+++ b/test/Test.hs
@@ -11,10 +11,10 @@
 import Sound.Tidal.StepwiseTest
 import Sound.Tidal.UITest
 import Sound.Tidal.UtilsTest
-import Test.Microspec
+import Test.Hspec
 
 main :: IO ()
-main = microspec $ do
+main = hspec $ do
   Sound.Tidal.CoreTest.run
   Sound.Tidal.ParseTest.run
   Sound.Tidal.ParamsTest.run
diff --git a/test/TestUtils.hs b/test/TestUtils.hs
--- a/test/TestUtils.hs
+++ b/test/TestUtils.hs
@@ -1,34 +1,38 @@
 {-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE TypeSynonymInstances #-}
+{-# LANGUAGE InstanceSigs #-}
 
 module TestUtils where
 
 import Data.List (sort)
 import qualified Data.Map.Strict as Map
-import Data.Ratio as C
-import Sound.Tidal.Control as C
-import Sound.Tidal.Core as C
-import Sound.Tidal.Params as C
-import Sound.Tidal.ParseBP as C
-import Sound.Tidal.Pattern as C
-import Sound.Tidal.Scales as C
-import Sound.Tidal.Show as C
-import Sound.Tidal.Simple as C
-import Sound.Tidal.Stepwise as C
-import Sound.Tidal.UI as C
-import Prelude hiding ((*>), (<*))
-
-import Test.Microspec
+import Sound.Tidal.ParseBP (parseBP_E)
+import Sound.Tidal.Pattern
+  ( Arc,
+    ArcF (Arc),
+    Context (Context),
+    ControlPattern,
+    Event,
+    EventF (Event, value),
+    Pattern,
+    Value (VF, VI, VR, VS),
+    ValueMap,
+    defragParts,
+    queryArc,
+    setContext,
+  )
+import Sound.Tidal.Show ()
+import Test.Hspec (Expectation, shouldBe)
 import Prelude hiding ((*>), (<*))
 
 class TolerantEq a where
   (~==) :: a -> a -> Bool
 
 instance TolerantEq Double where
+  (~==) :: Double -> Double -> Bool
   a ~== b = abs (a - b) < 0.000001
 
 instance TolerantEq Value where
+  (~==) :: Value -> Value -> Bool
   (VS a) ~== (VS b) = a == b
   (VI a) ~== (VI b) = a == b
   (VR a) ~== (VR b) = a == b
@@ -36,22 +40,25 @@
   _ ~== _ = False
 
 instance (TolerantEq a) => TolerantEq [a] where
+  (~==) :: (TolerantEq a) => [a] -> [a] -> Bool
   as ~== bs = (length as == length bs) && all (uncurry (~==)) (zip as bs)
 
 instance TolerantEq ValueMap where
+  (~==) :: ValueMap -> ValueMap -> Bool
   a ~== b = Map.differenceWith (\a' b' -> if a' ~== b' then Nothing else Just a') a b == Map.empty
 
 instance TolerantEq (Event ValueMap) where
+  (~==) :: Event ValueMap -> Event ValueMap -> Bool
   (Event _ w p x) ~== (Event _ w' p' x') = w == w' && p == p' && x ~== x'
 
 -- | Compare the events of two patterns using the given arc
-compareP :: (Ord a, Show a) => Arc -> Pattern a -> Pattern a -> Property
+compareP :: (Ord a, Show a) => Arc -> Pattern a -> Pattern a -> Expectation
 compareP a p p' =
   sort (queryArc (stripContext p) a)
     `shouldBe` sort (queryArc (stripContext p') a)
 
 -- | Like @compareP@, but tries to 'defragment' the events
-comparePD :: (Ord a, Show a) => Arc -> Pattern a -> Pattern a -> Property
+comparePD :: (Ord a, Show a) => Arc -> Pattern a -> Pattern a -> Expectation
 comparePD a p p' =
   sort (defragParts $ queryArc (stripContext p) a)
     `shouldBe` sort (defragParts $ queryArc (stripContext p') a)
diff --git a/tidal-core.cabal b/tidal-core.cabal
--- a/tidal-core.cabal
+++ b/tidal-core.cabal
@@ -1,7 +1,7 @@
 cabal-version: 2.0
 
 name: tidal-core
-version: 1.9.6
+version: 1.10.0
 
 license: GPL-3
 copyright:           (c) Alex McLean and other contributors, 2025
@@ -45,7 +45,7 @@
 
   build-depends:
     base >=4.8 && <5,
-    containers <0.8,
+    containers <0.9,
     colour <2.4,
     text <2.2,
     parsec >=3.1.12 && <3.2,
@@ -74,7 +74,7 @@
 
   build-depends:
     base >=4 && <5,
-    microspec >=0.2.0.1,
+    hspec >=2.11.9,
     containers,
     tidal-core,
     deepseq
