diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Jonas Carpay (c) 2017
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Jonas Carpay nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+# apecs-gloss
+[![Hackage](https://img.shields.io/hackage/v/apecs-gloss.svg)](https://hackage.haskell.org/package/apecs-gloss)
+
+Tools for simple gloss-based rendering, split out to avoid unnecessary dependencies if you roll our own rendering
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/apecs-gloss.cabal b/apecs-gloss.cabal
new file mode 100644
--- /dev/null
+++ b/apecs-gloss.cabal
@@ -0,0 +1,37 @@
+name:                apecs-gloss
+version:             0.1.0
+synopsis:            Simple gloss renderer for apecs
+homepage:            https://github.com/jonascarpay/apecs-physics#readme
+license:             BSD3
+license-file:        LICENSE
+author:              Jonas Carpay
+maintainer:          jonascarpay@gmail.com
+copyright:           MIT
+category:            Web
+build-type:          Simple
+extra-source-files:  README.md
+cabal-version:       >= 1.10
+
+description:
+  Simple 2D gloss-based rendering for apecs.
+  Intended for prototyping.
+
+library
+  hs-source-dirs:
+    src
+  exposed-modules:
+    Apecs.Gloss
+    Apecs.Physics.Gloss
+  build-depends:
+    base >= 4.7 && < 5,
+    containers,
+    apecs,
+    apecs-physics,
+    linear,
+    gloss
+  default-language:
+    Haskell2010
+
+source-repository head
+  type: git
+  location: https://github.com/jonascarpay/apecs/apecs-gloss
diff --git a/src/Apecs/Gloss.hs b/src/Apecs/Gloss.hs
new file mode 100644
--- /dev/null
+++ b/src/Apecs/Gloss.hs
@@ -0,0 +1,69 @@
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE TypeFamilies     #-}
+
+module Apecs.Gloss
+  ( module Apecs.Gloss
+  , module G
+  ) where
+
+import           Data.Semigroup                   (Semigroup (..))
+import           Graphics.Gloss.Interface.IO.Game as G
+import           Linear.V2
+import           Linear.Vector
+
+import           Apecs
+
+data Camera = Camera
+  { camOffset :: V2 Double
+  , camScale  :: Double
+  } deriving (Eq, Show, Read)
+
+instance Semigroup Camera where
+  Camera x1 r1 <> Camera x2 r2 = Camera (x1 + x2) (r1 * r2)
+instance Monoid Camera where
+  mempty = Camera 0 1
+  mappend = (<>)
+
+-- | Apply a camera transformation to a picture
+cameraTransform :: Camera -> Picture -> Picture
+cameraTransform (Camera (V2 cx cy) cs) =
+    Scale (f cs) (f cs) .  Translate (f . negate $ cx) (f . negate $ cy)
+  where f = realToFrac
+
+windowToWorld :: Camera -> (Float,Float) -> V2 Double
+windowToWorld (Camera cx cs) (x,y) = v ^/ cs - cx
+  where v = V2 (realToFrac x) (realToFrac y)
+
+instance Component Camera where
+  type Storage Camera = Global Camera
+
+play
+  :: (Has w IO Camera)
+  => Display            -- ^ Display mode
+  -> Color              -- ^ Background color
+  -> Int                  -- ^ Desired FPS
+  -> System w Picture -- ^ Drawing function
+  -> (Event -> System w ()) -- ^ Event handling function
+  -> (Double  -> System w ()) -- ^ Stepping function, with a time delta argument.
+  -> System w ()
+play disp col fps draw handle step = do
+  w <- ask
+  liftIO$ playIO disp col fps w draw' handle' step'
+    where
+      handle' event = runSystem $ handle event >> ask
+      step'   dT    = runSystem $ step (realToFrac dT) >> ask
+      draw'         = runSystem $ do
+        cam <- get global
+        cameraTransform cam <$> draw
+
+-- | Renders a picture given a component rendering function.
+foldDraw :: (Get w IO c, Members w IO c)
+         => (c -> Picture) -- ^ Component render function.
+         -> System w Picture
+foldDraw fn = cfold (\pic -> mappend pic . fn) mempty
+
+-- | Monadically renders a picture given a component rendering function.
+foldDrawM :: (Get w IO c, Members w IO c)
+          => (c -> System w Picture) -- ^ Component render function.
+          -> System w Picture
+foldDrawM fn = cfoldM (\pic -> fmap (mappend pic) . fn) mempty
diff --git a/src/Apecs/Physics/Gloss.hs b/src/Apecs/Physics/Gloss.hs
new file mode 100644
--- /dev/null
+++ b/src/Apecs/Physics/Gloss.hs
@@ -0,0 +1,66 @@
+{-# LANGUAGE FlexibleContexts      #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE TypeFamilies          #-}
+
+module Apecs.Physics.Gloss
+  ( convexToPicture
+  , Transform, worldTransform, drawBody
+  , simulate
+  , module Apecs.Gloss
+  ) where
+
+import           Control.Monad                        (foldM)
+import           Data.Semigroup                       (Semigroup (..))
+import           Graphics.Gloss.Geometry.Angle        (radToDeg)
+import           Graphics.Gloss.Interface.IO.Simulate (simulateIO)
+
+import           Apecs
+import           Apecs.Physics
+
+import           Apecs.Gloss
+
+convexToPicture :: Convex -> Picture
+convexToPicture (Convex [V2 x y] radius) = Translate (realToFrac x) (realToFrac y) $ Circle (realToFrac radius)
+convexToPicture (Convex [a,b] _) = Line [v2ToTuple a, v2ToTuple b]
+convexToPicture (Convex verts _) = Polygon (v2ToTuple <$> verts)
+
+v2ToTuple :: V2 Double -> (Float, Float)
+v2ToTuple (V2 x y) = (realToFrac x, realToFrac y)
+
+type Transform = (Position, Angle)
+worldTransform :: Transform -> Picture -> Picture
+worldTransform (Position (V2 x y), Angle theta) =
+ Translate (realToFrac x) (realToFrac y) .
+ Rotate (negate . radToDeg . realToFrac $ theta)
+
+drawBody :: Has w IO Shape => (Body, Transform, ShapeList) -> System w Picture
+drawBody (btype, transform, ShapeList shapes) = color shColor . worldTransform transform <$> foldM foldfn mempty shapes
+  where foldfn pic shapeEty = do
+          Shape _ convex <- get shapeEty
+          return . mappend pic $ convexToPicture convex
+        shColor = case btype of
+                    DynamicBody   -> red
+                    KinematicBody -> green
+                    StaticBody    -> blue
+
+simulate
+  :: ( Has w IO Camera
+     , Has w IO Physics
+     )
+  => Display
+  -> System w ()
+simulate disp = do
+  w <- ask
+  liftIO $
+     simulateIO disp
+                black
+                60
+                w
+                (\_     -> runSystem draw w)
+                (\_ _ _ -> runSystem (stepPhysics (1/60)) w >> return w)
+
+  where
+    draw = do
+      pic <- foldDrawM drawBody
+      cam <- get global
+      return $ cameraTransform cam pic
