diff --git a/Graphics/GL/Low.hs b/Graphics/GL/Low.hs
--- a/Graphics/GL/Low.hs
+++ b/Graphics/GL/Low.hs
@@ -1,3 +1,4 @@
+-- | Basic low-level GL wrapper and reference.
 module Graphics.GL.Low (
 
   -- * Overview
@@ -40,7 +41,7 @@
   --   GLFW.windowHint (GLFW.WindowHint'ContextVersionMinor 2)
   --   GLFW.windowHint (GLFW.WindowHint'OpenGLForwardCompat True)
   --   GLFW.windowHint (GLFW.WindowHint'OpenGLProfile GLFW.OpenGLProfile'Core)
-  --   mwin <- GLFW.createWindow 640 480 \"Demo\" Nothing Nothing
+  --   mwin <- GLFW.createWindow 640 480 \"Hello World\" Nothing Nothing
   --   case mwin of
   --     Nothing  -> putStrLn "createWindow failed"
   --     Just win -> do
@@ -107,7 +108,7 @@
   --
   -- And the output should look like
   --
-  -- <<demo.png Demo>>
+  -- <<hello_world.png Hello World>>
 
   -- * OpenGL API Basically
   --
@@ -145,16 +146,7 @@
   -- - Image attachment points of an FBO
 
   -- ** Shader Programs
-  -- | The role of the second half of a shader program, the fragment shader, is
-  -- to compute the color and depth of pixels covered by rasterized primitives
-  -- (points, lines, and triangles) in the process of rendering. The role of
-  -- the /first/ half of the program (vertex program) is to arrange the
-  -- vertices of those primitives somewhere in clip space. Where these vertices
-  -- and their attributes come from in the first place is determined by the VAO
-  -- bound to the vertex array binding target. The program may also make use of
-  -- uniform variables and texture units assigned by client code before
-  -- rendering (but in a process completely separate from configuring the VAO).
-  -- At most one Program can be "in use" at a time.
+  -- | See "Graphics.GL.Low.Shader"
 
   -- ** VAO
   -- | The VAO is essential. At least one VAO must be created and bound to the
diff --git a/Graphics/GL/Low/Blending.hs b/Graphics/GL/Low/Blending.hs
--- a/Graphics/GL/Low/Blending.hs
+++ b/Graphics/GL/Low/Blending.hs
@@ -1,4 +1,5 @@
--- | When blending is enabled, colors written to the color buffer will be
+-- | = Blending
+-- 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:
 --
@@ -11,19 +12,136 @@
 -- 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.
+-- = Example
 --
 -- @
--- blending example program here
+-- module Main where
+-- 
+-- import Control.Monad.Loops (whileM_)
+-- import Data.Functor ((\<$\>))
+-- import qualified Data.Vector.Storable as V
+-- import Control.Concurrent.STM
+-- 
+-- import qualified Graphics.UI.GLFW as GLFW
+-- import Linear
+-- import Graphics.GL.Low
+-- 
+-- main = do
+--   GLFW.init
+--   GLFW.windowHint (GLFW.WindowHint'ContextVersionMajor 3)
+--   GLFW.windowHint (GLFW.WindowHint'ContextVersionMinor 2)
+--   GLFW.windowHint (GLFW.WindowHint'OpenGLForwardCompat True)
+--   GLFW.windowHint (GLFW.WindowHint'OpenGLProfile GLFW.OpenGLProfile'Core)
+--   mwin <- GLFW.createWindow 640 480 \"Blending\" Nothing Nothing
+--   case mwin of
+--     Nothing  -> putStrLn "createWindow failed"
+--     Just win -> do
+--       GLFW.makeContextCurrent (Just win)
+--       GLFW.swapInterval 1
+--       shouldSwap <- newTVarIO False
+--       (GLFW.setKeyCallback win . Just)
+--         (\_ _ _ _ _ -> atomically (modifyTVar shouldSwap not))
+--       (vao, prog) <- setup
+--       whileM_ (not \<$\> GLFW.windowShouldClose win) $ do
+--         GLFW.pollEvents
+--         draw vao prog shouldSwap
+--         GLFW.swapBuffers win
+-- 
+-- setup = do
+--   vao <- newVAO
+--   bindVAO vao
+--   vsource <- readFile "blending.vert"
+--   fsource <- readFile "blending.frag"
+--   prog <- newProgram vsource fsource
+--   useProgram prog
+--   let blob = V.fromList
+--         [ -0.5,  0.5
+--         ,  0.5,    0
+--         , -0.5, -0.5 ] :: V.Vector Float
+--   vbo <- newVBO blob StaticDraw
+--   bindVBO vbo
+--   setVertexAttributeLayout [Attrib "position" 2 VFloat]
+--   enableBlending basicBlending
+--   return (vao, prog)
+-- 
+-- draw vao prog shouldSwap = do
+--   clearColorBuffer (0,0,0)
+--   yes <- readTVarIO shouldSwap
+--   if yes
+--     then sequence [drawRed, drawGreen]
+--     else sequence [drawGreen, drawRed]
+-- 
+-- drawGreen = do
+--   setUniform3f "color" [V3 0 1 0]
+--   setUniform1f "alpha" [0.5]
+--   setUniform44 "move" [eye4]
+--   drawTriangles 3
+-- 
+-- drawRed = do
+--   let ninety = pi/2
+--   let move = mkTransformation (axisAngle (V3 0 0 1) ninety) (V3 0.25 0.5 0)
+--   setUniform3f "color" [V3 1 0 0]
+--   setUniform1f "alpha" [0.5]
+--   setUniform44 "move" [transpose move]
+--   drawTriangles 3
 -- @
+-- 
+-- blending.vert
+--
+-- @
+-- #version 150
+-- 
+-- in vec3 Color;
+-- in float Alpha;
+-- out vec4 outColor;
+-- 
+-- void main()
+-- {
+--     outColor = vec4(Color, Alpha);
+-- }
+-- @
+--
+-- blending.frag
+--
+-- @
+-- #version 150
+-- 
+-- uniform vec3 color;
+-- uniform float alpha;
+-- uniform mat4 move;
+-- 
+-- in vec2 position;
+-- out vec3 Color;
+-- out float Alpha;
+-- 
+-- void main()
+-- {
+--     gl_Position = move * vec4(position, 0.0, 1.0);
+--     Color = color;
+--     Alpha = alpha;
+-- }
+-- @
+--
+-- This program draws two half-transparent shapes. When you press a key they
+-- are rendered in the opposite order. This makes one appear as if it were in
+-- front of the other. Because the depth test (see "Graphics.GL.Low.Depth")
+-- must be disabled while using this kind of blending, there may be significant
+-- overdraw in areas with many blending layers. This can harm performance.
+-- Also the order-dependency can make using alpha blending in a 3D scene
+-- complex or impossible. It may make more sense to use an off-screen render
+-- pass (see "Graphics.GL.Low.Framebuffer") and an appropriate shader to
+-- simulate transparency effects.
+--
+-- <<blending1.png Blending Before>> <<blending2.png Blending After>>
 
-module Graphics.GL.Low.Blending where
+module Graphics.GL.Low.Blending (
+  enableBlending,
+  disableBlending,
+  basicBlending,
+  Blending(..),
+  BlendFactor(..),
+  BlendEquation(..)
+) where
 
 import Data.Default
 import Graphics.GL
diff --git a/Graphics/GL/Low/BufferObject.hs b/Graphics/GL/Low/BufferObject.hs
--- a/Graphics/GL/Low/BufferObject.hs
+++ b/Graphics/GL/Low/BufferObject.hs
@@ -1,6 +1,42 @@
 {-# LANGUAGE ScopedTypeVariables #-}
--- | VBO and ElementArrays. Both are buffer objects but are used for two
--- different things.
+
+-- | Buffer Objects are objects for holding arbitrary blobs of bytes. This
+-- library exposes two types of buffer objects: VBOs and ElementArrays.
+--
+-- = VBO
+--
+-- Vertex Buffer Objects (VBO) contain data for a sequence of vertices. A
+-- vertex shader interprets the data for each vertex by mapping the attributes
+-- of the vertex (position, normal vector, etc) to input variables using the
+-- VAO. /VBOs have the data which is used as input to the vertex shader according to the configuration of the VAO/.
+--
+-- Example VBO contents:
+--
+-- <<vbo.png VBO diagram>>
+--
+-- The shader will interpret those parts of the VBO as illustrated only after
+-- appropiately configuring a VAO. See "Graphics.GL.Low.VAO".
+--
+-- = ElementArray
+--
+-- Element arrays are buffer objects that contain a sequence of indices. When
+-- using indexed rendering, the bound element array determines the order that
+-- the vertices in the VBOs are visited to construct primitives. This allows
+-- sharing vertices in cases that many vertices overlap with each other. OpenGL
+-- accepts element array objects whose indices are encoded as unsigned bytes,
+-- unsigned 16-bit ints, and unsigned 32-bit ints.
+--
+-- Example ElementArray contents appropriate for render triangles and lines
+-- respectively:
+--
+-- <<element_array.png Element array diagram>>
+--
+-- See 'Graphics.GL.Low.Render.drawIndexedTriangles' and friends to see which
+-- primitives are possible to construct with element arrays. It is not
+-- necessary to use element arrays to render. The non-indexed versions of
+-- the primitive render commands will simply traverse the vertices in order
+-- specified in the VBOs.
+
 module Graphics.GL.Low.BufferObject (
   VBO,
   ElementArray,
@@ -26,13 +62,10 @@
 
 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.
+-- | Handle to a VBO.
 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.
+-- | Handle to an element array buffer object.
 data ElementArray = ElementArray GLuint deriving Show
 
 -- | Usage hint for allocation of buffer object storage.
diff --git a/Graphics/GL/Low/Error.hs b/Graphics/GL/Low/Error.hs
--- a/Graphics/GL/Low/Error.hs
+++ b/Graphics/GL/Low/Error.hs
@@ -1,7 +1,18 @@
 {-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE PatternSynonyms #-}
-module Graphics.GL.Low.Error where
 
+-- | If you issue an invalid GL command or run out of memory, the GL
+-- implementation may set an internal flag indicating that an error has
+-- occurred. You can query this flag ('getGLError') to see if something went
+-- wrong with a previous command. If you ignore errors, chances are nothing
+-- will happen except perhaps some rendering weirdness. However checking this
+-- periodically is a good idea, perhaps once per frame, because it might
+-- indicate a bug in your code.
+module Graphics.GL.Low.Error (
+  GLError(..),
+  getGLError,
+) where
+
 import Control.Exception
 import Data.Typeable
 
@@ -25,7 +36,16 @@
   show InvalidFramebufferOperation = "INVALID_FRAMEBUFFER_OPERATION Framebuffer object is not complete"
   show OutOfMemory = "Not enough memory left to execute command"
 
--- | Check for a GL Error.
+-- | Check for a GL Error. This call has the semantics of a dequeue. If an
+-- error is returned, then calling getGLError again may return more errors that
+-- have "stacked up." When it returns Nothing then there are no more errors to
+-- report. An error indicates that a bug in your code caused incorrect ussage
+-- of the API or that the implementation has run out of memory.
+--
+-- It has been suggested that using this after every single GL command may
+-- adversely affect performance (not to mention be very tedious). Since there
+-- is no reasonable way to recover from a GL error, a good idea might be to
+-- check this once per frame or even less often, and respond with a core dump.
 getGLError :: IO (Maybe GLError)
 getGLError = do
   n <- glGetError
diff --git a/Graphics/GL/Low/Shader.hs b/Graphics/GL/Low/Shader.hs
--- a/Graphics/GL/Low/Shader.hs
+++ b/Graphics/GL/Low/Shader.hs
@@ -1,3 +1,178 @@
+-- | A shader program is composed of two cooperating parts: the vertex program
+-- and the fragment program. The vertex program is executed once for each
+-- vertex. The fragment program is executed once for each pixel covered by
+-- a rasterized primitive (actually this is more complicated but close enough).
+--
+-- The inputs to the vertex program are:
+--
+-- - a vertex (see "Graphics.GL.Low.VAO")
+-- - uniforms
+--
+-- The outputs of the vertex program are:
+--
+-- - clip space position of the vertex, gl_Position
+-- - any number of variables matching inputs to the fragment program
+-- - (if rendering a point, then you can set gl_PointSize)
+--
+-- The inputs to the fragment program are:
+--
+-- - the previously mentioned outputs of the vertex program (interpolated)
+-- - the window position of the pixel, gl_FragCoord
+-- - samplers (see "Graphics.GL.Low.Texture")
+-- - uniforms
+-- - gl_FrontFacing, true if pixel is part of a front facing triangle
+-- - (if rendering a point, then you can use gl_PointCoord)
+--
+-- The outputs of the fragment program are:
+--
+-- - a color (this is more complicated in reality but close enough)
+-- - the depth of the pixel, gl_FragDepth, which will default to the pixel's Z.
+--
+-- = Example
+--
+-- @
+-- module Main where
+-- 
+-- import Control.Monad.Loops (whileM_)
+-- import Data.Functor ((\<$\>))
+-- import qualified Data.Vector.Storable as V
+-- import Data.Maybe (fromJust)
+-- 
+-- import qualified Graphics.UI.GLFW as GLFW
+-- import Linear
+-- import Graphics.GL.Low
+-- 
+-- main = do
+--   GLFW.init
+--   GLFW.windowHint (GLFW.WindowHint'ContextVersionMajor 3)
+--   GLFW.windowHint (GLFW.WindowHint'ContextVersionMinor 2)
+--   GLFW.windowHint (GLFW.WindowHint'OpenGLForwardCompat True)
+--   GLFW.windowHint (GLFW.WindowHint'OpenGLProfile GLFW.OpenGLProfile'Core)
+--   mwin <- GLFW.createWindow 640 480 \"Shaders\" Nothing Nothing
+--   case mwin of
+--     Nothing  -> putStrLn "createWindow failed"
+--     Just win -> do
+--       GLFW.makeContextCurrent (Just win)
+--       GLFW.swapInterval 1
+--       (vao, prog1, prog2, prog3) <- setup
+--       whileM_ (not \<$\> GLFW.windowShouldClose win) $ do
+--         GLFW.pollEvents
+--         t <- (realToFrac . fromJust) \<$\> GLFW.getTime
+--         draw vao prog1 prog2 prog3 t
+--         GLFW.swapBuffers win
+-- 
+-- setup = do
+--   vao <- newVAO
+--   bindVAO vao
+--   vsource <- readFile "shader.vert"
+--   fsource1 <- readFile "shader1.frag"
+--   fsource2 <- readFile "shader2.frag"
+--   fsource3 <- readFile "shader3.frag"
+--   prog1 <- newProgram vsource fsource1
+--   prog2 <- newProgram vsource fsource2
+--   prog3 <- newProgram vsource fsource3
+--   useProgram prog1
+--   let blob = V.fromList
+--         [ -0.4, -0.4, 0, 0
+--         ,  0,    0.4, 0, 1
+--         ,  0.4, -0.4, 1, 1] :: V.Vector Float
+--   vbo <- newVBO blob StaticDraw
+--   bindVBO vbo
+--   setVertexAttributeLayout
+--     [ Attrib "position" 2 VFloat
+--     , Attrib "location" 2 VFloat ]
+--   return (vao, prog1, prog2, prog3)
+-- 
+-- draw vao prog1 prog2 prog3 t = do
+--   clearColorBuffer (0,0,0)
+--   bindVAO vao
+--   drawThing prog1 t (V3 (-0.5)   0.5    0.0)
+--   drawThing prog2 t (V3   0.5    0.5    0.0)
+--   drawThing prog3 t (V3   0.0  (-0.5) (-0.0))
+-- 
+-- drawThing :: Program -> Float -> V3 Float -> IO ()
+-- drawThing prog t shift = do
+--   let angle = t / 5
+--   let move = mkTransformation (axisAngle (V3 0 0 1) angle) shift
+--   useProgram prog
+--   setUniform1f "time" [t]
+--   setUniform44 "move" [transpose move]
+--   drawTriangles 3
+-- @
+--
+-- Where the vertex shader is
+--
+-- @
+-- #version 150
+-- uniform mat4 move;
+-- in vec2 position;
+-- in vec2 location;
+-- out vec2 Location;
+-- void main()
+-- {
+--     gl_Position = move * vec4(position, 0.0, 1.0);
+--     Location = location;
+-- }
+-- @
+--
+-- And the three fragment shaders are
+--
+-- @
+-- #version 150
+-- uniform float time;
+-- in vec2 Location;
+-- out vec4 outColor;
+-- void main()
+-- {
+--   float x = gl_FragCoord.x / 640;
+--   float y = gl_FragCoord.y / 480;
+--   outColor = vec4(
+--     fract(x*25) < 0.5 ? 1.0 : 0.0,
+--     fract(y*25) < 0.5 ? 1.0 : 0.0,
+--     0.0, 1.0
+--   );
+-- }
+-- @
+--
+-- @
+-- #version 150
+-- uniform float time;
+-- in vec2 Location;
+-- out vec4 outColor;
+-- void main()
+-- {
+--   outColor = vec4(
+--     fract(Location.x*10) < 0.5 ? 1.0 : 0.0,
+--     fract(Location.y*10) < 0.5 ? 1.0 : 0.0,
+--     0.0, 1.0
+--   );
+-- }
+-- @
+--
+-- @
+-- #version 150
+-- uniform float time;
+-- in vec2 Location;
+-- out vec4 outColor;
+-- void main()
+-- {
+--   float t = time;
+--   outColor = vec4(
+--     fract(Location.x*5) < 0.5 ? sin(t*3.145) : cos(t*4.567),
+--     fract(Location.y*5) < 0.5 ? cos(t*6.534) : sin(t*4.321),
+--     0.0, 1.0
+--   );
+-- }
+-- @
+--
+-- The output should look like
+--
+-- <<shaders.gif 3 Different Shaders Animated Demo>>
+--
+-- Where the window coordinates, the interpolated location on the triangle,
+-- and the elapsed time are used to color the triangle respectively.
+--
+
 {-# LANGUAGE DeriveDataTypeable #-}
 module Graphics.GL.Low.Shader (
   Program,
@@ -33,11 +208,7 @@
 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.
+-- | Handle to a shader program.
 newtype Program = Program GLuint deriving Show
 
 -- | Either a vertex shader or a fragment shader.
diff --git a/Graphics/GL/Low/Texture.hs b/Graphics/GL/Low/Texture.hs
--- a/Graphics/GL/Low/Texture.hs
+++ b/Graphics/GL/Low/Texture.hs
@@ -24,7 +24,7 @@
 import Data.Vector.Storable
 import Data.Word
 import Control.Applicative
-import Data.Traversable
+import Data.Traversable (sequenceA)
 
 import Graphics.GL
 
diff --git a/Graphics/GL/Low/VAO.hs b/Graphics/GL/Low/VAO.hs
--- a/Graphics/GL/Low/VAO.hs
+++ b/Graphics/GL/Low/VAO.hs
@@ -1,3 +1,57 @@
+-- | Vertex Array Objects (VAO). Despite having almost no operations of its
+-- own, the VAO mechanism is one of the most complex pieces of OpenGL. A VAO
+-- has mutable state which associates vertex shader input variables (actually
+-- the integer location of the variable) with three things:
+--
+-- - The VBO to read from.
+-- - The position in the each vertex array (in the VBO) to read from.
+-- - The interpretation of the bytes found there (32-bit float, 16-bit int, etc).
+--
+-- You set these VAO parameters with the following dance:
+--
+-- - Bind a VAO
+-- - Bind a VBO
+-- - Use a Program
+-- - call 'Graphics.GL.Low.VertexAttrib.setVertexAttributeLayout' which will
+-- lookup the position of the input variables and issue the appropriate
+-- glVertexAttribPointer calls.
+--
+-- After a VAO is configured against a Program, either can be swapped in or
+-- out freely and they will still work when both are swapped in again together.
+--
+-- An "in-use" program will use whatever VAO is bound for getting its vertex
+-- inputs. It is up to the programmer to ensure that the VAO has been
+-- configured with the right variable positions for the current program. Two
+-- ways to do this are to use a consistent set of variables for all shaders or
+-- restrict a set of VAOs to only be allowed with specific Program objects.
+--
+-- The currently bound VBO is not remembered or restored by binding a VAO, but
+-- the currently bound ElementArray is. This detail won't affect you if you
+-- always explicitly bind an ElementArray after binding a VAO. Alternatively
+-- you can exploit this to bundle particular models and their element arrays
+-- as a VAO.
+--
+-- Diagram of possible VAO contents:
+--
+-- <<vao.png VAO Diagram>>
+--
+-- The above VAO would be compatible with the following vertex program:
+--
+-- @
+-- #version 150
+-- 
+-- in vec2 position;
+-- in vec3 color;
+--
+-- out vec3 Color;
+-- 
+-- void main()
+-- {
+--     gl_Position = vec4(position, 0.0, 1.0);
+--     Color = color;
+-- }
+-- @
+
 module Graphics.GL.Low.VAO 
 (
   VAO,
@@ -13,9 +67,7 @@
 
 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.
+-- | Handle to a VAO.
 newtype VAO = VAO GLuint deriving Show
 
 instance GLObject VAO where
diff --git a/Graphics/GL/Low/VertexAttrib.hs b/Graphics/GL/Low/VertexAttrib.hs
--- a/Graphics/GL/Low/VertexAttrib.hs
+++ b/Graphics/GL/Low/VertexAttrib.hs
@@ -116,7 +116,8 @@
       let total = totalLayout layout
       forM_ layout' $ \(name, size, offset, fmt) -> do
         attrib <- withCString name $ \ptr -> glGetAttribLocation (fromIntegral p) (castPtr ptr)
-        let stride = total - size * sizeOfVertexComponent fmt
+        --let stride = total - size * sizeOfVertexComponent fmt
+        let stride = total
         let norm = isNormalized fmt
         glVertexAttribPointer
           (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.1.0
+version:             0.2.1.1
 
 -- A short (one-line) description of the package.
 synopsis:            Basic gl wrapper and reference
@@ -89,4 +89,4 @@
 source-repository this
   type: git
   location: https://github.com/evanrinehart/lowgl
-  tag: 0.2.1.0
+  tag: 0.2.1.1
