diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Liam O'Connor (c) 2015
+
+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 Liam O'Connor 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/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/latex-formulae-image.cabal b/latex-formulae-image.cabal
new file mode 100644
--- /dev/null
+++ b/latex-formulae-image.cabal
@@ -0,0 +1,52 @@
+name:                latex-formulae-image
+version:             0.1.0.0
+synopsis:            A library for rendering LaTeX formulae as images using an actual LaTeX installation
+description:         This library provides the basic infrastructure necessary to convert LaTeX formulae into images, using a real
+                     LaTeX installation. This is useful in particular for showing formulae on websites, where using alternatives
+                     like MathJax is not an option (e.g, when you want to use various LaTeX packages that MathJax doesn't support).
+                     .
+                     This library requires @latex@, @dvips@ and ImageMagick's @convert@ to be present in the system. @convert@
+                     needs to understand PostScript files.
+                     .
+                     We also
+                     use a few tricks to compute where the baseline is in the image, so these images can be effectively typeset inline
+                     with other text.
+                     .
+                     The companion library to this, @latex-formulae-pandoc@, provides useful tools to integrate this library with pandoc,
+                     when generating HTML documents.
+homepage:            http://github.com/liamoc/latex-formulae#readme
+license:             BSD3
+license-file:        LICENSE
+author:              Liam O'Connor
+maintainer:          liamoc@cse.unsw.edu.au
+copyright:           Liam O'Connor, 2015
+category:            Image
+build-type:          Simple
+-- extra-source-files:
+cabal-version:       >=1.10
+source-repository head
+  type:     git
+  location: https://github.com/liamoc/latex-formulae
+flag old-base
+  description: if building with older base versions
+  default: False
+
+library
+  hs-source-dirs:      src
+  exposed-modules:     Image.LaTeX.Render
+  if flag(old-base)
+     build-depends: base >= 4.7 && < 4.8
+                  , transformers-compat >= 0.4 && < 0.5
+  else
+     build-depends: base >= 4.8 && < 4.9
+  build-depends:       JuicyPixels >= 3.2 && < 3.3, errors >= 2.0 && < 2.1
+                     , temporary >= 1.2 && < 1.3
+                     , transformers >= 0.3 && < 0.5
+                     , directory >= 1.2 && < 1.3
+                     , process >= 1.2 && < 1.3
+                     , filepath >= 1.3 && < 1.5
+  default-language:    Haskell2010
+
+source-repository head
+  type:     git
+  location: https://github.com/liamoc/latex-formulae-image
diff --git a/src/Image/LaTeX/Render.hs b/src/Image/LaTeX/Render.hs
new file mode 100644
--- /dev/null
+++ b/src/Image/LaTeX/Render.hs
@@ -0,0 +1,169 @@
+{-# LANGUAGE FlexibleContexts, RecordWildCards #-}
+module Image.LaTeX.Render
+       ( -- * Rendering Formulas
+         imageForFormula
+       , Formula, Baseline
+         -- * Errors
+       , RenderError (..)
+         -- * Options
+         -- ** Environment Options
+       , EnvironmentOptions (..)
+       , defaultEnv
+       , TempDirectoryHandling (..)
+         -- ** Formula Options
+       , FormulaOptions (..)
+       , displaymath
+       , math
+       )
+       where
+
+import Codec.Picture
+import Data.Maybe
+import Control.Error.Util
+import Data.List
+import System.IO.Temp
+import System.FilePath
+import System.Process
+import System.Directory
+import Control.Monad.Trans.Except
+import Control.Monad
+import System.Exit
+import Control.Exception
+import Control.Arrow(second)
+import Control.Applicative
+import Data.Monoid
+import Prelude
+
+-- | This type contains all possible errors than can happen while rendering an equation.
+--   It includes all IO errors that can happen as well as more specific errors.
+data RenderError = ImageIsEmpty -- ^ The equation produced an empty image
+                 | CannotDetectBaseline -- ^ The baseline marker could not be found
+                 | LaTeXFailure String -- ^ @latex@ returned a nonzero error code
+                 | DVIPSFailure String -- ^ @dvips@ returned a nonzero error code
+                 | IMConvertFailure String -- ^ @convert@ returned a nonzero error code
+                 | IOException IOException -- ^ An 'IOException' occurred while managing the temporary files used to convert the equation
+                 | ImageReadError String -- ^ The PNG image from ImageMagick could not be read by JuicyPixels.
+                 deriving (Show)
+
+
+data TempDirectoryHandling = UseSystemTempDir { nameTemplate :: String }
+                           -- ^ A temporary directory with a name based on the given template will be created in the system temporary files location
+                           | UseCurrentDir    { nameTemplate :: String }
+                           -- ^ A temporary directory with a name based on the given template will be created in the current directory
+
+data EnvironmentOptions
+     = EnvironmentOptions { latexCommand :: String -- ^ Command to use for @latex@, default is @latex@
+                          , dvipsCommand :: String -- ^ Command to use for @dvips@, default is @dvips@
+                          , imageMagickCommand :: String -- ^ Command to use for ImageMagick's @convert@, default is @convert@
+                          , latexArgs :: [String] -- ^ Any additional arguments for @latex@
+                          , dvipsArgs :: [String] -- ^ Any additional arguments for @dvips@
+                          , imageMagickArgs :: [String] -- ^ Any additional arguments for @convert@
+                          , tempDir :: TempDirectoryHandling -- ^ How to handle temporary files
+                          , tempFileBaseName :: String -- ^ The base name to use for the temporary files.
+                          }
+
+data FormulaOptions
+     = FormulaOptions { preamble :: String -- ^ LaTeX preamble to use. Put your @\usepackage@ commands here.@ commands here.
+                       , environment :: String -- ^ LaTeX environment in which the equation will be typeset, usually @math@ or @displaymath@
+                       , dpi :: Int -- ^ DPI for the image to be rendered at. ~200 is good for retina displays, ~100 works OK for non-retina displays.
+                       }
+
+-- | Use the @amsmath@ package, the @displaymath@ environment, and 200dpi.
+displaymath :: FormulaOptions
+displaymath = FormulaOptions "\\usepackage{amsmath}" "displaymath" 200
+
+-- | Use the @amsmath@ package, the @math@ environment, and 200dpi.
+math :: FormulaOptions
+math = FormulaOptions "\\usepackage{amsmath}\\usepackage{amsfonts}\\usepackage{stmaryrd}" "math" 200
+
+-- | Sensible defaults for system environments. Works if @dvips@, @convert@, and @latex@ are recent enough and in your @$PATH@.
+defaultEnv :: EnvironmentOptions
+defaultEnv = EnvironmentOptions "latex" "dvips" "convert" [] [] [] (UseSystemTempDir "latex-eqn-temp") "working"
+
+-- | A LaTeX formula, e.g @x=\frac{-b\pm\sqrt{b^2-4ac}}{2a}@ for the quadratic formula. Do not include any @$@s to denote the environment, just
+--   specify the environment in the 'FormulaOptions' instead.
+type Formula = String
+
+-- | Number of pixels from the bottom of the image to the typesetting baseline. Useful for setting your formulae inline with text.
+type Baseline = Int
+
+
+
+-- | Convert a formula into a JuicyPixels 'DynamicImage', also detecting where the typesetting baseline of the image is.
+imageForFormula :: EnvironmentOptions -> FormulaOptions -> Formula -> IO (Either RenderError (Baseline, DynamicImage))
+imageForFormula (EnvironmentOptions {..}) (FormulaOptions {..}) eqn =
+    bracket getCurrentDirectory setCurrentDirectory $ const $ withTemp $ \temp -> runExceptT $ do
+      let doc = mconcat ["\\nonstopmode\n",
+                 "\\documentclass[12pt]{article}\n",
+                 "\\pagestyle{empty}\n", preamble,
+                 "\\begin{document}\n",
+                 "\\begin{", environment, "}\n",
+                 ".",eqn,
+                 "\\end{", environment, "}\n",
+                 "\\end{document}\n"]
+      io $ writeFile (temp </> tempFileBaseName <.> "tex") doc
+      io $ setCurrentDirectory temp
+      (c,o,e) <- io $ flip (readProcessWithExitCode latexCommand) "" $ latexArgs ++ [tempFileBaseName <.> "tex"]
+      io $ removeFile (tempFileBaseName <.> "tex") 
+      io $ removeFile (tempFileBaseName <.> "aux")
+      when (c /= ExitSuccess) $ do
+        io $ removeFile (tempFileBaseName <.> "dvi")
+        throwE $ LaTeXFailure (o ++ "\n" ++ e)
+      (c',o',e') <- io $ flip (readProcessWithExitCode dvipsCommand) "" $ dvipsArgs ++ ["-q", "-E", "-o", tempFileBaseName <.> "ps", tempFileBaseName <.> "dvi"]
+      io $ removeFile (tempFileBaseName <.> "dvi")
+      when (c' /= ExitSuccess) $ throwE $ DVIPSFailure (o' ++ "\n" ++ e')
+      (c'', o'', e'') <- io $ flip (readProcessWithExitCode imageMagickCommand) "" $
+                                [ "-density", show dpi
+                                , "-bordercolor", "none"
+                                , "-border", "1x1"
+                                , "-trim"
+                                , "-background", "none"
+                                , "-splice","1x0"
+                                ] ++ imageMagickArgs ++
+                                [ tempFileBaseName <.> "ps", tempFileBaseName <.> "png" ]
+      io $ removeFile (tempFileBaseName <.> "ps")
+      when (c'' /= ExitSuccess) $ throwE $ IMConvertFailure (o'' ++ "\n" ++ e'')
+      imgM <- io $ readImage (tempFileBaseName <.> "png")
+      img <- withExceptT ImageReadError $ hoistEither imgM
+      io $ removeFile $ tempFileBaseName <.> "png"
+      hoistEither $ postprocess img
+  where
+    io = withExceptT IOException . tryIO
+    withTemp a = case tempDir of
+      UseSystemTempDir f -> withSystemTempDirectory f a
+      UseCurrentDir f -> withTempDirectory "." f a
+
+postprocess :: DynamicImage -> Either RenderError (Int, DynamicImage)
+postprocess (ImageY8 i)     = second ImageY8     <$> postprocess' i (pixelAt i 0 0)
+postprocess (ImageY16 i)    = second ImageY16    <$> postprocess' i (pixelAt i 0 0)
+postprocess (ImageYF i)     = second ImageYF     <$> postprocess' i (pixelAt i 0 0)
+postprocess (ImageYA8 i)    = second ImageYA8    <$> postprocess' i (pixelAt i 0 0)
+postprocess (ImageYA16 i)   = second ImageYA16   <$> postprocess' i (pixelAt i 0 0)
+postprocess (ImageRGB8 i)   = second ImageRGB8   <$> postprocess' i (pixelAt i 0 0)
+postprocess (ImageRGB16 i)  = second ImageRGB16  <$> postprocess' i (pixelAt i 0 0)
+postprocess (ImageRGBF i)   = second ImageRGBF   <$> postprocess' i (pixelAt i 0 0)
+postprocess (ImageRGBA8 i)  = second ImageRGBA8  <$> postprocess' i (pixelAt i 0 0)
+postprocess (ImageRGBA16 i) = second ImageRGBA16 <$> postprocess' i (pixelAt i 0 0)
+postprocess (ImageYCbCr8 i) = second ImageYCbCr8 <$> postprocess' i (pixelAt i 0 0)
+postprocess (ImageCMYK8 i)  = second ImageCMYK8  <$> postprocess' i (pixelAt i 0 0)
+postprocess (ImageCMYK16 i) = second ImageCMYK16 <$> postprocess' i (pixelAt i 0 0)
+
+
+postprocess' :: (Eq a, Pixel a) => Image a -> a -> Either RenderError (Int, Image a)
+postprocess' img bg
+  = do startX <- note ImageIsEmpty $ listToMaybe $ dropWhile isEmptyCol [0.. imageWidth img - 1]
+       let (dotXs, postXs) = break isEmptyCol [startX .. imageWidth img]
+       postX <- note CannotDetectBaseline $ listToMaybe postXs
+       let postY = (+ 2) $ average $ dotXs >>= (\x -> takeWhile (not . isEmpty x) (dropWhile (isEmpty x) [0..imageHeight img - 1]))
+           average = uncurry div . foldl' (\(s,c) e -> (e+s,c+1)) (0,0)
+           newHeight = imageHeight img
+           newWidth  = imageWidth img - postX + 3
+           baseline  = imageHeight img - postY
+       let image = generateImage (pixelAt' . (+ postX)) newWidth newHeight
+       return (baseline, image)
+  where
+    isEmptyCol x = all (isEmpty x) [0.. imageHeight img - 1]
+    isEmpty x = (== bg) . pixelAt img x
+    pixelAt' x y | x < imageWidth img && y < imageHeight img = pixelAt img x y
+                 | otherwise = bg
+
