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
@@ -987,7 +987,7 @@
     distrib' (True : a) (x : b) = x : distrib' a b
     distrib' (False : a) b = False : distrib' a b
     layers = map bjorklund . (zip <*> drop 1)
-    boolsToPat a b' = flip const <$> filterValues (== True) (fastFromList a) <* b'
+    boolsToPat a b' = const id <$> filterValues id (fastFromList a) <* b'
 
 -- | @euclidInv@ fills in the blanks left by `euclid`, i.e., it inverts the
 -- pattern.
@@ -1184,7 +1184,7 @@
 segment = patternify _segment
 
 _segment :: Time -> Pattern a -> Pattern a
-_segment n p = setSteps (Just $ 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
@@ -1428,7 +1428,7 @@
 -- 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 = take n $ map fst $ L.iterate' (markovStep $ renorm) (xi, seed + delta)
+runMarkov n tp xi seed = take n $ map fst $ L.iterate' (markovStep renorm) (xi, seed + delta)
   where
     markovStep tp' (x, seed') = (let v = tp' IM.! x in findIndex (r * fst (Map.findMax v)) v, seed' + delta)
       where
@@ -2062,7 +2062,7 @@
 @
 -}
 rolledBy :: Pattern (Ratio Integer) -> Pattern a -> Pattern a
-rolledBy pt = patternify rolledWith (segment 1 $ pt)
+rolledBy pt = patternify rolledWith (segment 1 pt)
 
 rolledWith :: Ratio Integer -> Pattern a -> Pattern a
 rolledWith t = withEvents aux
@@ -2836,7 +2836,7 @@
     showStep :: [String] -> String
     showStep [] = "~"
     showStep [x] = x
-    showStep xs = "[" ++ (intercalate ", " xs) ++ "]"
+    showStep xs = "[" ++ intercalate ", " xs ++ "]"
     toList :: Pattern a -> [[a]]
     toList pat = map (\(s, e) -> map value $ queryArc (_segment n' pat) (Arc s e)) arcs
       where
@@ -2951,11 +2951,11 @@
 -- the same as "t f f f t f t f f f t f". That is, 12 steps per cycle,
 -- with true values alternating between every 4 and every 2 steps.
 necklace :: Rational -> [Int] -> Pattern Bool
-necklace perCycle xs = _slow ((toRational $ sum xs) / perCycle) $ listToPat $ list xs
+necklace perCycle xs = _slow (toRational (sum xs) / perCycle) $ listToPat $ list xs
   where
     list :: [Int] -> [Bool]
     list [] = []
-    list (x : xs') = (True : (replicate (x - 1) False)) ++ list xs'
+    list (x : xs') = (True : replicate (x - 1) False) ++ list xs'
 
 -- | Inserts chromatic notes into a pattern.
 --
@@ -2967,7 +2967,7 @@
 -- > d1 $ up (chromaticiseBy "0 1 2 -1" "[0 2] [3 6] [5 6 8] [3 1 0]") # s "superpiano"
 -- > d1 $ up "[0 2] [[3 4] [6 7]] [[5 6 7] [6 7 8] [8 9 10] [[3 2] [1 0] [0 -1]]" # s "superpiano"
 chromaticiseBy :: (Num a, Enum a, Ord a) => Pattern a -> Pattern a -> Pattern a
-chromaticiseBy n pat = innerJoin $ (\np -> _chromaticiseBy np pat) <$> n
+chromaticiseBy n pat = innerJoin $ (`_chromaticiseBy` pat) <$> n
 
 _chromaticiseBy :: (Num a, Enum a, Ord a) => a -> Pattern a -> Pattern a
 _chromaticiseBy n pat =
@@ -2986,3 +2986,28 @@
 -- | Alias for chromaticiseBy
 chromaticizeBy :: (Num a, Enum a, Ord a) => Pattern a -> Pattern a -> Pattern a
 chromaticizeBy = chromaticiseBy
+
+_ribbon :: Time -> Time -> Pattern a -> Pattern a
+_ribbon offset cycles pat = restart (_slow cycles $ pure True) $ offset `rotL` pat
+
+-- | Loops a pattern inside an `offset` for `cycles`. If you think of the entire
+-- span of time in cycles as a ribbon, you can cut a single piece and loop it.
+ribbon :: Pattern Time -> Pattern Time -> Pattern a -> Pattern a
+ribbon = patternify2 _ribbon
+
+-- | Shorthand for `ribbon`.
+rib :: Pattern Time -> Pattern Time -> Pattern a -> Pattern a
+rib = ribbon
+
+-- | Turns a pattern into a pattern of patterns, according to the structure of another given pattern.
+unjoin :: Pattern Bool -> Pattern b -> Pattern (Pattern b)
+unjoin pieces pat = withEvent snip pieces
+  where
+    -- If true, set value to the part of pattern looped at event boundaries
+    snip e@Event {value = True} = e {value = _ribbon (wholeStart e) (wholeStop e - wholeStart e) pat}
+    -- Otherwise, set value to unchanged pattern
+    snip e = e {value = pat}
+
+-- | Applies a function to subcycles of a pattern, as defined by the structure of another given pattern.
+into :: Pattern Bool -> (Pattern a -> Pattern b) -> Pattern a -> Pattern b
+into pieces func pat = innerJoin $ fmap func $ unjoin pieces pat
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
@@ -442,13 +442,13 @@
         compareP
           (Arc 0 1)
           (loopFirst $ rotL 3 $ slow 8 $ "0 .. 7" :: Pattern Int)
-          ("3")
+          "3"
 
     describe "timeLoop" $ do
       it "can loop time" $ do
         compareP
           (Arc 0 1)
-          ((3 <~) $ (timeLoop 3 $ sound "<a b c d>"))
+          ((3 <~) $ timeLoop 3 (sound "<a b c d>"))
           (sound "a")
 
     describe "timeLoop" $ do
@@ -463,11 +463,28 @@
         compareP
           (Arc 0 1)
           (necklace 12 [4, 2])
-          ("t f f f t f t f f f t f")
+          "t f f f t f t f f f t f"
 
     describe "quantise" $ do
       it "can quantise notes" $ do
         compareP
           (Arc 0 1)
-          (segment 2 $ quantise 1 $ sine :: Pattern Note)
+          (segment 2 $ quantise 1 sine :: Pattern Note)
           ("0 1" :: Pattern Note)
+    describe "unjoin" $ do
+      it "can unjoin and join as a no-op" $ do
+        compareP
+         (Arc 0 1)
+         (unwrap $ unjoin "t [t t]" ("a b c d" :: Pattern String))
+         "a b c d"
+    describe "into" $ do
+      it "can transform a looped part of a pattern" $ do
+        compareP (Arc 0 1)
+          (into "t [t t]" (fast 2) ("a b c d" :: Pattern String))
+          "a b a b c c d d"
+    describe "ribbon" $ do
+      it "can loop subcycles" $ do
+        compareP
+         (Arc 0 1)
+         (_ribbon 0.25 0.25 ("a b c d" :: Pattern String))
+         "b b b b"
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.10.0
+version: 1.10.1
 
 license: GPL-3
 copyright:           (c) Alex McLean and other contributors, 2025
@@ -81,4 +81,4 @@
 
 source-repository head
   type: git
-  location: https://github.com/tidalcycles/Tidal
+  location: https://codeberg.org/uzu/tidal
