diff --git a/FractalArt.cabal b/FractalArt.cabal
new file mode 100644
--- /dev/null
+++ b/FractalArt.cabal
@@ -0,0 +1,40 @@
+name:                FractalArt
+version:             0.2.0.0
+synopsis:            Generates colorful wallpapers
+description:
+	This application generates colorful images as a wallpaper
+license:             MIT
+license-file:        LICENSE
+author:              Tom
+maintainer:          Tom.TSmeets@Gmail.com
+category:            Graphics
+build-type:          Simple
+cabal-version:       >=1.10
+
+executable FractalArt
+  main-is:          Main.hs
+  include-dirs:     cbits
+  if os(linux)
+      extra-libraries:  X11
+  if os(darwin)
+      cc-options:       -fmodules
+      extra-libraries:  objc
+      c-sources:        cbits/wallpaper.m
+  other-extensions: ForeignFunctionInterface
+  if !os(darwin)
+      c-sources:        cbits/wallpaper.c
+  build-depends:    base        >=4.7  && <4.9
+                  , random      >=1.0  && <1.2
+                  , mwc-random  >=0.13 && <0.14
+                  , directory   >=1.2  && <1.3
+                  , filepath    >=1.3  && <1.5
+                  , primitive   >=0.5  && <0.6
+                  , vector      >=0.10 && <0.11
+                  , JuicyPixels >=3.2  && <3.3
+  hs-source-dirs:   src
+  default-language: Haskell2010
+  ghc-options: -O2
+
+source-repository head
+  type:     git
+  location: git://github.com/TomSmeets/FractalArt.git
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Tom Smeets
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
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/cbits/wallpaper.c b/cbits/wallpaper.c
new file mode 100644
--- /dev/null
+++ b/cbits/wallpaper.c
@@ -0,0 +1,83 @@
+#include <stdio.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#elif __linux
+#include <X11/Xlib.h>
+#endif
+
+#include "wallpaper.h"
+
+#ifdef __linux
+int getRootWindowSize(int *w, int *h)
+{
+    Display* pdsp = NULL;
+    Window wid = 0;
+    XWindowAttributes xwAttr;
+
+    pdsp = XOpenDisplay( NULL );
+    if ( !pdsp ) {
+        fprintf(stderr, "Failed to open default display.\n");
+        return -1;
+    }
+
+    wid = DefaultRootWindow( pdsp );
+    if ( 0 > wid ) {
+        fprintf(stderr, "Failed to obtain the root windows Id "
+                        "of the default screen of given display.\n");
+        return -2;
+    }
+
+    Status ret = XGetWindowAttributes( pdsp, wid, &xwAttr );
+    *w = xwAttr.width;
+    *h = xwAttr.height;
+
+    XCloseDisplay( pdsp );
+    return 0;
+}
+
+int
+get_screen_size_x()
+{
+    unsigned int w, h;
+    getRootWindowSize(&w,&h);
+    return w;
+}
+
+int
+get_screen_size_y()
+{
+    unsigned int w, h;
+    getRootWindowSize(&w,&h);
+    return h;
+}
+
+void
+set_wallpaper(char *str)
+{
+    printf("can't automatically set wallpaper on Linux, please set your wallpaper path to ~/.fractalart/wallpaper.bmp\n");
+}
+
+#elif _WIN32
+
+int
+get_screen_size_x()
+{
+    return GetSystemMetrics(SM_CXVIRTUALSCREEN);
+}
+
+int
+get_screen_size_y()
+{
+    return GetSystemMetrics(SM_CYVIRTUALSCREEN);
+}
+
+void
+set_wallpaper(char *str)
+{
+    printf("Setting Wallpaper on Windows\n");
+    SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, str, SPIF_UPDATEINIFILE);
+}
+#else
+#error Unsuporrted OS
+#endif
diff --git a/cbits/wallpaper.m b/cbits/wallpaper.m
new file mode 100644
--- /dev/null
+++ b/cbits/wallpaper.m
@@ -0,0 +1,36 @@
+#include <stdlib.h>
+
+@import AppKit;
+
+NSSize getRootWindowSize()
+{
+    @autoreleasepool {
+        NSRect frame = [NSScreen mainScreen].frame;
+        return frame.size;
+    }
+}
+
+int
+get_screen_size_x()
+{
+    return (int)getRootWindowSize().width;
+}
+
+int
+get_screen_size_y()
+{
+    return (int)getRootWindowSize().height;
+}
+
+void
+set_wallpaper(char *str)
+{
+    @autoreleasepool {
+        NSWorkspace *sws = [NSWorkspace sharedWorkspace];
+        NSScreen *screen = [NSScreen mainScreen];
+        NSURL *image = [NSURL fileURLWithPath:[NSString stringWithUTF8String:str]];
+
+        NSDictionary *opts = [sws desktopImageOptionsForScreen:screen];
+        [sws setDesktopImageURL:image forScreen:screen options:opts error:nil];
+    }
+}
diff --git a/src/Main.hs b/src/Main.hs
new file mode 100644
--- /dev/null
+++ b/src/Main.hs
@@ -0,0 +1,185 @@
+module Main where
+
+import System.Environment          (getArgs)
+import System.Random               (randomIO)
+import System.Random.MWC           (Gen, initialize, uniformR)
+import System.Directory            (getHomeDirectory, createDirectoryIfMissing)
+import System.FilePath             ((</>))
+
+import Control.Monad               (liftM, unless)
+import Control.Monad.Primitive
+
+import Data.Word                   (Word32)
+
+import qualified Data.Vector                 as V
+import qualified Data.Vector.Unboxed         as UV
+import qualified Data.Vector.Unboxed.Mutable as MV
+
+import Data.Vector.Unboxed.Mutable (MVector, write)
+import Data.Vector.Unboxed         (Vector, (!), freeze)
+
+import Codec.Picture
+
+import FractalArt.SimpleCommandParser
+import FractalArt.ForeignFunctions
+
+type Position = (Int, Int)
+type Size     = (Int, Int)
+type Color    = (Float, Float, Float)
+
+type Grid m   = MVector m (Bool, Color)
+
+aOutputDir, aFileName :: Arg String
+aOutputDir = Arg "output" 'o' return
+aFileName  = Arg "file"   'f' return
+
+aWidth, aHeight :: Arg Int
+aWidth     = Arg "width"  'w' (return . read)
+aHeight    = Arg "height" 'h' (return . read)
+
+fNoSetWallpaper :: Flag
+fNoSetWallpaper = Flag "no-bg" 'n'
+
+main :: IO ()
+main = do
+    args <- getArgs
+    home <- getHomeDirectory
+
+    let workingDir = parseArg aOutputDir args `def` (home       </> ".fractalart")
+    let imageFile  = parseArg aFileName  args `def` (workingDir </> "wallpaper.bmp")
+
+    createDirectoryIfMissing False workingDir
+
+    putStrLn $ "Working in: " ++ workingDir
+
+    size@(width, height) <- do
+        (w,h) <- getScreenSize
+        let nw = parseArg aWidth  args `def` w
+        let nh = parseArg aHeight args `def` h
+        return (nw, nh)
+    
+    putStrLn $ "Screen Resolution: " ++ show size
+
+    seed  <- randomIO :: IO Word32
+    gen   <- initialize (V.singleton seed)
+
+    startPosition <- do
+        x <- uniformR (0, width  - 1) gen
+        y <- uniformR (0, height - 1) gen
+        return (x, y)
+
+    putStrLn "Generating Image..."
+
+    -- Generate and force evaluation
+    grid <- runBG startPosition size gen
+
+    putStrLn $ "Saving To: " ++ imageFile
+
+    -- Create an immutable copy of the grid
+    frozen <- freeze grid
+    let bitmap = UV.map snd frozen
+
+    writeBitmap  imageFile $ createImage size bitmap
+    unless (parseFlag fNoSetWallpaper args)
+        (setWallpaper imageFile)
+    putStrLn "Done"
+
+-- | Create an image from a vector of colors
+createImage :: Size -> Vector Color -> Image PixelRGB8
+createImage (width, height) grid = generateImage colorAt width height
+  where
+    colorAt x y       = toPixel $ grid ! toIndex (width, height) (x, y)
+    toPixel (r, g, b) = PixelRGB8 (toByte r) (toByte g) (toByte b)
+    toByte f          = floor(f * 255)
+
+-- | Generate the wallpaper
+runBG :: PrimMonad m => Position -> Size -> Gen (PrimState m) -> m (Grid (PrimState m))
+runBG pos@(x, y) size@(w, h) gen = do
+    grid <- startGrid
+    mapM_ (iter pos size gen grid) [1..iterations]
+    return grid
+  where
+    startGrid  = do
+        grid <- MV.replicate (w * h)  (False, (0, 0, 0))
+        col  <- startColor
+        write grid (toIndex size pos) (True, col)
+        return grid
+
+    startColor = do
+      hue <- uniformR (0, 360) gen
+      --sat <- uniform           gen
+      return $ hsv hue 0.6 1
+
+    iterations = maximum [x, w - 1 - x, y, h - 1 - y]
+
+iter :: PrimMonad m => Position -> Size -> Gen (PrimState m) -> Grid (PrimState m) -> Int -> m ()
+iter pos size gen grid n = mapM_ f next
+  where
+    f p = do
+      col <- nextColor grid size gen p
+      write grid (toIndex size p) (True, col)
+
+    next = filter (isInside size) $ ringAt pos n
+
+nextColor :: PrimMonad m => Grid (PrimState m) -> Size -> Gen (PrimState m) -> Position -> m Color
+nextColor grid size gen pos = do
+    c <- colors
+    i <- uniformR (0, length c - 1) gen
+    m <- modColor (mkRange $ length c) (c !! i)
+    return . clampColor $ m
+  where
+    colors = liftM (map snd . filter isValid) . mapM (MV.read grid . toIndex size) . filter (isInside size) $ ringAt pos 1
+
+    mkRange l = 0.006 * 4 / fromIntegral l
+
+    modColor range (r, g, b)  = do
+      mr <- uniformR (-range, range) gen
+      mg <- uniformR (-range, range) gen
+      mb <- uniformR (-range, range) gen
+      return (r + mr, g + mg, b + mb)
+
+foldM' :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a
+foldM' _ z [] = return z
+foldM' f z (x:xs) = do
+  z' <- f z x
+  z' `seq` foldM' f z' xs
+
+hsv :: Float -> Float -> Float -> Color
+hsv h s v = case hId of
+    0 -> (v, t, p)
+    1 -> (q, v, p)
+    2 -> (p, v, t)
+    3 -> (p, q, v)
+    4 -> (t, p, v)
+    5 -> (v, p, q)
+    _ -> error $ "invalid hue: " ++ show h
+  where
+    hId :: Int
+    hId = floor (h / 60) `mod` 6
+    f    = h / 60 - fromIntegral hId
+    p    = v * (1 - s)
+    q    = v * (1 - s * f)
+    t    = v * (1 - s * (1 - f))
+
+clampColor :: Color -> Color
+clampColor (r, g, b) = (f r, f g, f b)
+  where
+    f = min 1 . max 0
+
+ringAt :: Position -> Int -> [Position]
+ringAt (x, y) l = sides ++ top ++ bottom
+  where
+    top    = [(n + x, l + y) | n <- [-l .. l]]
+    bottom = [(n + x, -l + y) | n <- [-l .. l]]
+    sides  = concat [[(l + x, n + y), (-l + x, n + y)] | n <- [1 - l .. l - 1]]
+
+toIndex :: Size -> Position -> Int
+toIndex (w, _) (x, y) = y * w + x
+
+isValid :: (Bool, a) -> Bool
+isValid = fst
+
+isInside :: Size -> Position -> Bool
+isInside (w, h) (x, y) = inside' x w && inside' y h
+  where
+    inside' n s = (n < s) && (n >= 0)
