diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -5,6 +5,9 @@
 The format is based on [Keep a Changelog](http://keepachangelog.com/) and the
 project adheres to the [Haskell Package Versioning
 Policy (PVP)](https://pvp.haskell.org)
+## 1.1.6.0 -- 2022-06-02
+
+ * Drop hgeometry dependency.
 
 ## 1.1.4.0 -- 2021-03-11
 
diff --git a/docs/gifs/doc_renderObject.gif b/docs/gifs/doc_renderObject.gif
new file mode 100644
Binary files /dev/null and b/docs/gifs/doc_renderObject.gif differ
diff --git a/reanimate.cabal b/reanimate.cabal
--- a/reanimate.cabal
+++ b/reanimate.cabal
@@ -3,7 +3,7 @@
 --  see http://haskell.org/cabal/users-guide/
 
 name:                reanimate
-version:             1.1.5.0
+version:             1.1.6.0
 -- synopsis:
 -- description:
 license:             PublicDomain
@@ -37,13 +37,6 @@
     Type:      git
     Location:  git://github.com/lemmih/reanimate.git
 
-flag no-hgeometry
-  description:
-    Disable all features that depend on hgeometry. Should only
-    be used when developing new hgeometry algorithms.
-  default: False
-  manual: True
-
 library
   hs-source-dirs:     src
   if os(windows)
@@ -113,12 +106,7 @@
                       Detach
                       POVRay
   autogen-modules:    Paths_reanimate
-  if flag(no-hgeometry)
-    cpp-options: -DNO_HGEOMETRY
-  else
-    build-depends:
-      hgeometry               >=0.11.0.0,
-      hgeometry-combinatorial >=0.11.0.0
+  cpp-options: -DNO_HGEOMETRY
   build-depends:
     base                 >=4.10 && <5,
     JuicyPixels          >=3.3.3,
@@ -178,6 +166,7 @@
     Helpers
   hs-source-dirs: test
   ghc-options: -w
+  cpp-options: -DNO_HGEOMETRY
   build-depends:
     base,
     directory,
diff --git a/src/Reanimate.hs b/src/Reanimate.hs
--- a/src/Reanimate.hs
+++ b/src/Reanimate.hs
@@ -105,12 +105,14 @@
   , spriteDuration    -- :: Frame s Duration
   , signalS           -- :: Sprite s -> Duration -> Signal -> Scene s ()
   , newSprite         -- :: Frame s SVG -> Scene s (Sprite s)
+  , newSpritePart     -- :: Frame s SVG -> Scene s (Sprite s)
   , newSprite_        -- :: Frame s SVG -> Scene s ()
   , newSpriteA        -- :: Animation -> Scene s (Sprite s)
   , newSpriteA'       -- :: Sync -> Animation -> Scene s (Sprite s)
   , newSpriteSVG      -- :: SVG -> Scene s (Sprite s)
   , newSpriteSVG_     -- :: SVG -> Scene s ()
   , destroySprite     -- :: Sprite s -> Scene s ()
+  , renderSprite      -- :: Sprite s -> Frame s SVG
   , applyVar          -- :: Var s a -> Sprite s -> (a -> SVG -> SVG) -> Scene s ()
   , spriteModify      -- :: Sprite s -> Frame s ((SVG,ZIndex) -> (SVG, ZIndex)) -> Scene s ()
   , spriteMap         -- :: Sprite s -> (SVG -> SVG) -> Scene s ()
diff --git a/src/Reanimate/PolyShape.hs b/src/Reanimate/PolyShape.hs
--- a/src/Reanimate/PolyShape.hs
+++ b/src/Reanimate/PolyShape.hs
@@ -41,7 +41,7 @@
   , plGroupTouching
   ) where
 
-import           Control.Lens                                         ((&), (.~), (^.))
+import           Control.Lens                                         ((&), (.~))
 import           Data.List                                            (nub, partition, sortOn)
 import qualified Data.Vector                                          as V
 import           Geom2D.CubicBezier.Linear                            (ClosedPath (..),
@@ -67,6 +67,7 @@
 import           Reanimate.Svg
 
 #if !defined(NO_HGEOMETRY)
+import           Control.Lens                                         ((^.))
 import           Algorithms.Geometry.PolygonTriangulation.Triangulate (triangulate')
 
 import           Data.Ext
diff --git a/src/Reanimate/Scene.hs b/src/Reanimate/Scene.hs
--- a/src/Reanimate/Scene.hs
+++ b/src/Reanimate/Scene.hs
@@ -39,12 +39,14 @@
     spriteT, -- :: Frame s Time
     spriteDuration, -- :: Frame s Duration
     newSprite, -- :: Frame s SVG -> Scene s (Sprite s)
+    newSpritePart, -- :: Frame s SVG -> Scene s (Sprite s)
     newSprite_, -- :: Frame s SVG -> Scene s ()
     newSpriteA, -- :: Animation -> Scene s (Sprite s)
     newSpriteA', -- :: Sync -> Animation -> Scene s (Sprite s)
     newSpriteSVG, -- :: SVG -> Scene s (Sprite s)
     newSpriteSVG_, -- :: SVG -> Scene s ()
     destroySprite, -- :: Sprite s -> Scene s ()
+    renderSprite, -- :: Sprite s -> Frame s SVG
     applyVar, -- :: Var s a -> Sprite s -> (a -> SVG -> SVG) -> Scene s ()
     spriteModify, -- :: Sprite s -> Frame s ((SVG,ZIndex) -> (SVG, ZIndex)) -> Scene s ()
     spriteMap, -- :: Sprite s -> (SVG -> SVG) -> Scene s ()
@@ -60,6 +62,8 @@
     signalO,
     oNew,
     newObject,
+    newObjectPart,
+    renderObject,
     oModify,
     oModifyS,
     oRead,
diff --git a/src/Reanimate/Scene/Object.hs b/src/Reanimate/Scene/Object.hs
--- a/src/Reanimate/Scene/Object.hs
+++ b/src/Reanimate/Scene/Object.hs
@@ -18,7 +18,7 @@
 import           Reanimate.Svg
 
 import           Reanimate.Scene.Core   (Scene, fork, scene, wait)
-import           Reanimate.Scene.Sprite (Sprite, newSprite, newSpriteA', play, spriteModify, unVar, signalS)
+import           Reanimate.Scene.Sprite (Frame, Sprite, addPartToScene, newSpritePart, newSpriteA', play, spriteModify, unVar, signalS, renderSprite)
 import           Reanimate.Scene.Var    (Var, modifyVar, newVar, readVar, tweenVar)
 
 -------------------------------------------------------
@@ -290,6 +290,15 @@
 -- | Create new object.
 newObject :: Renderable a => a -> Scene s (Object s a)
 newObject val = do
+  obj <- newObjectPart val
+  addPartToScene $ objectSprite obj
+  return obj
+
+-- | Create new object, but not showing as part of the scene, Such
+--   objects can be used hierachically within the definitions of
+--   sprites, via the function renderObject.
+newObjectPart :: Renderable a => a -> Scene s (Object s a)
+newObjectPart val = do
   ref <-
     newVar
       ObjectData
@@ -306,7 +315,7 @@
           _oScale = 1,
           _oScaleOrigin = V2 0 0
         }
-  sprite <- newSprite $ do
+  sprite <- newSpritePart $ do
     ~obj@ObjectData {..} <- unVar ref
     pure $
       if _oShown
@@ -326,6 +335,32 @@
       }
   where
     svg = toSVG val
+
+-- | Create a frame context from an object for use within a sprite definition.
+--
+--   Example:
+--
+-- @
+-- do opacityVar <- 'newVar' 1
+--    textObj1 <- 'newObjectPart' $ 'scale' 3 $ 'center' $ 'latex' "Fade"
+--    'oModify' textObj1 $ 'oContext' .~ 'withFillColor' "red"
+--    textObj2 <- 'newObjectPart' $ 'scale' 3 $ 'center' $ 'latex' "Overlap"
+--    'oModify' textObj2 $ 'oContext' .~ 'withFillColor' "blue"
+--    sprite <- 'newSprite' $ do
+--      opacity <- 'unVar' opacityVar
+--      text1 <- 'renderObject' textObj1
+--      text2 <- 'renderObject' textObj2
+--      'return' $ 'withGroupOpacity' opacity $ 'mkGroup' [text1, text2]
+--    'fork' $ 'oShowWith' textObj1 $ 'setDuration' 1 . 'oDraw'
+--    'wait' 0.2
+--    'fork' $ 'oShowWith' textObj2 $ 'setDuration' 1 . 'oDraw'
+--    'wait' 0.2
+--    'tweenVar' opacityVar 1 $ 'const' $ 'fromToS' 1 0
+-- @
+--
+-- <<docs/gifs/doc_renderObject.gif>>
+renderObject :: Object s a -> Frame s SVG
+renderObject obj = renderSprite $ objectSprite obj
 
 oScaleApply :: ObjectData a -> (SVG -> SVG)
 oScaleApply ObjectData {..} =
diff --git a/src/Reanimate/Scene/Sprite.hs b/src/Reanimate/Scene/Sprite.hs
--- a/src/Reanimate/Scene/Sprite.hs
+++ b/src/Reanimate/Scene/Sprite.hs
@@ -12,7 +12,7 @@
 import           Reanimate.Animation  (Animation, Duration, SVG, Sync (SyncStretch), Time, dropA,
                                        duration, getAnimationFrame)
 import           Reanimate.Effect     (Effect, delayE)
-import           Reanimate.Scene.Core (Scene (M), ZIndex, addGen, fork, liftST, queryNow, scene,
+import           Reanimate.Scene.Core (Gen, Scene (M), ZIndex, addGen, fork, liftST, queryNow, scene,
                                        wait)
 import           Reanimate.Scene.Var  (Var (..), newVar, readVar, unpackVar)
 import           Reanimate.Transition (Transition, overlapT)
@@ -58,22 +58,22 @@
 
 -- | Sprites are animations with a given time of birth as well as a time of death.
 --   They can be controlled using variables, tweening, and effects.
-data Sprite s = Sprite Time (STRef s (Time -> Time)) (STRef s (Duration, ST s (Duration -> Time -> SVG -> (SVG, ZIndex))))
+data Sprite s = Sprite Time (STRef s (Time -> Time)) (STRef s (Duration, ST s (Duration -> Duration -> Time -> SVG -> (SVG, ZIndex)))) (Gen s)
 
 -- | Sprite frame generator. Generates frames over time in a stateful environment.
-newtype Frame s a = Frame {unFrame :: ST s (Time -> Duration -> Time -> a)}
+newtype Frame s a = Frame {unFrame :: ST s (Duration -> Time -> Duration -> Time -> a)}
 
 instance Functor (Frame s) where
   fmap fn (Frame gen) = Frame $ do
     m <- gen
-    return (\real_t d t -> fn $ m real_t d t)
+    return (\scene_d real_t d t -> fn $ m scene_d real_t d t)
 
 instance Applicative (Frame s) where
-  pure v = Frame $ return (\_ _ _ -> v)
+  pure v = Frame $ return (\_ _ _ _ -> v)
   Frame f <*> Frame g = Frame $ do
     m1 <- f
     m2 <- g
-    return $ \real_t d t -> m1 real_t d t (m2 real_t d t)
+    return $ \scene_d real_t d t -> m1 scene_d real_t d t (m2 scene_d real_t d t)
 
 -- | Dereference a variable as a Sprite frame.
 --
@@ -90,15 +90,15 @@
 unVar :: Var s a -> Frame s a
 unVar var = Frame $ do
   fn <- unpackVar var
-  return $ \real_t _d _t -> fn real_t
+  return $ \_scene_d real_t _d _t -> fn real_t
 
 -- | Dereference seconds since sprite birth.
 spriteT :: Frame s Time
-spriteT = Frame $ return (\_real_t _d t -> t)
+spriteT = Frame $ return (\_scene_d _real_t _d t -> t)
 
 -- | Dereference duration of the current sprite.
 spriteDuration :: Frame s Duration
-spriteDuration = Frame $ return (\_real_t d _t -> d)
+spriteDuration = Frame $ return (\_scene_d _real_t d _t -> d)
 
 -- | Create new sprite defined by a frame generator. Unless otherwise specified using
 --   'destroySprite', the sprite will die at the end of the scene.
@@ -113,17 +113,26 @@
 --   <<docs/gifs/doc_newSprite.gif>>
 newSprite :: Frame s SVG -> Scene s (Sprite s)
 newSprite render = do
+  s <- newSpritePart render
+  addPartToScene s
+  return s
+
+-- | Create a new sprite defined by a frame generator, but not showing
+--   as part of the scene. Such sprites can be used hierarchically within
+--   the definitions of other sprites, via the function renderSprite.
+newSpritePart :: Frame s SVG -> Scene s (Sprite s)
+newSpritePart render = do
   now <- queryNow
   tmod <- liftST $ newSTRef id
-  ref <- liftST $ newSTRef (-1, return $ \_d _t svg -> (svg, 0))
-  addGen $ do
+  ref <- liftST $ newSTRef (-1, return $ \_ad _d _t svg -> (svg, 0))
+  return $ Sprite now tmod ref $ do
     fn <- unFrame render
     time_fn <- readSTRef tmod
     (spriteDur, spriteEffectGen) <- readSTRef ref
     spriteEffect <- spriteEffectGen
-    return $ \d absT_ ->
+    return $ \absD absT_ ->
       let absT = time_fn absT_
-          relD = (if spriteDur < 0 then d else spriteDur) - now
+          relD = (if spriteDur < 0 then absD else spriteDur) - now
           relT = absT - now
           -- Sprite is live [now;duration[
           -- If we're at the end of a scene, sprites
@@ -131,12 +140,14 @@
           -- This behavior is difficult to get right. See the 'bug_*' examples for
           -- automated tests.
           inTimeSlice = relT >= 0 && relT < relD
-          isLastFrame = d == absT && relT == relD
-       in if inTimeSlice || isLastFrame
-            then spriteEffect relD relT (fn absT relD relT)
+          isLastFrame = absD == absT && relT == relD
+      in if inTimeSlice || isLastFrame
+            then spriteEffect absD relD relT (fn absD absT relD relT)
             else (None, 0)
-  return $ Sprite now tmod ref
 
+addPartToScene :: Sprite s -> Scene s ()
+addPartToScene (Sprite _ _ _ gen) = addGen gen
+
 -- | Create new sprite defined by a frame generator. The sprite will die at
 --   the end of the scene.
 newSprite_ :: Frame s SVG -> Scene s ()
@@ -197,6 +208,15 @@
 newSpriteSVG_ :: SVG -> Scene s ()
 newSpriteSVG_ = void . newSpriteSVG
 
+-- | Create a frame context from the source sprite for use within another. Use of this
+--   function allows several sprites to be combined into a single one, with the
+--   method of combination controled by variables.
+renderSprite :: Sprite s -> Frame s SVG
+renderSprite (Sprite _ _ _ gen) =
+  Frame $ do
+            genFn <- gen
+            return (\absD absT _ _ -> fst $ genFn absD absT)
+
 -- | Change the rendering of a sprite using data from a variable. If data from several variables
 --   is needed, use a frame generator instead.
 --
@@ -228,7 +248,7 @@
 --
 --   <<docs/gifs/doc_destroySprite.gif>>
 destroySprite :: Sprite s -> Scene s ()
-destroySprite (Sprite _ _tmod ref) = do
+destroySprite (Sprite _ _tmod ref _) = do
   now <- queryNow
   liftST $
     modifySTRef ref $ \(ttl, render) ->
@@ -236,19 +256,19 @@
 
 -- | Low-level frame modifier.
 spriteModify :: Sprite s -> Frame s ((SVG, ZIndex) -> (SVG, ZIndex)) -> Scene s ()
-spriteModify (Sprite born _tmod ref) modFn = liftST $
+spriteModify (Sprite born _tmod ref _) modFn = liftST $
   modifySTRef ref $ \(ttl, renderGen) ->
     ( ttl,
       do
         render <- renderGen
         modRender <- unFrame modFn
-        return $ \relD relT ->
-          let absT = relT + born in modRender absT relD relT . render relD relT
+        return $ \absD relD relT ->
+          let absT = relT + born in modRender absD absT relD relT . render absD relD relT
     )
 
 -- | Apply easing function before rendering sprite.
 signalS :: Sprite s -> Duration -> Signal -> Scene s ()
-signalS (Sprite _born tmod _ref) dur signal = do
+signalS (Sprite _born tmod _ref _) dur signal = do
   now <- queryNow
   let modify_t t
         | t < now     = t
@@ -271,7 +291,7 @@
 --
 --   <<docs/gifs/doc_spriteMap.gif>>
 spriteMap :: Sprite s -> (SVG -> SVG) -> Scene s ()
-spriteMap sprite@(Sprite born _ _) fn = do
+spriteMap sprite@(Sprite born _ _ _) fn = do
   now <- queryNow
   let tDelta = now - born
   spriteModify sprite $ do
@@ -289,7 +309,7 @@
 --
 --   <<docs/gifs/doc_spriteTween.gif>>
 spriteTween :: Sprite s -> Duration -> (Double -> SVG -> SVG) -> Scene s ()
-spriteTween sprite@(Sprite born _ _) dur fn = do
+spriteTween sprite@(Sprite born _ _ _) dur fn = do
   now <- queryNow
   let tDelta = now - born
   spriteModify sprite $ do
@@ -331,15 +351,15 @@
 --
 --   <<docs/gifs/doc_spriteE.gif>>
 spriteE :: Sprite s -> Effect -> Scene s ()
-spriteE (Sprite born _tmod ref) effect = do
+spriteE (Sprite born _tmod ref _) effect = do
   now <- queryNow
   liftST $
     modifySTRef ref $ \(ttl, renderGen) ->
       ( ttl,
         do
           render <- renderGen
-          return $ \d t svg ->
-            let (svg', z) = render d t svg
+          return $ \ad d t svg ->
+            let (svg', z) = render ad d t svg
              in (delayE (max 0 $ now - born) effect d t svg', z)
       )
 
@@ -357,15 +377,15 @@
 --
 --   <<docs/gifs/doc_spriteZ.gif>>
 spriteZ :: Sprite s -> ZIndex -> Scene s ()
-spriteZ (Sprite born _tmod ref) zindex = do
+spriteZ (Sprite born _tmod ref _) zindex = do
   now <- queryNow
   liftST $
     modifySTRef ref $ \(ttl, renderGen) ->
       ( ttl,
         do
           render <- renderGen
-          return $ \d t svg ->
-            let (svg', z) = render d t svg in (svg', if t < now - born then z else zindex)
+          return $ \ad d t svg ->
+            let (svg', z) = render ad d t svg in (svg', if t < now - born then z else zindex)
       )
 
 -- | Destroy all local sprites at the end of a scene.
diff --git a/test/Properties.hs b/test/Properties.hs
--- a/test/Properties.hs
+++ b/test/Properties.hs
@@ -1,13 +1,17 @@
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE FlexibleInstances    #-}
 {-# OPTIONS_GHC -w      #-}
 {-# LANGUAGE TemplateHaskell      #-}
 module Properties where
 
+import           Test.Tasty
+
+#if !defined(NO_HGEOMETRY)
+
 import qualified Data.Vector            as V
 import           Linear.V2
 import           Linear.Vector
 import           Test.QuickCheck
-import           Test.Tasty
 import           Test.Tasty.QuickCheck
 
 import           Reanimate.Math.Common
@@ -18,6 +22,8 @@
 
 import Debug.Trace
 
+
+
 prop_pGenerate (PolyParam a) (PolyParam b) (PolyParam c) (PolyParam d) =
   pIsSimple $ pGenerate [a,b,c,d]
 
@@ -87,3 +93,7 @@
 return []
 all_props :: TestTree
 all_props = testProperties "properties" $allProperties
+#else
+all_props :: TestTree
+all_props = testGroup "properties" []
+#endif
