diff --git a/Web/Herringbone.hs b/Web/Herringbone.hs
--- a/Web/Herringbone.hs
+++ b/Web/Herringbone.hs
@@ -32,6 +32,8 @@
     module Web.Herringbone.Configuration,
     -- * Assets
     LogicalPath,
+    makeLogicalPath,
+    unsafeMakeLogicalPath,
     fromLogicalPath,
     toFilePath,
     Asset(..),
@@ -42,6 +44,7 @@
     AssetError(..),
     CompileError,
     PPReader(..),
+    ppReaderFileName,
     PPM,
     -- * WAI
     toApplication
diff --git a/Web/Herringbone/Preprocessor/CoffeeScript.hs b/Web/Herringbone/Preprocessor/CoffeeScript.hs
new file mode 100644
--- /dev/null
+++ b/Web/Herringbone/Preprocessor/CoffeeScript.hs
@@ -0,0 +1,9 @@
+module Web.Herringbone.Preprocessor.CoffeeScript (
+    coffeeScript
+) where
+
+import Web.Herringbone
+import Web.Herringbone.Preprocessor.StdIO
+
+coffeeScript :: PP
+coffeeScript = makeStdIOPP "coffee" "coffee" ["--stdio", "--print"]
diff --git a/Web/Herringbone/Preprocessor/Sass.hs b/Web/Herringbone/Preprocessor/Sass.hs
new file mode 100644
--- /dev/null
+++ b/Web/Herringbone/Preprocessor/Sass.hs
@@ -0,0 +1,15 @@
+module Web.Herringbone.Preprocessor.Sass (
+    sass,
+    scss
+) where
+
+import Web.Herringbone
+import Web.Herringbone.Preprocessor.StdIO
+
+-- | A preprocessor for the sass mode of Sass.
+sass :: PP
+sass = makeStdIOPP "sass" "sass" ["--stdin"]
+
+-- | A preprocessor for the scss mode of Sass.
+scss :: PP
+scss = makeStdIOPP "scss" "sass" ["--stdin", "--scss"]
diff --git a/Web/Herringbone/Preprocessor/StdIO.hs b/Web/Herringbone/Preprocessor/StdIO.hs
new file mode 100644
--- /dev/null
+++ b/Web/Herringbone/Preprocessor/StdIO.hs
@@ -0,0 +1,47 @@
+module Web.Herringbone.Preprocessor.StdIO (
+    makeStdIOPP
+) where
+
+import Control.Monad.IO.Class
+import Data.Monoid
+import Data.Text (Text)
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Char8 as C8
+import System.Exit
+import System.Process.ByteString
+import Web.Herringbone
+
+-- | Make a preprocessor which works over standard IO; reading input from
+-- stdin, and writing output to stdout.
+makeStdIOPP :: Text     -- ^ File extension
+            -> String   -- ^ Program name
+            -> [String] -- ^ Arguments
+            -> PP
+makeStdIOPP ext progname args = PP
+    { ppExtension = ext
+    , ppAction = compile progname args
+    }
+    
+compile :: String
+        -> [String]
+        -> ByteString
+        -> PPM (Either CompileError ByteString)
+compile progname args source = do
+    liftIO $ readAllFromProcess progname args source
+
+-- | Read from a process returning both std err and out.
+readAllFromProcess :: String        -- ^ Program
+                   -> [String]      -- ^ Args
+                   -> ByteString    -- ^ Stdin
+                   -> IO (Either ByteString ByteString)
+readAllFromProcess program flags input = do
+  (code,out,err) <- readProcessWithExitCode program flags input
+  return $ case code of
+    ExitFailure 127 -> Left $ "cannot find executable " <> C8.pack program
+    ExitFailure _ -> Left $ join (err, out)
+    ExitSuccess -> Right $ join (err, out)
+  where
+  join (err, out) = if B.null err
+                        then out
+                        else err <> "\n" <> out
diff --git a/Web/Herringbone/Types.hs b/Web/Herringbone/Types.hs
--- a/Web/Herringbone/Types.hs
+++ b/Web/Herringbone/Types.hs
@@ -1,6 +1,7 @@
 module Web.Herringbone.Types where
 
 import Control.Monad.Reader
+import Control.Applicative
 import Data.Char
 import Data.Time.Clock
 import Data.Time.Format
@@ -44,9 +45,12 @@
     }
     deriving (Show, Eq)
 
+ppReaderFileName :: PPReader -> FilePath
+ppReaderFileName = F.fromText . last . fromLogicalPath . ppReaderLogicalPath
+
 -- | A monad in which preprocessor actions happen.
 newtype PPM a = PPM { unPPM :: ReaderT PPReader IO a }
-    deriving (Monad, MonadIO, (MonadReader PPReader))
+    deriving (Functor, Applicative, Monad, MonadIO, (MonadReader PPReader))
 
 runPPM :: PPM a -> PPReader -> IO a
 runPPM comp readerData = runReaderT (unPPM comp) readerData
@@ -76,8 +80,8 @@
 instance Show PP where
     show pp = "<PP: " ++ show (ppExtension pp) ++ ">"
 
--- | Beware: This instance is only here for testing. It only looks at the
--- extensions to decide whether two 'PP's are equal. Don't use this!
+-- | Beware: This instance only looks at the extensions to decide whether two
+-- 'PP's are equal.
 instance Eq PP where
     (PP ext1 _) == (PP ext2 _) = ext1 == ext2
 
diff --git a/herringbone.cabal b/herringbone.cabal
--- a/herringbone.cabal
+++ b/herringbone.cabal
@@ -1,5 +1,5 @@
 name:                herringbone
-version:             0.0.5
+version:             0.0.6
 synopsis:            A library for compiling and serving static web assets.
 description:         A library for compiling and serving static web assets. For more information, please see <https://github.com/hdgarrood/herringbone>.
 license:             MIT
@@ -25,9 +25,12 @@
                         text,
                         bytestring,
                         containers,
+                        transformers,
                         mtl,
                         system-filepath,
                         system-fileio,
+                        process,
+                        process-listlike,
                         directory,
                         unix-compat,
                         http-types,
@@ -45,7 +48,10 @@
 
 library
   ghc-options:          -Wall
-  exposed-modules:      Web.Herringbone
+  exposed-modules:      Web.Herringbone,
+                        Web.Herringbone.Preprocessor.StdIO
+                        Web.Herringbone.Preprocessor.Sass
+                        Web.Herringbone.Preprocessor.CoffeeScript
   other-modules:        Web.Herringbone.Types
                         Web.Herringbone.Configuration
                         Web.Herringbone.LocateAssets
@@ -58,9 +64,12 @@
                         text,
                         bytestring,
                         containers,
+                        transformers,
                         mtl,
                         system-filepath,
                         system-fileio,
+                        process,
+                        process-listlike,
                         directory,
                         unix-compat,
                         http-types,
@@ -72,3 +81,10 @@
                         RankNTypes,
                         GeneralizedNewtypeDeriving
   default-language:     Haskell2010
+
+executable herringbone-test-server
+  hs-source-dirs:   test
+  build-depends:    base, herringbone, text, warp
+  main-is:          TestServer.hs
+  default-language: Haskell2010
+  default-extensions: OverloadedStrings
diff --git a/test/TestServer.hs b/test/TestServer.hs
new file mode 100644
--- /dev/null
+++ b/test/TestServer.hs
@@ -0,0 +1,9 @@
+module Main where
+
+import TestHerringbone
+
+main :: IO ()
+main = do
+    let port = 3002 :: Int
+    putStrLn $ "starting test server on port " ++ show port ++ "..."
+    runTestHB port
