diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+Shpadoinkle Lens aka S11 Lens
+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/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/Shpadoinkle-lens.cabal b/Shpadoinkle-lens.cabal
new file mode 100644
--- /dev/null
+++ b/Shpadoinkle-lens.cabal
@@ -0,0 +1,32 @@
+cabal-version: 1.12
+
+-- This file has been generated from package.yaml by hpack version 0.32.0.
+--
+-- see: https://github.com/sol/hpack
+--
+-- hash: ebcb50534c3fd9629e258370d8be4b17cb04e004d5b3320e96d3226e1b500716
+
+name:           Shpadoinkle-lens
+version:        0.0.0.1
+description:    Lens combinators for Shpadoinkle applications.
+category:       Web
+author:         Isaac Shapira
+maintainer:     fresheyeball@protonmail.com
+license:        BSD3
+license-file:   LICENSE
+build-type:     Simple
+
+library
+  exposed-modules:
+      Shpadoinkle.Lens
+  other-modules:
+      Paths_Shpadoinkle_lens
+  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
+    , base >=4.12.0 && <4.16
+    , lens >=4.17.1 && <4.2
+    , text >=1.2.3 && <1.3
+  default-language: Haskell2010
diff --git a/Shpadoinkle/Lens.hs b/Shpadoinkle/Lens.hs
new file mode 100644
--- /dev/null
+++ b/Shpadoinkle/Lens.hs
@@ -0,0 +1,122 @@
+{-# LANGUAGE RankNTypes #-}
+
+
+{-|
+  Lens combinators for Shpadoinkle applications.
+-}
+
+
+module Shpadoinkle.Lens (
+  -- * Continuous Composition
+  generalize
+  , onSum, onRecord
+  , mapLens, (%>)
+  , forLens, (<%)
+  -- * Misc Outlaws
+  , rounding, defaulting, fracIntegral
+  ) where
+
+
+import           Control.Lens
+import           Data.Maybe
+import           Shpadoinkle.Continuation
+
+
+generalize, onRecord :: forall f m s a. Functor m => Continuous f => Lens' s a -> f m a -> f m s
+{-|
+  Compose multiple Shpadoinkle views onto a product type, most frequently a record.
+  Let's say we have @Html@ which produces 'Int's, and we need to use it in a view
+  with more components. The model for such a view might be @(Int, String)@. To use
+  our child @Html@ inside the parent, we can assign produced @Int@s to the parent
+  tuple by using the '_1' lens like so:
+
+  @
+  child :: Html Int
+
+  parent :: Html (Int, String)
+  parent = div_
+    [ button [ onClick (0, \"Reset\") [ text "Reset!" ]
+    , generalize _1 child
+    ]
+  @
+-}
+generalize len = liftC (set len) (view len)
+{-|
+  Alias for 'generalize' with a name idiomatic to the common case of composing onto a record.
+-}
+onRecord = generalize
+{-# INLINE onRecord #-}
+{-# INLINE generalize #-}
+
+
+{-|
+  Split multiple Shpadoinkle views over a sum type. This is commonly the case when
+  using a sum to represent pages in a single page application, but it's useful for
+  any sum. For example, consider that you have a view with a model of @Either Int String@
+  and a child @Html@ that produces 'Int's. You can compose this child onto the parent
+  using '_Left' traversal like so:
+
+  @
+  child :: Html Int
+
+  parent :: Html (Either Int String)
+  parent = div_
+    [ button [ onClick (Right \"Reset\") ] [ text "Reset!" ]
+    , onSum _Left child
+    ]
+  @
+-}
+onSum ::  forall f m s a. Applicative m => Continuous f => Traversal' s a -> f m a -> f m s
+onSum p = liftCMay (set p) (preview p)
+{-# INLINE onSum #-}
+
+
+infixl 8 <%
+infixr 8 %>
+
+
+forLens, (<%) :: forall f m s a. Functor m => Continuous f => s -> Lens' s a -> (a -> f m a) -> f m s
+{-|
+   A variant of 'generalize' for the case where you might need to map the smaller value.
+
+   @
+   parent :: Html (Int, String)
+   parent model = div_
+    [ forLens model _1 $ \(i :: Int) ->
+        if i < 10 then text \"too low\" else
+          button [ onClick (i + 1) ] [ text \"Increment\" ]
+    ]
+   @
+-}
+forLens big len f = generalize len . f $ view len big
+-- | Infix for 'forLens'
+(<%) = forLens
+{-# INLINE forLens #-}
+{-# INLINE (<%) #-}
+
+
+mapLens, (%>) :: forall f m s a. Functor m => Continuous f => (a -> f m a) -> s -> Lens' s a -> f m s
+{-|
+  Like 'forLens' but with the lambda as the first argument.
+-}
+mapLens f big len = forLens big len f
+-- | Infix for 'mapLens'
+(%>) = mapLens
+{-# INLINE mapLens #-}
+{-# INLINE (%>) #-}
+
+
+fracIntegral :: forall s a. Integral a => RealFrac s => Prism' s a
+fracIntegral = prism fromIntegral $
+  \f -> let r = round f in
+    if fromIntegral r == f then Right r else Left f
+
+
+rounding :: forall a s. Integral s => RealFrac a => Iso' s a
+rounding = iso fromIntegral round
+{-# INLINE rounding #-}
+
+
+defaulting :: a -> Iso' (Maybe a) a
+defaulting x = iso (fromMaybe x) Just
+{-# INLINE defaulting #-}
