diff --git a/Graphics/GL/Low/Blending.hs b/Graphics/GL/Low/Blending.hs
new file mode 100644
--- /dev/null
+++ b/Graphics/GL/Low/Blending.hs
@@ -0,0 +1,127 @@
+-- | When blending is enabled, colors written to the color buffer will be
+-- blended using a formula with the color already there. The three options
+-- for the formula are:
+--
+-- - S*s + D*d ('FuncAdd', the default)
+-- - S*s - D*d ('FuncSubtract')
+-- - D*d - S*s ('FuncReverseSubtract')
+--
+-- where S and D are source and destination color components respectively. The
+-- factors s and d are computed blending factors which can depend on the alpha
+-- component of the source pixel, the destination pixel, or a specified
+-- constant color. See 'basicBlending' for a common choice.
+--
+-- The order of rendering matters when using blending. The farther-away
+-- primitives should be rendered first to get transparent materials to look
+-- right. This means a depth test is unhelpful when using blending. Also
+-- blending many layers of transparent primitives can significantly degrade
+-- performance. For these reasons transparency effects may be better
+-- accomplished with an off-screen rendering pass followed by a suitable
+-- shader.
+--
+-- @
+-- blending example program here
+-- @
+
+module Graphics.GL.Low.Blending where
+
+import Data.Default
+import Graphics.GL
+
+import Graphics.GL.Low.Classes
+
+-- | Enable blending with the specified blending parameters.
+enableBlending :: Blending -> IO ()
+enableBlending (Blending s d f (r,g,b,a)) = do
+  glBlendFunc (toGL s) (toGL d)
+  glBlendEquation (toGL f)
+  let c = realToFrac
+  glBlendColor (c r) (c g) (c b) (c a)
+  glEnable GL_BLEND
+
+-- | Disable alpha blending.
+disableBlending :: IO ()
+disableBlending = glDisable GL_BLEND
+
+-- | This blending configuration is suitable for ordinary alpha blending
+-- transparency effects.
+--
+-- @
+-- Blending
+--   { sFactor   = BlendSourceAlpha
+--   , dFactor   = BlendOneMinusSourceAlpha
+--   , blendFunc = FuncAdd }
+-- @
+basicBlending :: Blending
+basicBlending = def
+  { sFactor = BlendSourceAlpha
+  , dFactor = BlendOneMinusSourceAlpha }
+
+
+-- | Blending parameters.
+data Blending = Blending
+  { sFactor :: BlendFactor
+  , dFactor :: BlendFactor
+  , blendFunc :: BlendEquation
+  , blendColor :: (Float,Float,Float,Float) }
+
+-- | The default blending parameters have no effect if enabled. The result
+-- will be no blending effect.
+instance Default Blending where
+  def = Blending
+    { sFactor = BlendOne
+    , dFactor = BlendZero
+    , blendFunc = FuncAdd
+    , blendColor = (0,0,0,0) }
+
+-- | Blending functions.
+data BlendEquation =
+  FuncAdd | -- ^ the default
+  FuncSubtract |
+  FuncReverseSubtract
+    deriving Show
+
+instance Default BlendEquation where
+  def = FuncAdd
+
+instance ToGL BlendEquation where
+  toGL FuncAdd = GL_FUNC_ADD
+  toGL FuncSubtract = GL_FUNC_SUBTRACT
+  toGL FuncReverseSubtract = GL_FUNC_REVERSE_SUBTRACT
+
+
+-- | Blending factors.
+data BlendFactor =
+  BlendOne |
+  BlendZero |
+  BlendSourceColor |
+  BlendOneMinusSourceColor |
+  BlendDestColor |
+  BlendOneMinusDestColor |
+  BlendSourceAlpha |
+  BlendOneMinusSourceAlpha |
+  BlendDestAlpha |
+  BlendOneMinusDestAlpha |
+  BlendConstantColor |
+  BlendOneMinusConstantColor |
+  BlendConstantAlpha |
+  BlendOneMinusConstantAlpha
+    deriving Show
+
+instance ToGL BlendFactor where
+  toGL BlendOne = GL_ONE
+  toGL BlendZero = GL_ZERO
+  toGL BlendSourceColor = GL_SRC_COLOR
+  toGL BlendOneMinusSourceColor = GL_ONE_MINUS_SRC_COLOR
+  toGL BlendDestColor = GL_DST_COLOR
+  toGL BlendOneMinusDestColor = GL_ONE_MINUS_DST_COLOR
+  toGL BlendSourceAlpha = GL_SRC_ALPHA
+  toGL BlendOneMinusSourceAlpha = GL_ONE_MINUS_SRC_ALPHA
+  toGL BlendDestAlpha = GL_DST_ALPHA
+  toGL BlendOneMinusDestAlpha = GL_ONE_MINUS_DST_ALPHA
+  toGL BlendConstantColor = GL_ONE_MINUS_CONSTANT_COLOR
+  toGL BlendOneMinusConstantColor = GL_ONE_MINUS_CONSTANT_COLOR
+  toGL BlendConstantAlpha = GL_CONSTANT_ALPHA
+  toGL BlendOneMinusConstantAlpha = GL_ONE_MINUS_CONSTANT_ALPHA
+
+
diff --git a/Graphics/GL/Low/BufferObject.hs b/Graphics/GL/Low/BufferObject.hs
new file mode 100644
--- /dev/null
+++ b/Graphics/GL/Low/BufferObject.hs
@@ -0,0 +1,123 @@
+-- | VBO and ElementArrays. Both are buffer objects but are used for two
+-- different things.
+module Graphics.GL.Low.BufferObject (
+  VBO,
+  ElementArray,
+  UsageHint(..),
+  newVBO,
+  updateVBO,
+  bindVBO,
+  newElementArray,
+  updateElementArray,
+  bindElementArray,
+  deleteBufferObject
+) where
+
+import Foreign.Ptr
+import Foreign.Marshal
+import Foreign.Storable
+import qualified Data.Vector.Storable as V
+import Data.Vector.Storable (Vector)
+import Data.Word
+
+import Graphics.GL
+
+import Graphics.GL.Low.Classes
+
+-- | A VBO is a buffer object which has vertex data. Shader programs use VBOs
+-- as input to their vertex attributes according to the configuration of the
+-- bound VAO.
+data VBO = VBO GLuint deriving Show
+
+-- | A buffer object which has a packed sequence of vertex indices. Indexed
+-- rendering uses the ElementArray bound to the element array binding target.
+data ElementArray = ElementArray GLuint deriving Show
+
+-- | Usage hint for allocation of buffer object storage.
+data UsageHint = StaticDraw  -- ^ Data will seldomly change.
+               | DynamicDraw -- ^ Data will change.
+               | StreamDraw  -- ^ Data will change very often.
+                 deriving Show
+
+instance ToGL UsageHint where
+  toGL StaticDraw  = GL_STATIC_DRAW
+  toGL DynamicDraw = GL_DYNAMIC_DRAW
+  toGL StreamDraw  = GL_STREAM_DRAW
+
+instance GLObject VBO where
+  glObjectName (VBO n) = fromIntegral n
+
+instance GLObject ElementArray where
+  glObjectName (ElementArray n) = fromIntegral n
+
+instance BufferObject VBO
+
+instance BufferObject ElementArray
+
+
+-- | Create a buffer object from a blob of bytes. The usage argument hints
+-- at how often you will modify the data.
+newVBO :: Vector Word8 -> UsageHint -> IO VBO
+newVBO src usage = do
+  n <- alloca (\ptr -> glGenBuffers 1 ptr >> peek ptr)
+  let len = V.length src
+  glBindBuffer GL_ARRAY_BUFFER n
+  V.unsafeWith src $ \ptr -> glBufferData
+    GL_ARRAY_BUFFER
+    (fromIntegral len)
+    (castPtr ptr)
+    (toGL usage)
+  return (VBO n)
+
+-- | Delete a VBO or ElementArray.
+deleteBufferObject :: BufferObject a => a -> IO ()
+deleteBufferObject bo = withArray [glObjectName bo] (\ptr -> glDeleteBuffers 1 ptr)
+
+-- | Modify the data in the currently bound VBO starting from the specified
+-- index in bytes.
+updateVBO :: Vector Word8 -> Int -> IO ()
+updateVBO src offset = do
+  let len = V.length src
+  V.unsafeWith src $ \ptr -> glBufferSubData
+    GL_ARRAY_BUFFER 
+    (fromIntegral offset)
+    (fromIntegral len)
+    (castPtr ptr)
+
+-- | Bind a VBO to the array buffer binding target. The buffer object bound
+-- there will be replaced, if any.
+bindVBO :: VBO -> IO ()
+bindVBO (VBO n) = glBindBuffer GL_ARRAY_BUFFER n
+
+
+-- | Create a new ElementArray buffer object from the blob of packed indices.
+-- The usage argument hints at how often you plan to modify the data.
+newElementArray :: Vector Word8 -> UsageHint -> IO ElementArray
+newElementArray bytes usage = do
+  n <- alloca (\ptr -> glGenBuffers 1 ptr >> peek ptr)
+  glBindBuffer GL_ELEMENT_ARRAY_BUFFER n
+  let len = V.length bytes
+  V.unsafeWith bytes $ \ptr -> do
+    glBufferData
+      GL_ELEMENT_ARRAY_BUFFER
+      (fromIntegral len)
+      (castPtr ptr)
+      (toGL usage)
+  return (ElementArray n)
+
+-- | Modify contents in the currently bound ElementArray starting at the
+-- specified index in bytes.
+updateElementArray :: Vector Word8 -> Int -> IO ()
+updateElementArray bytes offset = V.unsafeWith bytes $ \ptr -> do
+  glBufferSubData
+    GL_ELEMENT_ARRAY_BUFFER
+    (fromIntegral offset)
+    (fromIntegral (V.length bytes))
+    (castPtr ptr)
+
+
+-- | Assign an ElementArray to the element array binding target. It will
+-- replace the ElementArray already bound there, if any. Note that the state
+-- of the element array binding target is a function of the current VAO.
+bindElementArray :: ElementArray -> IO ()
+bindElementArray (ElementArray n) = glBindBuffer GL_ELEMENT_ARRAY_BUFFER n
diff --git a/Graphics/GL/Low/Classes.hs b/Graphics/GL/Low/Classes.hs
new file mode 100644
--- /dev/null
+++ b/Graphics/GL/Low/Classes.hs
@@ -0,0 +1,31 @@
+module Graphics.GL.Low.Classes where
+
+import Graphics.GL
+
+-- | OpenGL internal image formats.
+class InternalFormat a where
+  internalFormat :: (Eq b, Num b) => proxy a -> b
+
+-- | The allowed attachment point for images with an internal format.
+class InternalFormat a => Attachable a where
+  attachPoint :: (Eq b, Num b) => proxy a -> b
+
+-- | Textures are GL objects.
+class GLObject a => Texture a where
+
+-- | Framebuffers can be bound to the framebuffer binding target. There is
+-- a default framebuffer and the client may create an arbitrary number of
+-- new framebuffer objects.
+class Framebuffer a where
+  framebufferName :: Num b => a -> b
+
+class GLObject a => BufferObject a where
+
+-- | Mappable to GL enums.
+class ToGL a where
+  toGL :: (Num b, Eq b) => a -> b
+
+-- | All GL objects have some numeric name.
+class GLObject a where
+  glObjectName :: Num b => a -> b
+
diff --git a/Graphics/GL/Low/Color.hs b/Graphics/GL/Low/Color.hs
new file mode 100644
--- /dev/null
+++ b/Graphics/GL/Low/Color.hs
@@ -0,0 +1,19 @@
+module Graphics.GL.Low.Color where
+
+import Graphics.GL
+
+-- | Allow rendering commands to modify the color buffer of the current
+-- framebuffer.
+enableColorWriting :: IO ()
+enableColorWriting = glColorMask GL_TRUE GL_TRUE GL_TRUE GL_TRUE
+
+-- | Disable rendering to color buffer.
+disableColorWriting :: IO ()
+disableColorWriting = glColorMask GL_FALSE GL_FALSE GL_FALSE GL_FALSE
+
+-- | Clear the color buffer of the current framebuffer with the specified
+-- color. Has no effect if writing to the color buffer is disabled.
+clearColorBuffer :: (Float, Float, Float) -> IO ()
+clearColorBuffer (r, g, b) = do
+  glClearColor (realToFrac r) (realToFrac g) (realToFrac b) 1.0
+  glClear GL_COLOR_BUFFER_BIT
diff --git a/Graphics/GL/Low/Cube.hs b/Graphics/GL/Low/Cube.hs
new file mode 100644
--- /dev/null
+++ b/Graphics/GL/Low/Cube.hs
@@ -0,0 +1,41 @@
+{-# LANGUAGE Rank2Types #-}
+{-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE DeriveFoldable #-}
+{-# LANGUAGE DeriveTraversable #-}
+module Graphics.GL.Low.Cube where
+
+import Data.Functor
+import Data.Foldable
+import Control.Applicative
+import Data.Traversable
+import Data.Monoid
+
+-- | Six values, one on each side.
+data Cube a = Cube
+  { cubeRight  :: a
+  , cubeLeft   :: a
+  , cubeTop    :: a
+  , cubeBottom :: a
+  , cubeFront  :: a
+  , cubeBack   :: a }
+    deriving (Show, Functor, Foldable, Traversable)
+
+-- | A type to pick one of the sides of a cube. See the accessors of the
+-- type 'Cube'.
+type Side = forall a . Cube a -> a
+
+instance Applicative Cube where
+  pure x = Cube x x x x x x
+  (Cube f1 f2 f3 f4 f5 f6) <*> (Cube x1 x2 x3 x4 x5 x6) =
+    Cube (f1 x1) (f2 x2) (f3 x3) (f4 x4) (f5 x5) (f6 x6)
+
+instance Monoid a => Monoid (Cube a) where
+  mempty = Cube mempty mempty mempty mempty mempty mempty
+  (Cube x1 x2 x3 x4 x5 x6) `mappend` (Cube y1 y2 y3 y4 y5 y6) = Cube
+    (x1 <> y1)
+    (x2 <> y2)
+    (x3 <> y3)
+    (x4 <> y4)
+    (x5 <> y5)
+    (x6 <> y6)
+  
diff --git a/Graphics/GL/Low/Depth.hs b/Graphics/GL/Low/Depth.hs
new file mode 100644
--- /dev/null
+++ b/Graphics/GL/Low/Depth.hs
@@ -0,0 +1,17 @@
+module Graphics.GL.Low.Depth where
+
+import Graphics.GL
+
+-- | Enable the depth test. Attempting to render pixels with a depth value
+-- greater than the depth buffer at those pixels will have no effect. Otherwise
+-- the depth in the buffer will get updated to the new pixel's depth.
+enableDepthTest :: IO ()
+enableDepthTest = glEnable GL_DEPTH_TEST
+
+-- | Disable the depth test and depth buffer updates.
+disableDepthTest :: IO ()
+disableDepthTest = glDisable GL_DEPTH_TEST
+
+-- | Clear the depth buffer with the maximum depth value.
+clearDepthBuffer :: IO ()
+clearDepthBuffer = glClear GL_DEPTH_BUFFER_BIT
diff --git a/Graphics/GL/Low/Error.hs b/Graphics/GL/Low/Error.hs
new file mode 100644
--- /dev/null
+++ b/Graphics/GL/Low/Error.hs
@@ -0,0 +1,39 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE PatternSynonyms #-}
+module Graphics.GL.Low.Error where
+
+import Control.Exception
+import Data.Typeable
+
+import Graphics.GL
+
+-- | Detectable errors.
+data GLError =
+  InvalidEnum | -- ^ Enum argument out of range.
+  InvalidValue | -- ^ Integer argument out of range.
+  InvalidOperation | -- ^ Operation illegal in current state.
+  InvalidFramebufferOperation | -- ^ Framebuffer is not complete.
+  OutOfMemory
+    deriving Typeable
+
+instance Exception GLError
+
+instance Show GLError where
+  show InvalidEnum = "INVALID_ENUM enum argument out of range"
+  show InvalidValue = "INVALID_VALUE Numeric argument out of range"
+  show InvalidOperation = "INVALID_OPERATION Illegal in current state"
+  show InvalidFramebufferOperation = "INVALID_FRAMEBUFFER_OPERATION Framebuffer object is not complete"
+  show OutOfMemory = "Not enough memory left to execute command"
+
+-- | Check for a GL Error.
+getGLError :: IO (Maybe GLError)
+getGLError = do
+  n <- glGetError
+  return $ case n of
+    GL_NO_ERROR -> Nothing
+    GL_INVALID_ENUM -> Just InvalidEnum
+    GL_INVALID_VALUE -> Just InvalidValue
+    GL_INVALID_OPERATION -> Just InvalidOperation
+    GL_INVALID_FRAMEBUFFER_OPERATION -> Just InvalidFramebufferOperation
+    GL_OUT_OF_MEMORY -> Just OutOfMemory
+    _ -> error ("unknown GL error " ++ show n)
diff --git a/Graphics/GL/Low/Framebuffer.hs b/Graphics/GL/Low/Framebuffer.hs
new file mode 100644
--- /dev/null
+++ b/Graphics/GL/Low/Framebuffer.hs
@@ -0,0 +1,116 @@
+{-# LANGUAGE RankNTypes #-}
+module Graphics.GL.Low.Framebuffer (
+  FBO,
+  RBO,
+  DefaultFramebuffer,
+  bindFramebuffer,
+  newFBO,
+  deleteFBO,
+  attachTex2D,
+  attachCubeMap,
+  attachRBO,
+  newRBO,
+  deleteRBO
+) where
+
+import Foreign.Ptr
+import Foreign.Marshal
+import Foreign.Storable
+
+import Data.Default
+import Graphics.GL
+
+import Graphics.GL.Low.Classes
+import Graphics.GL.Low.Common
+import Graphics.GL.Low.Cube
+import Graphics.GL.Low.Texture
+
+
+-- | A framebuffer object is an alternative rendering destination. Once an FBO
+-- is bound to framebuffer binding target, it is possible to attach images
+-- (textures or RBOs) for color, depth, or stencil rendering.
+newtype FBO = FBO GLuint deriving Show
+
+-- | An RBO is a kind of image object used for rendering. The only thing
+-- you can do with an RBO is attach it to an FBO.
+data RBO a = RBO { unRBO :: GLuint } deriving Show
+
+-- | The default framebuffer. Bind this to render to the screen as usual.
+-- Use the Default instance method 'def' to construct it.
+data DefaultFramebuffer = DefaultFramebuffer deriving Show
+
+instance Default DefaultFramebuffer where
+  def = DefaultFramebuffer
+
+instance Framebuffer DefaultFramebuffer where
+  framebufferName _ = 0
+
+instance Framebuffer FBO where
+  framebufferName = glObjectName
+
+instance GLObject FBO where
+  glObjectName (FBO n) = fromIntegral n
+
+instance GLObject (RBO a) where
+  glObjectName (RBO n) = fromIntegral n
+
+-- | Binds an FBO or the default framebuffer to the framebuffer binding target.
+-- Replaces the framebuffer already bound there.
+bindFramebuffer :: Framebuffer a => a -> IO ()
+bindFramebuffer x = glBindFramebuffer GL_FRAMEBUFFER (framebufferName x)
+
+-- | Create a new framebuffer object. Before the framebuffer can be used for
+-- rendering it must have a color image attachment.
+newFBO :: IO FBO
+newFBO = do
+  n <- alloca (\ptr -> glGenFramebuffers 1 ptr >> peek ptr)
+  return (FBO n)
+
+-- | Delete an FBO.
+deleteFBO :: FBO -> IO ()
+deleteFBO (FBO n) = withArray [n] (\ptr -> glDeleteFramebuffers 1 ptr)
+
+-- | Attach a 2D texture to the FBO currently bound to the
+-- framebuffer binding target.
+attachTex2D :: Attachable a => Tex2D a -> IO ()
+attachTex2D tex =
+  glFramebufferTexture2D
+    GL_FRAMEBUFFER
+    (attachPoint tex)
+    GL_TEXTURE_2D
+    (glObjectName tex)
+    0
+
+-- | Attach one of the sides of a cubemap texture to the FBO currently bound
+-- to the framebuffer binding target.
+attachCubeMap :: Attachable a => CubeMap a -> Side -> IO ()
+attachCubeMap cm side =
+  glFramebufferTexture2D
+    GL_FRAMEBUFFER
+    (attachPoint cm)
+    (side cubeSideCodes)
+    (glObjectName cm)
+    0
+
+-- | Attach an RBO to the FBO currently bound to the framebuffer binding
+-- target.
+attachRBO :: Attachable a => RBO a -> IO ()
+attachRBO rbo = glFramebufferRenderbuffer
+  GL_FRAMEBUFFER (attachPoint rbo) GL_RENDERBUFFER (unRBO rbo)
+
+-- | Create a new renderbuffer with the specified dimensions.
+newRBO :: InternalFormat a => Int -> Int -> IO (RBO a)
+newRBO w h = do
+  n <- alloca (\ptr -> glGenRenderbuffers 1 ptr >> peek ptr)
+  rbo <- return (RBO n)
+  glBindRenderbuffer GL_RENDERBUFFER n
+  glRenderbufferStorage
+    GL_RENDERBUFFER
+    (internalFormat rbo)
+    (fromIntegral w)
+    (fromIntegral h)
+  return rbo
+
+-- | Delete an RBO.
+deleteRBO :: RBO a -> IO ()
+deleteRBO (RBO n) = withArray [n] (\ptr -> glDeleteRenderbuffers 1 ptr)
diff --git a/Graphics/GL/Low/ImageFormat.hs b/Graphics/GL/Low/ImageFormat.hs
new file mode 100644
--- /dev/null
+++ b/Graphics/GL/Low/ImageFormat.hs
@@ -0,0 +1,56 @@
+module Graphics.GL.Low.ImageFormat where
+
+import Graphics.GL
+
+import Graphics.GL.Low.Classes
+
+-- | 1-byte alpha channel only.
+data Alpha = Alpha deriving Show
+
+-- | 1-byte grayscale pixel format.
+data Luminance = Luminance deriving Show
+
+-- | 2-byte luminance and alpha channel format.
+data LuminanceAlpha = Luminancealpha deriving Show
+
+-- | 3-byte true color pixel format.
+data RGB = RGB deriving Show
+
+-- | 4-byte true color plus alpha channel format.
+data RGBA = RGBA deriving Show
+
+-- | 24-bit depth format.
+data Depth24 = Depth24 deriving Show
+
+-- | Combination depth and stencil format.
+data Depth24Stencil8 = Depth24Stencil8 deriving Show
+
+instance InternalFormat RGB where
+  internalFormat _ = GL_RGB8
+instance InternalFormat RGBA where
+  internalFormat _ = GL_RGBA
+instance InternalFormat Alpha where
+  internalFormat _ = GL_ALPHA
+instance InternalFormat Luminance where
+  internalFormat _ = GL_LUMINANCE
+instance InternalFormat LuminanceAlpha where
+  internalFormat _ = GL_LUMINANCE_ALPHA
+instance InternalFormat Depth24 where
+  internalFormat _ = GL_DEPTH_COMPONENT24
+instance InternalFormat Depth24Stencil8 where
+  internalFormat _ = GL_DEPTH24_STENCIL8
+
+instance Attachable RGB where
+  attachPoint _ = GL_COLOR_ATTACHMENT0
+instance Attachable RGBA where
+  attachPoint _ = GL_COLOR_ATTACHMENT0
+instance Attachable Luminance where
+  attachPoint _ = GL_COLOR_ATTACHMENT0
+instance Attachable LuminanceAlpha where
+  attachPoint _ = GL_COLOR_ATTACHMENT0
+instance Attachable Alpha where
+  attachPoint _ = GL_COLOR_ATTACHMENT0
+instance Attachable Depth24 where
+  attachPoint _ = GL_DEPTH_ATTACHMENT
+instance Attachable Depth24Stencil8 where
+  attachPoint _ = GL_DEPTH_STENCIL_ATTACHMENT
diff --git a/Graphics/GL/Low/Render.hs b/Graphics/GL/Low/Render.hs
new file mode 100644
--- /dev/null
+++ b/Graphics/GL/Low/Render.hs
@@ -0,0 +1,146 @@
+module Graphics.GL.Low.Render (
+  Culling(..),
+  Viewport(..),
+  IndexFormat(..),
+  drawPoints,
+  drawLines,
+  drawLineStrip,
+  drawLineLoop,
+  drawTriangles,
+  drawTriangleStrip,
+  drawTriangleFan,
+  drawIndexedPoints,
+  drawIndexedLines,
+  drawIndexedLineStrip,
+  drawIndexedLineLoop,
+  drawIndexedTriangles,
+  drawIndexedTriangleStrip,
+  drawIndexedTriangleFan,
+  enableScissorTest,
+  disableScissorTest,
+  enableCulling,
+  disableCulling,
+  setViewport
+) where
+
+import Foreign.Ptr
+import Foreign.Marshal
+import Foreign.Storable
+
+import Graphics.GL
+
+import Graphics.GL.Low.Classes
+
+-- | Facet culling modes.
+data Culling =
+  CullFront |
+  CullBack |
+  CullFrontAndBack
+    deriving Show
+
+instance ToGL Culling where
+  toGL CullFront = GL_FRONT
+  toGL CullBack = GL_BACK
+  toGL CullFrontAndBack = GL_FRONT_AND_BACK
+
+-- | A rectangular section of the window.
+data Viewport = Viewport
+  { viewportX :: Int
+  , viewportY :: Int
+  , viewportW :: Int
+  , viewportH :: Int }
+    deriving (Eq, Show)
+
+-- | How indices are packed in an ElementArray buffer object.
+data IndexFormat =
+  UByteIndices  | -- ^ Each index is one unsigned byte.
+  UShortIndices | -- ^ Each index is a two byte unsigned int.
+  UIntIndices     -- ^ Each index is a four byte unsigned int.
+    deriving Show
+
+instance ToGL IndexFormat where
+  toGL UByteIndices  = GL_UNSIGNED_BYTE
+  toGL UShortIndices = GL_UNSIGNED_SHORT
+  toGL UIntIndices   = GL_UNSIGNED_INT
+
+
+
+drawPoints :: Int -> IO ()
+drawPoints = drawArrays GL_POINTS
+
+drawLines :: Int -> IO ()
+drawLines = drawArrays GL_LINES
+
+drawLineStrip :: Int -> IO ()
+drawLineStrip = drawArrays GL_LINE_STRIP
+
+drawLineLoop :: Int -> IO ()
+drawLineLoop = drawArrays GL_LINE_LOOP
+
+drawTriangles :: Int -> IO ()
+drawTriangles = drawArrays GL_TRIANGLES
+
+drawTriangleStrip :: Int -> IO ()
+drawTriangleStrip = drawArrays GL_TRIANGLE_STRIP
+
+drawTriangleFan :: Int -> IO ()
+drawTriangleFan = drawArrays GL_TRIANGLE_FAN
+
+drawArrays :: GLenum -> Int -> IO ()
+drawArrays mode n = glDrawArrays mode (fromIntegral n) 0
+
+drawIndexedPoints :: Int -> IndexFormat -> IO ()
+drawIndexedPoints = drawIndexed GL_POINTS
+
+drawIndexedLines :: Int -> IndexFormat -> IO ()
+drawIndexedLines = drawIndexed GL_LINES
+
+drawIndexedLineStrip :: Int -> IndexFormat -> IO ()
+drawIndexedLineStrip = drawIndexed GL_LINE_STRIP
+
+drawIndexedLineLoop :: Int -> IndexFormat -> IO ()
+drawIndexedLineLoop = drawIndexed GL_LINE_LOOP
+
+drawIndexedTriangles :: Int -> IndexFormat -> IO ()
+drawIndexedTriangles = drawIndexed GL_TRIANGLES
+
+drawIndexedTriangleStrip :: Int -> IndexFormat -> IO ()
+drawIndexedTriangleStrip = drawIndexed GL_TRIANGLE_STRIP
+
+drawIndexedTriangleFan :: Int -> IndexFormat -> IO ()
+drawIndexedTriangleFan = drawIndexed GL_TRIANGLE_FAN
+
+drawIndexed :: GLenum -> Int -> IndexFormat -> IO ()
+drawIndexed mode n fmt = glDrawElements mode (fromIntegral n) (toGL fmt) nullPtr
+
+-- | Enable the scissor test. Graphics outside the scissor box will not be
+-- rendered.
+enableScissorTest :: Viewport -> IO ()
+enableScissorTest (Viewport x y w h) = do
+  glScissor (fromIntegral x) (fromIntegral y) (fromIntegral w) (fromIntegral h)
+  glEnable GL_SCISSOR_TEST
+
+-- | Disable the scissor test.
+disableScissorTest :: IO ()
+disableScissorTest = glDisable GL_SCISSOR_TEST
+
+
+-- | Enable facet culling. The argument specifies whether front faces, back
+-- faces, or both will be omitted from rendering. If both front and back
+-- faces are culled you can still render points and lines.
+enableCulling :: Culling -> IO ()
+enableCulling c = do
+  case c of
+    CullFront -> glCullFace GL_FRONT
+    CullBack -> glCullFace GL_BACK
+    CullFrontAndBack -> glCullFace GL_FRONT_AND_BACK
+  glEnable GL_CULL_FACE
+
+-- | Disable facet culling. Front and back faces will now be rendered.
+disableCulling :: IO ()
+disableCulling = glDisable GL_CULL_FACE
+
+-- | Set the viewport. The default viewport simply covers the entire window.
+setViewport :: Viewport -> IO ()
+setViewport (Viewport x y w h) =
+  glViewport (fromIntegral x) (fromIntegral y) (fromIntegral w) (fromIntegral h)
diff --git a/Graphics/GL/Low/Shader.hs b/Graphics/GL/Low/Shader.hs
new file mode 100644
--- /dev/null
+++ b/Graphics/GL/Low/Shader.hs
@@ -0,0 +1,176 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+module Graphics.GL.Low.Shader (
+  Program,
+  ProgramError(..),
+  newProgramSafe,
+  deleteProgram,
+  newProgram,
+  useProgram,
+  setUniform1f,
+  setUniform2f,
+  setUniform3f,
+  setUniform4f,
+  setUniform1i,
+  setUniform2i,
+  setUniform3i,
+  setUniform4i,
+  setUniform44,
+  setUniform33,
+  setUniform22
+) where
+
+import Foreign.Ptr
+import Foreign.C.String
+import Foreign.Marshal
+import Foreign.Storable
+import Control.Exception
+import Control.Monad (when, forM_)
+import Data.Typeable
+
+import Graphics.GL
+import Linear
+
+import Graphics.GL.Low.Classes
+import Graphics.GL.Low.VertexAttrib
+
+-- | A Program object is the combination of a compiled vertex shader and fragment
+-- shader. Programs have three kinds of inputs: vertex attributes, uniforms,
+-- and samplers. Programs have two outputs: fragment color and fragment depth.
+-- At most one program can be "in use" at a time. Same idea as binding targets
+-- it's just not called that.
+newtype Program = Program GLuint deriving Show
+
+-- | Either a vertex shader or a fragment shader.
+data ShaderType = VertexShader | FragmentShader deriving Show
+
+instance ToGL ShaderType where
+  toGL VertexShader = GL_VERTEX_SHADER
+  toGL FragmentShader = GL_FRAGMENT_SHADER
+
+-- | The error message emitted by the driver when shader compilation or
+-- linkage fails.
+data ProgramError =
+  VertexShaderError String |
+  FragmentShaderError String |
+  LinkError String
+    deriving (Show, Typeable)
+  
+instance Exception ProgramError
+
+-- | Same as 'newProgram' but does not throw exceptions.
+newProgramSafe :: String -> String -> IO (Either ProgramError Program)
+newProgramSafe vcode fcode = try $ newProgram vcode fcode
+
+-- | Delete a program.
+deleteProgram :: Program -> IO ()
+deleteProgram (Program n) = glDeleteProgram n
+
+-- | Compile the code for a vertex shader and a fragment shader, then link
+-- them into a new program. If the compiler or linker fails it will throw
+-- a ProgramError.
+newProgram :: String -- ^ vertex shader source code
+           -> String -- ^ fragment shader source code
+           -> IO Program
+newProgram vcode fcode = do
+  vertexShaderId <- compileShader vcode VertexShader
+  fragmentShaderId <- compileShader fcode FragmentShader
+  programId <- glCreateProgram
+  glAttachShader programId vertexShaderId
+  glAttachShader programId fragmentShaderId
+  glLinkProgram programId
+  result <- alloca $ \ptr ->
+    glGetProgramiv programId GL_LINK_STATUS ptr >> peek ptr
+  when (result == GL_FALSE) $ do
+    len <- fmap fromIntegral $ alloca $ \ptr ->
+      glGetProgramiv programId GL_INFO_LOG_LENGTH ptr >> peek ptr
+    errors <- allocaArray len $ \ptr -> do
+      glGetProgramInfoLog programId (fromIntegral len) nullPtr ptr
+      peekCString ptr
+    throwIO (LinkError errors)
+  glDeleteShader vertexShaderId
+  glDeleteShader fragmentShaderId
+  return (Program programId)
+
+-- | Install a program into the rendering pipeline. Replaces the program
+-- already in use, if any.
+useProgram :: Program -> IO ()
+useProgram (Program n) = glUseProgram n
+
+compileShader :: String -> ShaderType -> IO GLuint
+compileShader code vertOrFrag = do
+  shaderId <- glCreateShader (toGL vertOrFrag)
+  withCString code $ \ptr -> with ptr $ \pptr -> do
+    glShaderSource shaderId 1 pptr nullPtr
+    glCompileShader shaderId
+  result <- with GL_FALSE $ \ptr ->
+    glGetShaderiv shaderId GL_COMPILE_STATUS ptr >> peek ptr
+  when (result == GL_FALSE) $ do
+    len <- fmap fromIntegral $ alloca $ \ptr ->
+      glGetShaderiv shaderId GL_INFO_LOG_LENGTH ptr >> peek ptr
+    errors <- allocaArray len $ \ptr -> do
+      glGetShaderInfoLog shaderId (fromIntegral len) nullPtr ptr
+      peekCString ptr
+    case vertOrFrag of
+      VertexShader -> throwIO (VertexShaderError errors)
+      FragmentShader -> throwIO (FragmentShaderError errors)
+  return shaderId
+
+setUniform1f :: String -> [Float] -> IO ()
+setUniform1f = setUniform glUniform1fv
+
+setUniform2f :: String -> [V2 Float] -> IO ()
+setUniform2f = setUniform
+  (\loc cnt val -> glUniform2fv loc cnt (castPtr val))
+
+setUniform3f :: String -> [V3 Float] -> IO ()
+setUniform3f = setUniform
+  (\loc cnt val -> glUniform3fv loc cnt (castPtr val))
+
+setUniform4f :: String -> [V4 Float] -> IO ()
+setUniform4f = setUniform
+  (\loc cnt val -> glUniform4fv loc cnt (castPtr val))
+
+setUniform1i :: String -> [Int] -> IO ()
+setUniform1i = setUniform
+  (\loc cnt val -> glUniform1iv loc cnt (castPtr val))
+
+setUniform2i :: String -> [V2 Int] -> IO ()
+setUniform2i = setUniform 
+  (\loc cnt val -> glUniform2iv loc cnt (castPtr val))
+
+setUniform3i :: String -> [V3 Int] -> IO ()
+setUniform3i = setUniform
+  (\loc cnt val -> glUniform3iv loc cnt (castPtr val))
+
+setUniform4i :: String -> [V4 Int] -> IO ()
+setUniform4i = setUniform
+  (\loc cnt val -> glUniform4iv loc cnt (castPtr val))
+
+setUniform44 :: String -> [M44 Float] -> IO ()
+setUniform44 = setUniform
+  (\loc cnt val -> glUniformMatrix4fv loc cnt GL_FALSE (castPtr val))
+
+setUniform33 :: String -> [M33 Float] -> IO ()
+setUniform33 = setUniform
+  (\loc cnt val -> glUniformMatrix3fv loc cnt GL_FALSE (castPtr val))
+
+setUniform22 :: String -> [M22 Float] -> IO ()
+setUniform22 = setUniform
+  (\loc cnt val -> glUniformMatrix2fv loc cnt GL_FALSE (castPtr val))
+
+setUniform :: Storable a
+           => (GLint -> GLsizei -> Ptr a -> IO ())
+           -> String
+           -> [a]
+           -> IO ()
+setUniform glAction name xs = withArrayLen xs $ \n bytes -> do
+  p <- alloca (\ptr -> glGetIntegerv GL_CURRENT_PROGRAM ptr >> peek ptr)
+  if p == 0
+    then return ()
+    else do
+      loc <- withCString name (\ptr -> glGetUniformLocation (fromIntegral p) ptr)
+      if loc == -1
+        then return ()
+        else glAction loc (fromIntegral n) bytes
+
+
diff --git a/Graphics/GL/Low/Stencil.hs b/Graphics/GL/Low/Stencil.hs
new file mode 100644
--- /dev/null
+++ b/Graphics/GL/Low/Stencil.hs
@@ -0,0 +1,137 @@
+-- | The stencil test is like a configurable depth test with a dedicated
+-- additional buffer. Like the depth test, if the stencil test fails then the
+-- pixel being tested will not be rendered. The stencil test happens before the
+-- depth test, if it's enabled. For a given pixel, the stencil test passes if
+-- the following computation
+--
+-- > (ref & mask) `stencilOp` (buf & mask)
+--
+-- computes true. The variables ref and mask are configurable constants, buf is
+-- the value in the stencil buffer at the given pixel, and stencilOp is a
+-- configurable comparison operation. The stencil op can also be set to pass
+-- 'Always' or 'Never'.
+--
+-- The stencil buffer may be modified in various ways on the following events:
+--
+-- - When the stencil test fails or
+-- - When the stencil test passes then the depth test fails or
+-- - When both tests pass.
+--
+-- An example way of updating the stencil buffer is to write zero, or to write
+-- the ref value mentioned above to the location being tested.
+--
+-- > example program here
+
+module Graphics.GL.Low.Stencil where
+
+import Data.Default
+import Data.Bits
+import Data.Word
+
+import Graphics.GL
+import Graphics.GL.Low.Classes
+
+-- | Enable the stencil test with a set of operating parameters.
+enableStencil :: Stencil -> IO ()
+enableStencil (Stencil f r m op1 op2 op3) = do
+  glStencilFunc (toGL f) (fromIntegral r) (fromIntegral m)
+  glStencilOp (toGL op1) (toGL op2) (toGL op2)
+  glEnable GL_STENCIL_TEST
+
+-- | Disable the stencil test and updates to the stencil buffer, if one exists.
+disableStencil :: IO ()
+disableStencil = glDisable GL_STENCIL_TEST
+
+-- | Clear the stencil buffer with all zeros.
+clearStencilBuffer :: IO ()
+clearStencilBuffer = glClear GL_STENCIL_BUFFER_BIT
+
+-- | In this basic configuration of the stencil, anything rendered will
+-- create a silhouette of 1s in the stencil buffer. Attempting to render a
+-- second time into the silhouette will have no effect because the stencil
+-- test will fail (ref=1 isn't greater than buffer=1).
+--
+-- @
+-- def { func = Greater
+--     , ref = 1
+--     , onBothPass = Replace }
+-- @
+basicStencil :: Stencil
+basicStencil = def
+  { func = Greater
+  , ref = 1
+  , onBothPass = Replace }
+
+-- | Configuration of the stencil test and associated stencil buffer updating.
+data Stencil = Stencil
+  { func          :: StencilFunc
+  , ref           :: Int
+  , mask          :: Word
+  , onStencilFail :: StencilOp
+  , onDepthFail   :: StencilOp
+  , onBothPass    :: StencilOp }
+
+-- | The default state of the stencil, if it were simply enabled, would be
+-- to always pass and update nothing in the buffer. It would have no effect
+-- on rendering.
+instance Default Stencil where
+  def = Stencil
+    { func = Always
+    , ref  = 0
+    , mask = complement 0
+    , onStencilFail = Keep
+    , onDepthFail   = Keep
+    , onBothPass    = Keep }
+
+-- the proper implementation of toList f is the one that for any
+-- g :: t a -> [a] you can make a unique k such that g = k . f
+-- jle`
+  
+
+
+-- | The stencil test passes under what condition.
+data StencilFunc =
+  Never |
+  Less |
+  LessOrEqual |
+  Greater |
+  GreaterOrEqual |
+  Equal |
+  NotEqual |
+  Always
+    deriving Show
+
+instance ToGL StencilFunc where
+  toGL x = case x of
+    Never -> GL_NEVER
+    Less -> GL_LESS
+    LessOrEqual -> GL_LEQUAL
+    Greater -> GL_GREATER
+    GreaterOrEqual -> GL_GEQUAL
+    Equal -> GL_EQUAL
+    NotEqual -> GL_NOTEQUAL
+    Always -> GL_ALWAYS
+
+
+-- | Modification action for the stencil buffer. 
+data StencilOp =
+  Keep | -- ^ Do nothing.
+  Zero | -- ^ Set to zero.
+  Replace | -- ^ Write the ref value passed to enableStencil.
+  Increment |
+  Decrement |
+  Invert | -- ^ Bitwise complement.
+  IncrementWrap |
+  DecrementWrap
+    deriving Show
+
+instance ToGL StencilOp where
+  toGL x = case x of
+    Keep -> GL_KEEP
+    Zero -> GL_ZERO
+    Replace -> GL_REPLACE
+    Increment -> GL_INCR
+    Decrement -> GL_DECR
+    Invert -> GL_INVERT
+    IncrementWrap -> GL_INCR_WRAP
+    DecrementWrap -> GL_DECR_WRAP
diff --git a/Graphics/GL/Low/Texture.hs b/Graphics/GL/Low/Texture.hs
new file mode 100644
--- /dev/null
+++ b/Graphics/GL/Low/Texture.hs
@@ -0,0 +1,206 @@
+module Graphics.GL.Low.Texture (
+  Tex2D,
+  CubeMap,
+  Filtering(..),
+  Wrapping(..),
+  Dimensions(..),
+  newTexture2D,
+  deleteTexture,
+  newCubeMap,
+  newEmptyTexture2D,
+  newEmptyCubeMap,
+  bindTexture2D,
+  bindTextureCubeMap,
+  setActiveTextureUnit,
+  setTex2DFiltering,
+  setCubeMapFiltering,
+  setTex2DWrapping,
+  setCubeMapWrapping
+) where
+
+import Foreign.Ptr
+import Foreign.Marshal
+import Foreign.Storable
+import Data.Vector.Storable
+import Data.Word
+import Control.Applicative
+import Data.Traversable
+
+import Graphics.GL
+
+import Graphics.GL.Low.Classes
+import Graphics.GL.Low.Common
+import Graphics.GL.Low.Cube
+
+-- | A 2D texture. A program can sample a texture if it has been bound to
+-- the appropriate texture unit.
+newtype Tex2D a = Tex2D GLuint deriving Show
+
+-- | A cubemap texture is just six 2D textures. A program can sample a cubemap
+-- texture if it has been bound to the appropriate texture unit.
+newtype CubeMap a = CubeMap GLuint deriving Show
+
+-- | Texture filtering modes.
+data Filtering =
+  Nearest | -- ^ No interpolation.
+  Linear    -- ^ Linear interpolation.
+    deriving Show
+
+instance ToGL Filtering where
+  toGL Nearest = GL_NEAREST
+  toGL Linear = GL_LINEAR
+
+-- | Texture wrapping modes.
+data Wrapping =
+  Repeat         | -- ^ Tile the texture past the boundary.
+  MirroredRepeat | -- ^ Tile the texture but mirror every other tile.
+  ClampToEdge      -- ^ Use the edge color for anything past the boundary.
+    deriving Show
+
+instance ToGL Wrapping where
+  toGL Repeat = GL_REPEAT
+  toGL MirroredRepeat = GL_MIRRORED_REPEAT
+  toGL ClampToEdge = GL_CLAMP_TO_EDGE
+
+-- | The size of an image in pixels.
+data Dimensions = Dimensions
+  { imageWidth  :: Int
+  , imageHeight :: Int }
+    deriving (Show)
+
+instance Texture (Tex2D a) where
+
+instance Texture (CubeMap a) where
+
+instance GLObject (Tex2D a) where
+  glObjectName (Tex2D n) = fromIntegral n
+
+instance GLObject (CubeMap a) where
+  glObjectName (CubeMap n) = fromIntegral n
+
+-- | Create a new 2D texture from a blob and its image format.
+-- Dimensions should be powers of two.
+newTexture2D :: InternalFormat a => Vector Word8 -> Dimensions -> IO (Tex2D a)
+newTexture2D bytes (Dimensions w h)  = do
+  n <- alloca (\ptr -> glGenTextures 1 ptr >> peek ptr)
+  glBindTexture GL_TEXTURE_2D n
+  tex <- return (Tex2D n)
+  unsafeWith bytes $ \ptr -> glTexImage2D
+    GL_TEXTURE_2D
+    0
+    (internalFormat tex)
+    (fromIntegral w)
+    (fromIntegral h)
+    0
+    (internalFormat tex)
+    GL_UNSIGNED_BYTE
+    (castPtr ptr)
+  return tex
+
+-- | Delete a texture.
+deleteTexture :: Texture a => a -> IO ()
+deleteTexture x = withArray [glObjectName x] (\ptr -> glDeleteTextures 1 ptr)
+
+-- | Create a new cube map texture from six blobs and their respective formats.
+-- Dimensions should be powers of two.
+newCubeMap :: InternalFormat a
+           => Cube (Vector Word8, Dimensions)
+           -> IO (CubeMap a)
+newCubeMap images = do
+  n <- alloca (\ptr -> glGenTextures 1 ptr >> peek ptr)
+  glBindTexture GL_TEXTURE_CUBE_MAP n
+  cm <- return (CubeMap n)
+  let fmt = internalFormat cm
+  sequenceA (liftA2 (loadCubeMapSide fmt) images cubeSideCodes)
+  return cm
+  
+loadCubeMapSide :: GLenum -> (Vector Word8, Dimensions) -> GLenum -> IO ()
+loadCubeMapSide fmt (bytes, (Dimensions w h)) side = do
+  unsafeWith bytes $ \ptr -> glTexImage2D
+    side
+    0
+    (fromIntegral fmt)
+    (fromIntegral w)
+    (fromIntegral h)
+    0
+    fmt
+    GL_UNSIGNED_BYTE
+    (castPtr ptr)
+
+-- | Create an empty texture with the specified dimensions and format.
+newEmptyTexture2D :: InternalFormat a => Int -> Int -> IO (Tex2D a)
+newEmptyTexture2D w h = do
+  let w' = fromIntegral w
+  let h' = fromIntegral h
+  n <- alloca (\ptr -> glGenTextures 1 ptr >> peek ptr)
+  tex <- return (Tex2D n)
+  let fmt = internalFormat tex
+  let fmt' = internalFormat tex
+  glBindTexture GL_TEXTURE_2D n
+  glTexImage2D GL_TEXTURE_2D 0 fmt w' h' 0 fmt' GL_UNSIGNED_BYTE nullPtr
+  return tex
+
+-- | Create a cubemap texture where each of the six sides has the specified
+-- dimensions and format.
+newEmptyCubeMap :: InternalFormat a => Int -> Int -> IO (CubeMap a)
+newEmptyCubeMap w h = do
+  let w' = fromIntegral w
+  let h' = fromIntegral h
+  n <- alloca (\ptr -> glGenTextures 1 ptr >> peek ptr)
+  tex <- return (CubeMap n)
+  let fmt = internalFormat tex
+  let fmt' = internalFormat tex
+  glBindTexture GL_TEXTURE_CUBE_MAP n
+  glTexImage2D GL_TEXTURE_CUBE_MAP_POSITIVE_X 0 fmt w' h' 0 fmt' GL_UNSIGNED_BYTE nullPtr
+  glTexImage2D GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0 fmt w' h' 0 fmt' GL_UNSIGNED_BYTE nullPtr
+  glTexImage2D GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0 fmt w' h' 0 fmt' GL_UNSIGNED_BYTE nullPtr
+  glTexImage2D GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0 fmt w' h' 0 fmt' GL_UNSIGNED_BYTE nullPtr
+  glTexImage2D GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0 fmt w' h' 0 fmt' GL_UNSIGNED_BYTE nullPtr
+  glTexImage2D GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0 fmt w' h' 0 fmt' GL_UNSIGNED_BYTE nullPtr
+  return tex
+  
+
+-- | Bind a 2D texture to the 2D texture binding target and the currently
+-- active texture unit.
+bindTexture2D :: Tex2D a -> IO ()
+bindTexture2D (Tex2D n) = glBindTexture GL_TEXTURE_2D n
+
+-- | Bind a cubemap texture to the cubemap texture binding target and
+-- the currently active texture unit.
+bindTextureCubeMap :: CubeMap a -> IO ()
+bindTextureCubeMap (CubeMap n) = glBindTexture GL_TEXTURE_CUBE_MAP n
+
+-- | Set the active texture unit. The default is zero.
+setActiveTextureUnit :: Enum a => a -> IO ()
+setActiveTextureUnit n =
+  (glActiveTexture . fromIntegral) (GL_TEXTURE0 + fromEnum n)
+
+-- | Set the filtering for the 2D texture currently bound to the 2D texture
+-- binding target.
+setTex2DFiltering :: Filtering -> IO ()
+setTex2DFiltering filt = do
+  glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MIN_FILTER (toGL filt)
+  glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MAG_FILTER (toGL filt)
+
+-- | Set the filtering for the cubemap texture currently bound to the cubemap
+-- texture binding target.
+setCubeMapFiltering :: Filtering -> IO ()
+setCubeMapFiltering filt = do
+  glTexParameteri GL_TEXTURE_CUBE_MAP GL_TEXTURE_MIN_FILTER (toGL filt)
+  glTexParameteri GL_TEXTURE_CUBE_MAP GL_TEXTURE_MAG_FILTER (toGL filt)
+
+-- | Set the wrapping mode for the 2D texture currently bound to the 2D
+-- texture binding target.
+setTex2DWrapping :: Wrapping -> IO ()
+setTex2DWrapping wrap = do
+  glTexParameteri GL_TEXTURE_2D GL_TEXTURE_WRAP_S (toGL wrap)
+  glTexParameteri GL_TEXTURE_2D GL_TEXTURE_WRAP_T (toGL wrap)
+
+-- | Set the wrapping mode for the cubemap texture currently bound to the
+-- cubemap texture binding target. Because no blending occurs between cube
+-- faces you probably want ClampToEdge.
+setCubeMapWrapping :: Wrapping -> IO ()
+setCubeMapWrapping wrap = do
+  glTexParameteri GL_TEXTURE_CUBE_MAP GL_TEXTURE_WRAP_S (toGL wrap)
+  glTexParameteri GL_TEXTURE_CUBE_MAP GL_TEXTURE_WRAP_T (toGL wrap)
+  glTexParameteri GL_TEXTURE_CUBE_MAP GL_TEXTURE_WRAP_R (toGL wrap)
diff --git a/Graphics/GL/Low/VAO.hs b/Graphics/GL/Low/VAO.hs
new file mode 100644
--- /dev/null
+++ b/Graphics/GL/Low/VAO.hs
@@ -0,0 +1,38 @@
+module Graphics.GL.Low.VAO 
+(
+  VAO,
+  newVAO,
+  deleteVAO,
+  bindVAO
+) where
+
+import Foreign.Storable
+import Foreign.Marshal
+
+import Graphics.GL
+
+import Graphics.GL.Low.Classes
+
+-- | A VAO stores vertex attribute layouts and the VBO source of vertices
+-- for those attributes. It also stores the state of the element array binding
+-- target. The vertex array binding target admits one VAO at a time.
+newtype VAO = VAO GLuint deriving Show
+
+instance GLObject VAO where
+  glObjectName (VAO n) = fromIntegral n
+
+-- | Create a new VAO. The only thing you can do with a VAO is bind it to
+-- the vertex array binding target.
+newVAO :: IO VAO
+newVAO = do
+  n <- alloca (\ptr -> glGenVertexArrays 1 ptr >> peek ptr)
+  return (VAO n)
+
+-- | Delete a VAO.
+deleteVAO :: VAO -> IO ()
+deleteVAO (VAO n) = withArray [n] (\ptr -> glDeleteVertexArrays 1 ptr)
+
+-- | Assign the VAO to the vertex array binding target. The VAO already bound
+-- will be replaced, if any.
+bindVAO :: VAO -> IO ()
+bindVAO (VAO n) = glBindVertexArray n
diff --git a/Graphics/GL/Low/VertexAttrib.hs b/Graphics/GL/Low/VertexAttrib.hs
new file mode 100644
--- /dev/null
+++ b/Graphics/GL/Low/VertexAttrib.hs
@@ -0,0 +1,127 @@
+-- | Vertex Attribute Array.
+module Graphics.GL.Low.VertexAttrib where
+
+import Foreign.C.String
+import Foreign.Ptr
+import Foreign.Marshal
+import Foreign.Storable
+import Control.Monad (forM_)
+
+import Graphics.GL
+
+import Graphics.GL.Low.Classes
+
+-- | The name of a vertex input to a program combined with the
+-- component format and number of components for that attribute in the
+-- vertex data. Alternatively the size of an unused section of the data
+-- in bytes.
+data LayoutElement =
+  Attrib String Int ComponentFormat | -- ^ Name, component count and component format of a vertex attribute.
+  Unused Int -- ^ Size in bytes of an unused section of the vertex data.
+    deriving Show
+
+-- | The layout of interleaved vertex attribute data.
+type VertexAttributeLayout = [LayoutElement]
+
+-- | The size and interpretation of a vertex attribute component. Normalized
+-- components will be mapped to floats in the range [0, 1].
+data ComponentFormat =
+  VFloat | -- ^ 4-byte float
+  VByte | 
+  VUByte | 
+  VByteNormalized | 
+  VUByteNormalized |
+  VShort | -- ^ 2-byte signed integer
+  VUShort | -- ^ 2-byte unsigned integer
+  VShortNormalized |
+  VUShortNormalized |
+  VInt | -- ^ 4-byte signed integer
+  VUInt | -- ^ 4-byte unsigned integer
+  VIntNormalized |
+  VUIntNormalized
+    deriving (Eq, Show)
+
+instance ToGL ComponentFormat where
+  toGL VFloat = GL_FLOAT
+  toGL VByte = GL_BYTE
+  toGL VUByte = GL_UNSIGNED_BYTE
+  toGL VByteNormalized = GL_BYTE
+  toGL VUByteNormalized = GL_UNSIGNED_BYTE
+  toGL VShort = GL_SHORT
+  toGL VUShort = GL_UNSIGNED_SHORT
+  toGL VShortNormalized = GL_SHORT
+  toGL VUShortNormalized = GL_UNSIGNED_SHORT
+  toGL VInt = GL_INT
+  toGL VUInt = GL_UNSIGNED_INT
+  toGL VIntNormalized = GL_INT
+  toGL VUIntNormalized = GL_UNSIGNED_INT
+  
+
+
+elaborateLayout :: Int -> VertexAttributeLayout -> [(String, Int, Int, ComponentFormat)]
+elaborateLayout here layout = case layout of
+  [] -> []
+  (Unused n):xs -> elaborateLayout (here+n) xs
+  (Attrib name n fmt):xs ->
+    let size = n * sizeOfVertexComponent fmt in
+    (name, n, here, fmt) : elaborateLayout (here+size) xs
+
+totalLayout :: VertexAttributeLayout -> Int
+totalLayout layout = sum (map arraySize layout) where
+  arraySize (Unused n) = n
+  arraySize (Attrib _ n fmt) = n * sizeOfVertexComponent fmt
+
+sizeOfVertexComponent :: ComponentFormat -> Int
+sizeOfVertexComponent c = case c of
+  VByte -> 1
+  VUByte -> 1
+  VByteNormalized -> 1
+  VUByteNormalized -> 1
+  VShort -> 2
+  VUShort -> 2
+  VShortNormalized -> 2
+  VUShortNormalized -> 2
+  VInt -> 4
+  VUInt -> 4
+  VIntNormalized -> 4
+  VUIntNormalized -> 4
+  VFloat -> 4
+
+isNormalized :: ComponentFormat -> Bool
+isNormalized c = case c of
+  VByte -> False
+  VUByte -> False
+  VByteNormalized -> True
+  VUByteNormalized -> True
+  VShort -> False
+  VUShort -> False
+  VShortNormalized -> True
+  VUShortNormalized -> True
+  VInt -> False
+  VUInt -> False
+  VIntNormalized -> True
+  VUIntNormalized -> True
+  VFloat -> False
+
+
+-- | This configures the currently bound VAO. It calls glVertexAttribPointer
+-- and glEnableVertexAttribArray.
+setVertexAttributeLayout :: VertexAttributeLayout -> IO ()
+setVertexAttributeLayout layout = do
+  p <- alloca (\ptr -> glGetIntegerv GL_CURRENT_PROGRAM ptr >> peek ptr)
+  if p == 0
+    then return ()
+    else do
+      let layout' = elaborateLayout 0 layout
+      let total = totalLayout layout
+      forM_ layout' $ \(name, size, offset, fmt) -> do
+        attrib <- withCString name $ \ptr -> glGetAttribLocation (fromIntegral p) (castPtr ptr)
+        let norm = isNormalized fmt
+        glVertexAttribPointer
+          (fromIntegral attrib)
+          (fromIntegral size)
+          (toGL fmt)
+          (fromIntegral . fromEnum $ norm)
+          (fromIntegral offset)
+          (castPtr (nullPtr `plusPtr` offset))
+        glEnableVertexAttribArray (fromIntegral attrib)
diff --git a/lowgl.cabal b/lowgl.cabal
--- a/lowgl.cabal
+++ b/lowgl.cabal
@@ -10,7 +10,7 @@
 -- PVP summary:      +-+------- breaking API changes
 --                   | | +----- non-breaking API additions
 --                   | | | +--- code changes with no API change
-version:             0.2.0.0
+version:             0.2.0.1
 
 -- A short (one-line) description of the package.
 synopsis:            Basic gl wrapper and reference
@@ -49,6 +49,21 @@
 library
   -- Modules exported by the library.
   exposed-modules:     Graphics.GL.Low
+                       Graphics.GL.Low.Blending
+                       Graphics.GL.Low.BufferObject
+                       Graphics.GL.Low.Color
+                       Graphics.GL.Low.Classes
+                       Graphics.GL.Low.Cube
+                       Graphics.GL.Low.Depth
+                       Graphics.GL.Low.Error
+                       Graphics.GL.Low.Framebuffer
+                       Graphics.GL.Low.ImageFormat
+                       Graphics.GL.Low.Render
+                       Graphics.GL.Low.Shader
+                       Graphics.GL.Low.Stencil
+                       Graphics.GL.Low.Texture
+                       Graphics.GL.Low.VAO
+                       Graphics.GL.Low.VertexAttrib
   other-modules:       Graphics.GL.Low.Common
   
   -- Modules included in this library but not exported.
@@ -74,4 +89,4 @@
 source-repository this
   type: git
   location: https://github.com/evanrinehart/lowgl
-  tag: 0.2.0.0
+  tag: 0.2.0.1
