diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+Shpadoinkle Disembodied aka S11 Disembodied
+Copyright © 2020 Isaac Shapira
+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 Shpadoinkle nor the names of its 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/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,31 @@
+# Shpadoinkle Disembodied
+
+[![Goldwater](https://gitlab.com/fresheyeball/Shpadoinkle/badges/master/pipeline.svg)](https://gitlab.com/fresheyeball/Shpadoinkle)
+[![Haddock](https://img.shields.io/badge/haddock-master-informational)](https://shpadoinkle.org/disembodied)
+[![BSD-3](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
+[![built with nix](https://img.shields.io/badge/built%20with-nix-41439a)](https://builtwithnix.org)
+[![Hackage](https://img.shields.io/hackage/v/Shpadoinkle-disembodied.svg)](https://hackage.haskell.org/package/Shpadoinkle-disembodied)
+[![Hackage Deps](https://img.shields.io/hackage-deps/v/Shpadoinkle-disembodied.svg)](http://packdeps.haskellers.com/reverse/Shpadoinkle-disembodied)
+[![Hackage CI](https://matrix.hackage.haskell.org/api/v2/packages/Shpadoinkle-disembodied/badge)](https://matrix.hackage.haskell.org/#/package/Shpadoinkle-disembodied)
+
+This module provides static site generation tools for Html.
+
+## Usage
+
+Lets say you have the following API routes for you SPA application:
+
+```haskell
+type Pages m
+  = "about" :> View m Int
+  :<|> View m ()
+```
+
+And you have a view for each. You can now produce a `SiteSpec` mapping these routes to the views.
+
+```haskell
+site :: SiteSpec () (Pages m)
+site = about 0 :<|> const home
+```
+
+Which can be written to static pages with `writeSite`. Each route will become a directory,
+and each View will become an `index.html` file in that directory.
diff --git a/Shpadoinkle-disembodied.cabal b/Shpadoinkle-disembodied.cabal
new file mode 100644
--- /dev/null
+++ b/Shpadoinkle-disembodied.cabal
@@ -0,0 +1,42 @@
+cabal-version: 1.12
+
+-- This file has been generated from package.yaml by hpack version 0.33.0.
+--
+-- see: https://github.com/sol/hpack
+--
+-- hash: c9dcea41cbf0ee840bc68d0b857f3bf7b71f5d173c808dcd6c800cf9c9c11fc4
+
+name:           Shpadoinkle-disembodied
+version:        0.0.0.1
+synopsis:       Shpadoinkle as a static site.
+description:    Static site backed SPA applications.
+category:       Web
+author:         Isaac Shapira
+maintainer:     fresheyeball@protonmail.com
+license:        BSD3
+license-file:   LICENSE
+build-type:     Simple
+extra-source-files:
+    README.md
+    CHANGELOG.md
+
+library
+  exposed-modules:
+      Shpadoinkle.Disembodied
+  other-modules:
+      Shpadoinkle.Disembodied.Sample
+  hs-source-dirs:
+      ./.
+  ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities
+  build-depends:
+      Shpadoinkle
+    , Shpadoinkle-backend-static
+    , Shpadoinkle-html
+    , Shpadoinkle-router
+    , base >=4.12.0 && <4.16
+    , directory
+    , filepath
+    , servant
+    , text >=1.2.3 && <1.3
+    , unliftio
+  default-language: Haskell2010
diff --git a/Shpadoinkle/Disembodied.hs b/Shpadoinkle/Disembodied.hs
new file mode 100644
--- /dev/null
+++ b/Shpadoinkle/Disembodied.hs
@@ -0,0 +1,197 @@
+{-# LANGUAGE AllowAmbiguousTypes   #-}
+{-# LANGUAGE DataKinds             #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE GADTs                 #-}
+{-# LANGUAGE InstanceSigs          #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE OverloadedStrings     #-}
+{-# LANGUAGE ScopedTypeVariables   #-}
+{-# LANGUAGE TypeApplications      #-}
+{-# LANGUAGE TypeFamilies          #-}
+{-# LANGUAGE TypeOperators         #-}
+
+
+{-|
+   Static sites are the opposite of headless applications, they are disembodied.
+   Shpadoinkle Disembodied is a static site generator for Shpadoinkle applications.
+-}
+
+
+module Shpadoinkle.Disembodied (
+  -- * Site Reification
+  Site(..)
+  -- * Class
+  , Disembodied(..)
+  -- * Write Files
+  , writeSite
+  ) where
+
+
+import           Control.Monad              (void)
+import           Data.Kind                  (Type)
+import           Data.Proxy                 (Proxy (..))
+import           Data.Text                  (unpack)
+import           Data.Text.IO               as T (writeFile)
+import           Servant.API
+import           System.Directory           (createDirectoryIfMissing)
+import           System.FilePath            ((<.>), (</>))
+import           UnliftIO.Async             (concurrently, forConcurrently_)
+
+import           GHC.TypeLits               (KnownSymbol, symbolVal)
+import           Shpadoinkle                (Html)
+import           Shpadoinkle.Backend.Static (renderStatic)
+import           Shpadoinkle.Router         (HTML, View)
+
+
+-- | The reification of a static site based on Servant routes.
+-- Site takes a context `ctx` which is universal for the static site.
+-- This is useful for storing commonly used valus like the site name,
+-- site url, copyright date, ect.
+data Site ctx where
+
+  -- | A path segment in the URI
+  SPath
+    :: String
+    -- ^ The current URI path segment
+    -> Site ctx
+    -- ^ The site to be rendered at the path
+    -> Site ctx
+
+  -- | Html to be rendered as @index.html@
+  SIndex
+    :: forall m a ctx
+     . (ctx -> Html m a)
+     -- ^ Given a context, how can we render a page?
+     -> Site ctx
+
+  -- | Capture is the one Servant combinator that can be meaningful in static
+  -- site generation, and only if we can generate all possible instances.
+  SCapture
+    :: (FromHttpApiData x, ToHttpApiData x, Bounded x, Enum x)
+    => (x -> Site ctx)
+    -- ^ Given a context, provide the remaining site to be generated
+    -> Site ctx
+
+  -- | Branch the site at a given point in generation.
+  SChoice :: Site ctx -> Site ctx -> Site ctx
+
+
+
+-- | Type class induction for building the site out of a specification
+class Disembodied ctx a where
+  -- | A type family to represent the relationship between a Servant API
+  -- and the 'Html' views to render.
+  --
+  -- @
+  -- type SPA m = "about" :> Html m ()
+  --         :\<|\> Html m ()
+  --
+  -- site :: SiteSpec () (SPA m)
+  -- site = const (h1_ [ text "about" ])
+  --   :\<|\> const (h1_ [ text "home" ])
+  -- @
+  type SiteSpec ctx a :: Type
+
+  -- | Construct the site structure out of the associated API
+  buildSite :: SiteSpec ctx a -> Site ctx
+
+instance (Disembodied ctx x, Disembodied ctx y)
+  => Disembodied ctx (x :<|> y) where
+
+  type SiteSpec ctx (x :<|> y) = SiteSpec ctx x :<|> SiteSpec ctx y
+
+  buildSite :: SiteSpec ctx x :<|> SiteSpec ctx y -> Site ctx
+  buildSite (x :<|> y) = SChoice (buildSite @ctx @x x) (buildSite @ctx @y y)
+  {-# INLINABLE buildSite #-}
+
+instance (Disembodied ctx sub, KnownSymbol path)
+  => Disembodied ctx (path :> sub) where
+
+  type SiteSpec ctx (path :> sub) = SiteSpec ctx sub
+
+  buildSite :: SiteSpec ctx sub -> Site ctx
+  buildSite = SPath (symbolVal (Proxy @path)) . buildSite @ctx @sub
+  {-# INLINABLE buildSite #-}
+
+instance (Disembodied ctx sub, FromHttpApiData x, ToHttpApiData x, Bounded x, Enum x)
+  => Disembodied ctx (Capture sym x :> sub) where
+
+  type SiteSpec ctx (Capture sym x :> sub) = x -> SiteSpec ctx sub
+
+  buildSite :: (x -> SiteSpec ctx sub) -> Site ctx
+  buildSite = SCapture . (buildSite @ctx @sub .)
+  {-# INLINABLE buildSite #-}
+
+instance Disembodied ctx sub
+  => Disembodied ctx (QueryParam sym x :> sub) where
+
+  type SiteSpec ctx (QueryParam sym x :> sub) = Maybe x -> SiteSpec ctx sub
+
+  buildSite :: (Maybe x -> SiteSpec ctx sub) -> Site ctx
+  buildSite f = buildSite @ctx @sub $ f Nothing
+  {-# INLINABLE buildSite #-}
+
+instance Disembodied ctx sub
+  => Disembodied ctx (QueryParam' ms sym x :> sub) where
+
+  type SiteSpec ctx (QueryParam' ms sym x :> sub) = Maybe x -> SiteSpec ctx sub
+
+  buildSite :: (Maybe x -> SiteSpec ctx sub) -> Site ctx
+  buildSite f = buildSite @ctx @sub $ f Nothing
+  {-# INLINABLE buildSite #-}
+
+instance Disembodied ctx sub
+  => Disembodied ctx (QueryParams sym x :> sub) where
+
+  type SiteSpec ctx (QueryParams sym x :> sub) = [x] -> SiteSpec ctx sub
+
+  buildSite :: ([x] -> SiteSpec ctx sub) -> Site ctx
+  buildSite f = buildSite @ctx @sub $ f []
+  {-# INLINABLE buildSite #-}
+
+instance Disembodied ctx sub
+  => Disembodied ctx (QueryFlag sym :> sub) where
+
+  type SiteSpec ctx (QueryFlag sym :> sub) = Bool -> SiteSpec ctx sub
+
+  buildSite :: (Bool -> SiteSpec ctx sub) -> Site ctx
+  buildSite f = buildSite @ctx @sub $ f False
+  {-# INLINABLE buildSite #-}
+
+instance Disembodied ctx (f '[HTML] (Html m a)) where
+
+  type SiteSpec ctx (f '[HTML] (Html m a)) = ctx -> Html m a
+
+  buildSite :: (ctx -> Html m a) -> Site ctx
+  buildSite = SIndex
+  {-# INLINABLE buildSite #-}
+
+instance Disembodied ctx (View m a) where
+
+  type SiteSpec ctx (View m a) = ctx -> Html m a
+
+  buildSite :: (ctx -> Html m a) -> Site ctx
+  buildSite = SIndex
+  {-# INLINABLE buildSite #-}
+
+
+-- | Actually write the site to disk. Branches are written in parallel.
+writeSite
+  :: forall layout ctx. Disembodied ctx layout
+  => FilePath
+  -- ^ Out path
+  -> ctx
+  -- ^ Universal context for the static site.
+  -> SiteSpec ctx layout
+  -- ^ Specification for the pages of the site relative to a Servant API.
+  -> IO ()
+writeSite fs ctx layout = go fs $ buildSite @ctx @layout layout where
+
+  go :: FilePath -> Site ctx -> IO ()
+  go curr (SIndex page) = T.writeFile (curr </> "index" <.> "html") . renderStatic $ page ctx
+  go curr (SChoice x y) = void $ go curr x `concurrently` go curr y
+  go curr (SCapture f)  = forConcurrently_ [ minBound .. maxBound ] $
+    \c -> go curr $ SPath (unpack $ toUrlPiece c) $ f c
+  go curr (SPath path site) = do
+    createDirectoryIfMissing False (curr </> path)
+    go (curr </> path) site
diff --git a/Shpadoinkle/Disembodied/Sample.hs b/Shpadoinkle/Disembodied/Sample.hs
new file mode 100644
--- /dev/null
+++ b/Shpadoinkle/Disembodied/Sample.hs
@@ -0,0 +1,48 @@
+{-# LANGUAGE CPP               #-}
+{-# LANGUAGE DataKinds         #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TypeApplications  #-}
+{-# LANGUAGE TypeOperators     #-}
+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
+
+
+module Shpadoinkle.Disembodied.Sample where
+
+
+import           Data.Text               (Text)
+import           Servant.API
+
+import           Shpadoinkle             (Html, JSM, MonadJSM, text)
+import           Shpadoinkle.Disembodied (Disembodied (SiteSpec), writeSite)
+import           Shpadoinkle.Html        (button, h1_, onClick)
+import           Shpadoinkle.Router      (View)
+
+
+type Pages m
+  = "about" :> View m Int
+  :<|> View m ()
+
+
+newtype Context = Context
+  { siteName :: Text }
+
+
+about :: MonadJSM m => Context -> Html m Int
+about ctx =
+  h1_ [ text $ "about us at " <> siteName ctx
+      , button
+        [ onClick (+ 1) ]
+        [ "Increment" ]
+      ]
+
+
+home :: Html m a
+home = h1_ [ "home" ]
+
+
+site :: MonadJSM m => SiteSpec Context (Pages m)
+site = about :<|> const home
+
+
+makeSite :: IO ()
+makeSite = writeSite @(Pages JSM) "" (Context "Sample") site
