diff --git a/app/heatmap/Main.hs b/app/heatmap/Main.hs
--- a/app/heatmap/Main.hs
+++ b/app/heatmap/Main.hs
@@ -1,7 +1,7 @@
 module Main where
 
-import Control.Monad (forM_)
-import Data.Ratio
+-- import Control.Monad (forM_)
+-- import Data.Ratio
 
 import Graphics.Rendering.Plot.Light
 import Graphics.Rendering.Plot.Light.PlotTypes
@@ -17,7 +17,6 @@
 import qualified Data.Colour as C
 import qualified Data.Colour.Palette.BrewerSet as CP
 
--- main = print "hello"
 
 
 fname = "data/heatmap-bw"
@@ -33,72 +32,16 @@
 palette :: [C.Colour Double]
 palette = CP.brewerSet CP.GnBu nColors
 
+main :: IO ()
 main = do
   dat <- T.readFile fname
   let pd = A.parseOnly (gridNum space) dat
   case pd of Left e -> error e
              Right d -> do
-               let (nh, nw, vmin, vmax, d') = prepData d
-                   w = xPlot / nw
-                   h = yPlot / nh
-                   from = Frame (Point 0 0) (Point 1 1)
-                   to = frameFromFigData fdat
-                   pixels = forM_ d' (mkPixel w h vmin vmax . toFigFrame from to)
-                   svg_t = svgHeader (mkFrameOrigin xPlot yPlot) pixels
---                -- putStrLn $ renderSvg svg_t
-               T.writeFile fnameOut $ T.pack $ renderSvg svg_t   
-
-toFigFrame
-  :: Fractional a =>
-     Frame a -> Frame a -> LabeledPoint l Rational -> LabeledPoint l a
-toFigFrame from to = moveLabeledPointBwFrames from to False False . fromRationalLP
-
-fromRationalLP :: Fractional a => LabeledPoint l Rational -> LabeledPoint l a
-fromRationalLP (LabeledPoint (Point x y) l) = LabeledPoint (Point (fromRational x) (fromRational y)) l
-
-
-mkPixel
-  :: (Show a, RealFrac a) =>
-     a
-     -> a
-     -> Scientific
-     -> Scientific
-     -> LabeledPoint Scientific a
-     -> Svg
-mkPixel w h vmin vmax (LabeledPoint p l) = rect w h 0 Nothing (Just col) p where
-  col = pickColor (toFloat vmin) (toFloat vmax) (toFloat l)
-  
-
-pickColor :: RealFrac t => t -> t -> t -> C.Colour Double
-pickColor xmin xmax x = palette !! i
-  where
-    i = floor (x01 * fromIntegral (nColors - 1))
-    x01 = (x-xmin)/(xmax - xmin)
-
-
-
-
-prepData ::
-  Ord t => [[t]] -> (Rational, Rational, t, t, [LabeledPoint t Rational])
-prepData ll = (nh, nw, valMin, valMax, d')
-  where
-    nh = toRational $ length ll
-    nw = toRational $ length (head ll)
-    d' = toUnitFramedLP nw nh <$> toCoord ll
-    valMin = minimum $ _lplabel <$> d'
-    valMax = maximum $ _lplabel <$> d'
-    
-   
-
-toCoord :: (Num i, Enum i) => [[c]] -> [(i, i, c)]
-toCoord ll = concat $ reverse $ go 0 ll [] where
-  go i (x:xs) acc = go (i + 1) xs $ zip3 (repeat i) [0 ..] x : acc
-  go _ [] acc = acc
-
-toUnitFramedLP :: (Fractional t) =>
-      t -> t -> (t, t, l) -> LabeledPoint l t
-toUnitFramedLP w h (i, j, x) = LabeledPoint p x
-  where p = Point (i/h) (j/w)
+                  let pixels = heatmap fdat palette d
+                      svg_t = svgHeader (mkFrameOrigin xPlot yPlot) pixels
+                  -- putStrLn $ renderSvg svg_t
+                  T.writeFile fnameOut $ T.pack $ renderSvg svg_t      
 
 
 
diff --git a/plot-light.cabal b/plot-light.cabal
--- a/plot-light.cabal
+++ b/plot-light.cabal
@@ -1,7 +1,7 @@
 name:                plot-light
-version:             0.2.3
+version:             0.2.4
 synopsis:            A lightweight plotting library, exporting to SVG
-description:         A lightweight plotting library, exporting to SVG
+description:         This library provides drawing and plotting datastructures and functions; it is aimed in particular at scientific visualization, but it also exposes its plotting primitives and a general purpose 2D geometry library.
 homepage:            https://github.com/ocramz/plot-light
 license:             BSD3
 license-file:        LICENSE
diff --git a/src/Graphics/Rendering/Plot/Light.hs b/src/Graphics/Rendering/Plot/Light.hs
--- a/src/Graphics/Rendering/Plot/Light.hs
+++ b/src/Graphics/Rendering/Plot/Light.hs
@@ -20,6 +20,8 @@
 -- @import qualified Data.Colour.Names as C@
 
 module Graphics.Rendering.Plot.Light (
+  -- * Plot types
+  heatmap,
   -- * Plot elements
   -- ** Geometrical primitives
   rect, rectCentered, circle, line, text, polyline, filledPolyline,
@@ -66,7 +68,7 @@
 -- import qualified Data.Colour.SRGB as C
 
 import Graphics.Rendering.Plot.Light.Internal
-
+import Graphics.Rendering.Plot.Light.PlotTypes
 
 
 
diff --git a/src/Graphics/Rendering/Plot/Light/PlotTypes/Heatmap.hs b/src/Graphics/Rendering/Plot/Light/PlotTypes/Heatmap.hs
--- a/src/Graphics/Rendering/Plot/Light/PlotTypes/Heatmap.hs
+++ b/src/Graphics/Rendering/Plot/Light/PlotTypes/Heatmap.hs
@@ -1,14 +1,94 @@
-module Graphics.Rendering.Plot.Light.PlotTypes.Heatmap where
-
+module Graphics.Rendering.Plot.Light.PlotTypes.Heatmap (heatmap) where
 
+import Data.Scientific (Scientific, toRealFloat)
 import Graphics.Rendering.Plot.Light.Internal
 
+import Text.Blaze.Svg
 
+import Control.Monad (forM_)
 
+-- import qualified Data.Colour.Names as C
+import qualified Data.Colour as C
+import Data.Colour.Palette.BrewerSet
+-- import Text.Blaze.Svg
 
 
+-- | A 2D heatmap plot
+--
+-- `heatmap` assumes the input data corresponds to evenly sampled values of a scalar-valued field, and it maps the data values onto the provided `palette` (which can be created e.g. with `brewerSet`).
+heatmap
+  :: FigureData Rational   -- ^ Figure data
+     -> [C.Colour Double]  -- ^ Colour palette
+     -> [[Scientific]]     -- ^ Data
+     -> Svg
+heatmap fdat palette d = do
+  let (nh, nw, vmin, vmax, d') = prepData d
+      w = figWidth fdat / nw
+      h = figHeight fdat / nh
+      from = Frame (Point 0 0) (Point 1 1)
+      to = frameFromFigData fdat
+  forM_ d' (mkPixel palette w h vmin vmax . toFigFrame from to) 
 
--- hmPlot fd
+
+toFigFrame
+  :: Fractional a =>
+     Frame a -> Frame a -> LabeledPoint l Rational -> LabeledPoint l a
+toFigFrame from to = moveLabeledPointBwFrames from to False False . fromRationalLP
+
+fromRationalLP :: Fractional a => LabeledPoint l Rational -> LabeledPoint l a
+fromRationalLP (LabeledPoint (Point x y) l) = LabeledPoint (Point (fromRational x) (fromRational y)) l
+
+
+mkPixel
+  :: (Show a, RealFrac a) =>
+     [C.Colour Double]
+     -> a
+     -> a
+     -> Scientific
+     -> Scientific
+     -> LabeledPoint Scientific a
+     -> Svg
+mkPixel palette w h vmin vmax (LabeledPoint p l) = rect w h 0 Nothing (Just col) p where
+  col = pickColor palette (toFloat vmin) (toFloat vmax) (toFloat l)
+  
+
+pickColor :: RealFrac t => [C.Colour Double] -> t -> t -> t -> C.Colour Double
+pickColor palette xmin xmax x = palette !! i
+  where
+    i = floor (x01 * fromIntegral (nColors - 1))
+    x01 = (x-xmin)/(xmax - xmin)
+    nColors = length palette
+
+
+
+-- | `prepData d` assumes the input lists correspond to evenly sampled values of a scalar-valued field.
+--
+-- The function extracts the pixel mesh size, the data ranges and places the data points within the unit square [0,1] x [0,1]
+prepData ::
+  (Ord t, Fractional a, Enum a) =>
+     [[t]]  -- ^ Data
+  -> (a, a, t, t, [LabeledPoint t a])  -- ^ (# of pixel rows, # of pixel columns, data minimum, data maximum, data points)
+prepData ll = (nh, nw, valMin, valMax, d')
+  where
+    nh = fromIntegral $ length ll
+    nw = fromIntegral $ length (head ll)
+    -- nh = toRational $ length ll
+    -- nw = toRational $ length (head ll)    
+    d' = toUnitFramedLP nw nh <$> toCoord ll
+    valMin = minimum $ _lplabel <$> d'
+    valMax = maximum $ _lplabel <$> d'
+    
+   
+
+toCoord :: (Num i, Enum i) => [[c]] -> [(i, i, c)]
+toCoord ll = concat $ reverse $ go 0 ll [] where
+  go i (x:xs) acc = go (i + 1) xs $ zip3 (repeat i) [0 ..] x : acc
+  go _ [] acc = acc
+
+toUnitFramedLP :: (Fractional t) =>
+      t -> t -> (t, t, l) -> LabeledPoint l t
+toUnitFramedLP w h (i, j, x) = LabeledPoint p x
+  where p = Point (i/h) (j/w)
 
 
 
