diff --git a/Animation.hs b/Animation.hs
new file mode 100644
--- /dev/null
+++ b/Animation.hs
@@ -0,0 +1,64 @@
+{-# LANGUAGE ExtendedDefaultRules #-}
+{-# LANGUAGE MultiWayIf           #-}
+{-# LANGUAGE OverloadedStrings    #-}
+{-# OPTIONS_GHC -fno-warn-type-defaults #-}
+
+
+module Main where
+
+
+import           Control.Concurrent.STM                  (atomically, writeTVar)
+import           Control.Monad                           (void)
+import           Control.Monad.IO.Class
+import           Data.Text
+import           Ease
+import           GHCJS.DOM
+import           GHCJS.DOM.RequestAnimationFrameCallback
+import           GHCJS.DOM.Window
+import           Shpadoinkle
+import           Shpadoinkle.Backend.ParDiff
+import           Shpadoinkle.Html                        as H
+
+
+default (Text)
+
+
+left :: Double -> Text
+left x = "transform:translate3d(150px, " <> pack (show $ x / 5) <> "px, 0)"
+
+
+easeRange :: Fractional b => b -> (b -> b) -> b -> b
+easeRange r e = (* r) . e . (/ r)
+
+
+dur :: Double
+dur = 3000
+
+
+view :: Double -> Html m a
+view clock = H.div
+  [ textProperty "style" $
+     "position:absolute;background:red;padding:10px;" <> left
+       (easeRange dur bounceOut clock) ]
+  [ if | clock < 500        -> "Wat?"
+       | clock < 1000       -> "AAAA!!"
+       | clock < dur * 0.65 -> "Oh no!"
+       | otherwise          -> "I'm ok" ]
+
+
+animation :: Window -> TVar Double -> JSM ()
+animation w t = void $ requestAnimationFrame w =<< go where
+  go = newRequestAnimationFrameCallback $ \clock -> do
+    liftIO . atomically $ writeTVar t clock
+    r <- go
+    if clock < dur then void $ requestAnimationFrame w r else return ()
+
+
+main :: IO ()
+main = do
+  putStrLn "\nHappy point of view on https://localhost:8080\n"
+  runJSorWarp 8080 $ do
+    t <- newTVarIO 0
+    w <- currentWindowUnchecked
+    animation w t
+    shpadoinkle id runParDiff 0 t view getBody
diff --git a/Calculator.hs b/Calculator.hs
new file mode 100644
--- /dev/null
+++ b/Calculator.hs
@@ -0,0 +1,76 @@
+{-# LANGUAGE LambdaCase        #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RankNTypes        #-}
+{-# LANGUAGE TemplateHaskell   #-}
+
+
+module Main where
+
+
+import           Data.Maybe
+import           Data.Text
+import           Safe
+import           Shpadoinkle
+import           Shpadoinkle.Backend.ParDiff
+import           Shpadoinkle.Html
+
+
+data Model = Model
+  { operation :: Operation
+  , left      :: Int
+  , right     :: Int
+  } deriving (Eq, Show)
+
+
+data Operation
+  = Addition
+  | Subtraction
+  | Multiplication
+  | Division
+  deriving (Eq, Show, Read, Enum, Bounded)
+
+
+opFunction :: Operation -> (Int -> Int -> Int)
+opFunction = \case
+  Addition       -> (+)
+  Subtraction    -> (-)
+  Multiplication -> (*)
+  Division       -> \x y ->
+    if y == 0 then 0 else Prelude.div x y
+
+
+opText :: Operation -> Text
+opText = \case
+  Addition       -> "+"
+  Subtraction    -> "-"
+  Multiplication -> "×"
+  Division       -> "÷"
+
+
+opSelect :: Html m Operation
+opSelect = select [ onOption $ read . unpack ]
+  $ opOption <$> [minBound..maxBound]
+  where opOption o = option [ value . pack $ show o ] [ text $ opText o ]
+
+
+num :: Int -> Html m Int
+num x = input'
+  [ value . pack $ show x
+  , onInput $ fromMaybe 0 . readMay . unpack
+  ]
+
+
+view :: Functor m => Model -> Html m Model
+view model = div_
+  [ liftC (\l m -> m { left      = l }) left      $ num (left model)
+  , liftC (\o m -> m { operation = o }) operation $ opSelect
+  , liftC (\r m -> m { right     = r }) right     $ num (right model)
+  , text $ " = " <> pack (show $ opFunction
+      (operation model) (left model) (right model))
+  ]
+
+
+main :: IO ()
+main = runJSorWarp 8080 $
+  simple runParDiff (Model Addition 0 0) view getBody
+
diff --git a/CalculatorIE.hs b/CalculatorIE.hs
new file mode 100644
--- /dev/null
+++ b/CalculatorIE.hs
@@ -0,0 +1,205 @@
+{-# LANGUAGE DeriveAnyClass         #-}
+{-# LANGUAGE DeriveGeneric          #-}
+{-# LANGUAGE DuplicateRecordFields  #-}
+{-# LANGUAGE ExtendedDefaultRules   #-}
+{-# LANGUAGE FlexibleContexts       #-}
+{-# LANGUAGE FlexibleInstances      #-}
+{-# LANGUAGE FunctionalDependencies #-}
+{-# LANGUAGE LambdaCase             #-}
+{-# LANGUAGE MultiParamTypeClasses  #-}
+{-# LANGUAGE OverloadedStrings      #-}
+{-# LANGUAGE TemplateHaskell        #-}
+{-# LANGUAGE TypeApplications       #-}
+{-# OPTIONS_GHC -fno-warn-type-defaults       #-}
+
+module Main where
+
+import           Control.Lens
+import           Data.Aeson
+import           Data.FileEmbed
+import           Data.List                   as L
+import           Data.List.Split             as L
+import           Data.Text
+import           Data.Text.Encoding
+import           GHC.Generics                (Generic)
+import           Shpadoinkle
+import           Shpadoinkle.Backend.ParDiff
+import           Shpadoinkle.Console
+import           Shpadoinkle.Html            as H
+
+default (ClassList)
+
+data Digit
+  = Seven | Eight | Nine
+  | Four  | Five  | Six
+  | One   | Two   | Three
+  | Zero deriving (Eq, Show, Ord, Enum, Bounded, Generic, ToJSON)
+
+data Operator = Addition | Multiplication | Subtraction | Division
+  deriving (Eq, Enum, Bounded, Generic, ToJSON)
+
+instance Show Operator where
+  show = \case
+    Addition       -> "+"
+    Subtraction    -> "−"
+    Multiplication -> "×"
+    Division       -> "÷"
+
+data Entry
+  = Whole [Digit]
+  | [Digit] :<.> [Digit]
+  | Negate Entry
+  deriving (Eq, Generic, ToJSON)
+
+noEntry :: Entry
+noEntry = Whole []
+
+instance Show Entry where
+  show = let asChar = traverse . re charDigit in \case
+    Whole xs        -> xs ^.. asChar
+    xs :<.> ys -> xs ^.. asChar <> "." <> ys ^.. asChar
+    Negate e        -> '-' : show e
+
+frac :: Iso' Entry Double
+frac = iso toFrac fromFrac where
+  toFrac =
+    let f x = case L.reverse x of
+             ""     -> "0"
+             '.':xs -> L.reverse xs
+             _      -> x
+    in read . f . show
+  fromFrac d =
+    let asChar = traverse . charDigit
+        ab xs = case L.splitOn "." xs of
+         [whole, []]  -> Whole $ whole ^.. asChar
+         [whole, dec] ->   (whole ^.. asChar)
+                      :<.> (dec   ^.. asChar)
+         _            -> noEntry
+     in case show d of
+       '-':xs -> Negate $ ab xs
+       xs     -> ab xs
+
+charDigit :: Prism' Char Digit
+charDigit = prism'
+  (\case Seven    -> '7';   Eight ->    '8';   Nine  ->   '9'
+         Four     -> '4';   Five  ->    '5';   Six   ->   '6'
+         One      -> '1';   Two   ->    '2';   Three ->   '3'
+         Zero     -> '0')
+  (\case '7' -> Just Seven; '8' -> Just Eight; '9' -> Just Nine
+         '4' -> Just Four;  '5' -> Just Five;  '6' -> Just Six
+         '1' -> Just One;   '2' -> Just Two;   '3' -> Just Three
+         '0' -> Just Zero;   _  -> Nothing)
+
+data Operation = Operation
+  { _operator :: Operator
+  , _previous :: Entry
+  } deriving (Eq, Show, Generic, ToJSON)
+
+makeFieldsNoPrefix ''Operation
+
+data Model = Model
+  { _current   :: Entry
+  , _operation :: Maybe Operation
+  } deriving (Eq, Show, Generic, ToJSON)
+
+makeFieldsNoPrefix ''Model
+
+initial :: Model
+initial = Model noEntry Nothing
+
+digit :: Model -> Digit -> Html m Model
+digit m d = button [ onClick $ m & current %~ applyDigit d, class' $ "d" <> d' ] [ text d' ]
+  where d' = d ^. re charDigit . to (pack . pure)
+
+operate :: Maybe Operator -> Operator -> Html m Operator
+operate active o = button
+  [ onClick o, class' ("active" :: Text, Just o == active) ]
+  [ text . pack $ show o ]
+
+applyDigit :: Digit -> Entry -> Entry
+applyDigit d = \case
+  Whole xs   -> Whole $ xs <> [d]
+  xs :<.> ys -> xs :<.> (ys <> [d])
+  Negate e   -> Negate $ applyDigit d e
+
+addDecimal :: Entry -> Entry
+addDecimal = \case
+  Whole xs -> xs :<.> []
+  ys       -> ys
+
+cleanEntry :: Entry -> Entry
+cleanEntry = \case
+  xs :<.> []     -> Whole xs
+  xs :<.> [Zero] -> Whole xs
+  x              -> x
+
+calcResult :: Model -> Model
+calcResult x = x
+  & operation .~ Nothing
+  & current .~ case x ^. operation of
+    Nothing -> x ^. current
+    Just o -> let l = o ^. previous . frac; r = x ^. current . frac
+      in cleanEntry . (^. from frac) $ case o ^. operator of
+      Addition       -> l + r
+      Subtraction    -> l - r
+      Multiplication -> l * r
+      Division       -> if r == 0 then l else l / r
+
+neg :: Entry -> Entry
+neg = \case
+  Negate e -> e
+  e -> Negate e
+
+readout :: Model -> Html m a
+readout x = H.div "readout" $
+  [ text . pack . show $ x ^. current
+  ] <> case x ^? operation . traverse . operator of
+         Nothing -> []
+         Just o  -> [ H.div "operator"
+             [ text $ pack (show o) <> " " <>
+               x ^. operation . traverse . previous . to (pack .show)
+             ]
+           ]
+
+clear :: Html m Model
+clear  = button [ class' "clear", onClick initial ] [ "AC" ]
+
+posNeg :: Model -> Html m Model
+posNeg x = button [ class' "posNeg", onClick (x & current %~ neg) ] [ "-/+" ]
+
+numberpad :: Model -> Html m Model
+numberpad m = H.div "numberpad" . L.intercalate [ br'_ ] . L.chunksOf 3 $
+  digit m <$> [minBound .. pred maxBound]
+
+operations :: Functor m => Model -> Html m Model
+operations x = H.div "operate" $ (\o' -> liftC (\o _ -> x
+  & operation .~ Just (Operation o (x ^. current))
+  & current .~ noEntry) (const o')
+  $ operate (x ^? operation . traverse . operator) o') <$> ([minBound .. maxBound] :: [Operator])
+
+dot :: Model -> Html m Model
+dot x = button [ onClick $ x & current %~ addDecimal ] [ "." ]
+
+equals :: Model -> Html m Model
+equals x = button [ class' "equals", onClick $ calcResult x ] [ "=" ]
+
+view :: Monad m => Model -> Html m Model
+view m = H.div "calculator"
+  [ readout m
+  , H.div "buttons"
+    [ clear, posNeg m, operations m
+    , numberpad m
+    , H.div "zerodot"
+      [ digit m Zero
+      , dot m
+      , equals m
+      ]
+    ]
+  ]
+
+main :: IO ()
+main = runJSorWarp 8080 $ do
+  setTitle "Calculator"
+  ctx <- askJSM
+  addInlineStyle $ decodeUtf8 $(embedFile "./CalculatorIE.css")
+  Shpadoinkle.simple runParDiff initial (Main.view . trapper @ToJSON ctx) getBody
diff --git a/Counter.hs b/Counter.hs
--- a/Counter.hs
+++ b/Counter.hs
@@ -15,14 +15,14 @@
 import           Shpadoinkle.Html.Utils
 
 
-view :: Applicative m => Int -> Html m Int
+view :: Int -> Html m Int
 view count = div_
   [ h2_ [ "Counter Example" ]
   , "The current count is: "
   , span [ id' "out" ] [ text (pack $ show count) ]
   , br'_, br'_
-  , button [ onClick (count - 1) ] [ "Decrement" ]
-  , button [ onClick (count + 1) ] [ "Increment" ]
+  , button [ onClick $ count - 1 ] [ "Decrement" ]
+  , button [ onClick $ count + 1 ] [ "Increment" ]
   ]
 
 
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,26 +1,27 @@
-Shpadoinkle Examples, I think I know exactly what it means
-Copyright © 2019 Isaac Shpaira
+Shpadoinkle Examples aka S11 Examples
+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:
-1. Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
-2. 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.
-3. Neither the name of the <`3:organization`> 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 <|2|> ''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 <|2|> 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.
+ * 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/Lens.hs b/Lens.hs
new file mode 100644
--- /dev/null
+++ b/Lens.hs
@@ -0,0 +1,84 @@
+{-# LANGUAGE DeriveGeneric              #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE LambdaCase                 #-}
+{-# LANGUAGE OverloadedLabels           #-}
+{-# LANGUAGE OverloadedStrings          #-}
+
+
+module Main where
+
+
+import           Control.Lens                ((^.))
+import           Data.Generics.Labels        ()
+import           Data.Maybe                  (fromMaybe)
+import           Data.Text                   (Text, pack, unpack)
+import           GHC.Generics                (Generic)
+import           Safe                        (readMay)
+import           Shpadoinkle                 (Html, JSM, runJSorWarp, simple,
+                                              text)
+import           Shpadoinkle.Backend.ParDiff (runParDiff)
+import           Shpadoinkle.Html            (button, div_, for', getBody, id',
+                                              input', label, onClick, onInput,
+                                              value)
+import           Shpadoinkle.Lens            (onRecord, onSum)
+
+
+data Form = Form
+  { name :: Text
+  , age  :: Int
+  } deriving (Eq, Show, Generic)
+
+
+form :: Functor m => Form -> Html m Form
+form f = div_
+  [ label [ for' "name" ] [ "Name" ]
+  , onRecord #name $ input'
+    [ id' "name"
+    , value $ f ^. #name
+    , onInput id
+    ]
+  , label [ for' "age" ] [ "Age" ]
+  , onRecord #age $ input'
+    [ id' "age"
+    , value . pack . show $ f ^. #age
+    , onInput $ fromMaybe 0 . readMay . unpack
+    ]
+  ]
+
+
+newtype Counter = Counter Int
+  deriving (Eq, Ord, Num, Show, Generic)
+
+
+counter :: Counter -> Html m Counter
+counter c = div_
+  [ label [ for' "counter" ] [ text . pack $ show c ]
+  , button [ id' "counter", onClick $ c + 1 ] [ "Increment" ]
+  ]
+
+
+data Model
+  = MCounter Counter
+  | MForm Form
+  deriving (Eq, Show, Generic)
+
+
+view :: Applicative m => Model -> Html m Model
+view = \case
+  MCounter c -> div_
+    [ onSum #_MCounter $ counter c
+    , button [ onClick . MForm $ Form "" 18 ] [ "Go to Form" ]
+    ]
+  MForm f    -> div_
+    [ onSum #_MForm $ form f
+    , button [ onClick $ MCounter 0 ] [ "Go to Counter" ]
+    ]
+
+
+app :: JSM ()
+app = simple runParDiff (MCounter 0) view getBody
+
+
+main :: IO ()
+main = runJSorWarp 8080 app
+
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -8,12 +8,15 @@
 [![Hackage CI](https://matrix.hackage.haskell.org/api/v2/packages/Shpadoinkle-examples/badge)](https://matrix.hackage.haskell.org/#/package/Shpadoinkle-examples)
 
 Work in progress developing a compelling collection of example applications using Shpadoinkle.
-This repo will be used with the Shpadoinkle tutorial when it's finished.
+This repo will be used with the Shpadoinkle tutorial when it is finished.
 
 ## Complete examples
 
-- Simple counter
-- TODOMVC
+- [Counter](https://fresheyeball.gitlab.io/Shpadoinkle/examples/counter.jsexe/)
+- [TODOMVC](https://fresheyeball.gitlab.io/Shpadoinkle/examples/todomvc.jsexe/)
+- [Widgets](https://fresheyeball.gitlab.io/Shpadoinkle/examples/widgets.jsexe/)
+- [Calculator](https://fresheyeball.gitlab.io/Shpadoinkle/examples/calculator.jsexe/)
+- [Immediate Execution](https://fresheyeball.gitlab.io/Shpadoinkle/examples/calculator-ie.jsexe/)
 - Servant CRUD using Widgets
 
 This package also houses experimental designs, and other explorations.
diff --git a/Shpadoinkle-examples.cabal b/Shpadoinkle-examples.cabal
--- a/Shpadoinkle-examples.cabal
+++ b/Shpadoinkle-examples.cabal
@@ -4,10 +4,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 1659b859dd98837a87a964e0cf7684da9755b756eacede1a890dab6740e71d01
+-- hash: 21985c60c92f72c182e14dfac317f3e717d0ca271e4b587fdf362594753f26ba
 
 name:           Shpadoinkle-examples
-version:        0.0.0.1
+version:        0.0.0.2
 synopsis:       Example usages of Shpadoinkle
 description:    A collection of illustrative applications to show various Shpadoinkle utilities.
 category:       Web
@@ -24,6 +24,62 @@
   type: git
   location: https://gitlab.com/fresheyeball/Shpadoinkle.git
 
+executable animation
+  main-is: Animation.hs
+  hs-source-dirs:
+      ./.
+  ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities
+  ghcjs-options: -Wall -Wcompat -fno-warn-missing-home-modules -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities -dedupe
+  build-depends:
+      Shpadoinkle
+    , Shpadoinkle-backend-pardiff
+    , Shpadoinkle-html
+    , base >=4.12.0 && <4.16
+    , ease
+    , ghcjs-dom
+    , stm
+    , text
+  default-language: Haskell2010
+
+executable calculator
+  main-is: Calculator.hs
+  hs-source-dirs:
+      ./.
+  ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities
+  ghcjs-options: -Wall -Wcompat -fno-warn-missing-home-modules -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities -dedupe
+  build-depends:
+      Shpadoinkle
+    , Shpadoinkle-backend-pardiff
+    , Shpadoinkle-html
+    , Shpadoinkle-lens
+    , Shpadoinkle-widgets
+    , base >=4.12.0 && <4.16
+    , lens
+    , safe
+    , text
+  default-language: Haskell2010
+
+executable calculator-ie
+  main-is: CalculatorIE.hs
+  hs-source-dirs:
+      ./.
+  ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities
+  ghcjs-options: -Wall -Wcompat -fno-warn-missing-home-modules -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities -dedupe
+  build-depends:
+      Shpadoinkle
+    , Shpadoinkle-backend-pardiff
+    , Shpadoinkle-console
+    , Shpadoinkle-html
+    , Shpadoinkle-widgets
+    , aeson
+    , base >=4.12.0 && <4.16
+    , file-embed
+    , lens
+    , safe
+    , split
+    , text
+  default-language: Haskell2010
+
 executable counter
   main-is: Counter.hs
   hs-source-dirs:
@@ -34,11 +90,29 @@
       Shpadoinkle
     , Shpadoinkle-backend-pardiff
     , Shpadoinkle-html
-    , base >=4.12.0 && <4.15
+    , base >=4.12.0 && <4.16
     , stm
     , text
   default-language: Haskell2010
 
+executable lens
+  main-is: Lens.hs
+  hs-source-dirs:
+      ./.
+  ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities
+  ghcjs-options: -Wall -Wcompat -fno-warn-missing-home-modules -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities -dedupe
+  build-depends:
+      Shpadoinkle
+    , Shpadoinkle-backend-pardiff
+    , Shpadoinkle-html
+    , Shpadoinkle-lens
+    , base >=4.12.0 && <4.16
+    , generic-lens
+    , lens
+    , safe
+    , text
+  default-language: Haskell2010
+
 executable servant-crud-client
   main-is: Client.hs
   other-modules:
@@ -57,11 +131,12 @@
     , Shpadoinkle-router
     , Shpadoinkle-widgets
     , aeson
-    , base >=4.12.0 && <4.15
+    , base >=4.12.0 && <4.16
     , beam-core
     , bytestring
     , containers
     , exceptions
+    , jsaddle
     , lens
     , mtl
     , servant
@@ -70,7 +145,7 @@
     , unliftio
   if impl(ghcjs)
     build-depends:
-        servant-client-ghcjs
+        servant-client-js
   else
     build-depends:
         beam-sqlite
@@ -96,7 +171,7 @@
     , Shpadoinkle-router
     , Shpadoinkle-widgets
     , aeson
-    , base >=4.12.0 && <4.15
+    , base >=4.12.0 && <4.16
     , beam-core
     , beam-sqlite
     , bytestring
@@ -119,11 +194,8 @@
     buildable: True
   default-language: Haskell2010
 
-executable todomvc
-  main-is: TODOMVC.hs
-  other-modules:
-      TODOMVC.Types
-      TODOMVC.Update
+executable throttle-and-debounce
+  main-is: ThrottleAndDebounce.hs
   hs-source-dirs:
       ./.
   ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities
@@ -131,27 +203,28 @@
   build-depends:
       Shpadoinkle
     , Shpadoinkle-backend-pardiff
+    , Shpadoinkle-console
     , Shpadoinkle-html
-    , base >=4.12.0 && <4.15
-    , containers
+    , base >=4.12.0 && <4.16
+    , lens
+    , safe
     , text
   default-language: Haskell2010
 
-executable todomvcatomic
-  main-is: TODOMVCAtomic.hs
-  other-modules:
-      TODOMVCAtomic.Types
-      TODOMVCAtomic.Update
+executable todomvc
+  main-is: TODOMVC.hs
   hs-source-dirs:
       ./.
   ghc-options: -Wall -Wcompat -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities
   ghcjs-options: -Wall -Wcompat -fno-warn-missing-home-modules -fwarn-redundant-constraints -fwarn-incomplete-uni-patterns -fwarn-tabs -fwarn-incomplete-record-updates -fwarn-identities -dedupe
   build-depends:
       Shpadoinkle
-    , Shpadoinkle-backend-snabbdom
+    , Shpadoinkle-backend-pardiff
     , Shpadoinkle-html
-    , base >=4.12.0 && <4.15
+    , Shpadoinkle-lens
+    , base >=4.12.0 && <4.16
     , containers
+    , generic-lens
     , lens
     , text
   default-language: Haskell2010
@@ -168,8 +241,10 @@
       Shpadoinkle
     , Shpadoinkle-backend-pardiff
     , Shpadoinkle-html
+    , Shpadoinkle-lens
     , Shpadoinkle-widgets
-    , base >=4.12.0 && <4.15
+    , base >=4.12.0 && <4.16
+    , lens
     , stm
     , text
   default-language: Haskell2010
diff --git a/TODOMVC.hs b/TODOMVC.hs
--- a/TODOMVC.hs
+++ b/TODOMVC.hs
@@ -1,30 +1,113 @@
-{-# LANGUAGE ExtendedDefaultRules #-}
-{-# LANGUAGE OverloadedStrings    #-}
+{-# LANGUAGE DeriveGeneric              #-}
+{-# LANGUAGE ExtendedDefaultRules       #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE OverloadedLabels           #-}
+{-# LANGUAGE OverloadedStrings          #-}
 {-# OPTIONS_GHC -fno-warn-type-defaults #-}
 
 
 module Main where
 
 
+import           Control.Lens                  hiding (view)
+import           Data.Generics.Labels          ()
+import           Data.String
 import           Data.Text                     hiding (count, filter, length)
+import           GHC.Generics
 import           Prelude                       hiding (div, unwords)
 import           Shpadoinkle
 import           Shpadoinkle.Backend.ParDiff
-import           Shpadoinkle.Html              hiding (main)
+import           Shpadoinkle.Html
 import           Shpadoinkle.Html.LocalStorage
 import           Shpadoinkle.Html.Memo
-import           Shpadoinkle.Html.Utils
-
-import           TODOMVC.Types
-import           TODOMVC.Update
+import           Shpadoinkle.Lens
 
 
 default (Text)
 
 
-filterHtml :: Applicative m => Visibility -> Visibility -> Html m Visibility
+newtype Description = Description { unDescription :: Text } deriving (Generic, Show, Read, Eq, IsString)
+newtype TaskId      = TaskId      { unTaskId      :: Int  } deriving (Generic, Show, Read, Eq, Ord, Num)
+
+
+data Completed  = Complete | Incomplete
+  deriving (Generic, Show, Read, Eq)
+
+
+data Visibility = All | Active | Completed
+  deriving (Generic, Show, Read, Eq, Ord, Enum, Bounded)
+
+
+data Task = Task
+  { description :: Description
+  , completed   :: Completed
+  , taskId      :: TaskId
+  } deriving (Generic, Show, Read, Eq)
+
+
+data Model = Model
+  { tasks      :: [Task]
+  , editing    :: Maybe TaskId
+  , visibility :: Visibility
+  , current    :: Description
+  } deriving (Generic, Show, Read, Eq)
+
+
+emptyModel :: Model
+emptyModel = Model [] Nothing All (Description "")
+
+
+appendItem :: Model -> Model
+appendItem m = if current m /= "" then m
+  & #tasks   %~ (Task (current m) Incomplete newId :)
+  & #current .~ "" else m
+  where newId = Prelude.maximum (0 : (taskId <$> tasks m)) + 1
+
+
+toggleCompleted :: TaskId -> Model -> Model
+toggleCompleted tid m = m & #tasks . mapped %~ \t ->
+  if taskId t == tid then t & #completed %~ negC else t
+  where negC Complete   = Incomplete
+        negC Incomplete = Complete
+
+
+toggleEditing :: Maybe TaskId -> Model -> Model
+toggleEditing t = #editing .~ t
+
+
+updateTaskDescription :: TaskId -> Description -> Model -> Model
+updateTaskDescription tid desc m = m & #tasks . mapped %~ \t ->
+  if taskId t == tid then t & #description .~ desc else t
+
+
+removeTask :: TaskId -> Model -> Model
+removeTask tid m = m & #tasks %~ filter ((/= tid) . taskId)
+
+
+toggleAll :: Model -> Model
+toggleAll m = m & #tasks . traverse . #completed .~ c
+  where c = if Prelude.all ((== Complete) . completed) $ tasks m then Incomplete else Complete
+
+
+count :: Completed -> [Task] -> Int
+count c = length . filter ((== c) . completed)
+
+
+clearComplete :: Model -> Model
+clearComplete = #tasks %~ filter ((== Incomplete) . completed)
+
+
+toVisible :: Visibility -> [Task] -> [Task]
+toVisible v = case v of
+  All       -> id
+  Active    -> filter $ (== Incomplete) . completed
+  Completed -> filter $ (== Complete)   . completed
+
+
+filterHtml :: Visibility -> Visibility -> Html m Visibility
 filterHtml = memo2 $ \cur item -> li_
-  [ a (href "#" : onClick item : [className ("selected", cur == item)]) [ text . pack $ show item ]
+  [ a (href "#" : onClick item
+        : [class' ("selected", cur == item)]) [ text . pack $ show item ]
   ]
 
 
@@ -32,42 +115,43 @@
 htmlIfTasks m h' = if Prelude.null (tasks m) then [] else h'
 
 
-taskView :: MonadJSM m => Model -> Task -> Html m Model
+taskView :: Model -> Task -> Html m Model
 taskView m = memo $ \(Task (Description d) c tid) ->
   li [ id' . pack . show $ unTaskId tid
-     , className [ ("completed", c == Complete)
-                 , ("editing", Just tid == editing m) ]
+     , class' [ ("completed", c == Complete)
+              , ("editing", Just tid == editing m)
+              ]
      ]
   [ div "view"
     [ input' [ type' "checkbox"
-             , className "toggle"
-             , onChange $ toggleCompleted m tid
+             , class' "toggle"
+             , onChange $ toggleCompleted tid m
              , checked $ c == Complete
              ]
-    , label [ onDblclick (toggleEditing m (Just tid)) ] [ text d ]
-    , button' [ className "destroy", onClick (removeTask m tid) ]
+    , label [ onDblclick $ toggleEditing (Just tid) m ] [ text d ]
+    , button' [ class' "destroy", onClick $ removeTask tid m ]
     ]
-  , form [ onSubmit $ toggleEditing m Nothing ]
-    [ input' [ className "edit"
+  , form [ onSubmit $ toggleEditing Nothing m ]
+    [ input' [ class' "edit"
              , value d
-             , onInput $ updateTaskDescription m tid . Description
+             , onInput $ ($ m) . updateTaskDescription tid . Description
              , autofocus True
-             , onBlur $ toggleEditing m Nothing
+             , onBlur $ toggleEditing Nothing m
              ]
     ]
   ]
 
 
-listFooter :: Applicative m => Model -> Html m Model
+listFooter :: Functor m => Model -> Html m Model
 listFooter model = footer "footer" $
   [ Shpadoinkle.Html.span "todo-count" $ let co = count Incomplete $ tasks model in
     [ strong_ [ text . pack $ show co ]
     , text $ " item" <> (if co == 1 then "" else "s") <> " left"
     ]
-  , ul "filters" $ fmap (\v -> model { visibility = v })
-                <$> (filterHtml (visibility model) <$> [minBound..maxBound])
+  , ul "filters" $ generalize #visibility .
+      filterHtml (visibility model) <$> [minBound..maxBound]
   ] ++ (if count Complete (tasks model) == 0 then [] else
-  [ button [ className "clear-completed", onClick $ clearComplete model ] [ "Clear completed" ]
+  [ button [ class' "clear-completed", onClick $ clearComplete model ] [ "Clear completed" ]
   ])
 
 
@@ -80,33 +164,33 @@
   ]
 
 
-newTaskForm :: MonadJSM m => Model -> Html m Model
-newTaskForm model = form [ className "todo-form", onSubmit (appendItem model) ]
-  [ input' [ className "new-todo"
+newTaskForm :: Model -> Html m Model
+newTaskForm model = form [ class' "todo-form", onSubmit (appendItem model) ]
+  [ input' [ class' "new-todo"
            , value . unDescription $ current model
-           , onInput $ updateDescription model . Description
+           , onInput $ ($ model) . (#current .~) . Description
            , placeholder "What needs to be done?" ]
   ]
 
 
-todoList :: MonadJSM m => Model -> Html m Model
-todoList model = ul "todo-list" $ taskView model <$> toVisible (visibility model) (tasks model)
+todoList :: Model -> Html m Model
+todoList model = ul "todo-list" $ taskView model <$> visibility model `toVisible` tasks model
 
 
-toggleAllBtn :: Applicative m => Model -> [Html m Model]
+toggleAllBtn :: Model -> [Html m Model]
 toggleAllBtn model =
-  [ input' [ id' "toggle-all", className "toggle-all", type' "checkbox", onChange (toggleAll model) ]
+  [ input' [ id' "toggle-all", class' "toggle-all", type' "checkbox", onChange $ toggleAll model ]
   , label [ for' "toggle-all" ] [ "Mark all as complete" ]
   ]
 
 
-view :: MonadJSM m => Model -> Html m Model
+view :: Functor m => Model -> Html m Model
 view model = div_
   [ section "todoapp" $
     header "header"
       [ h1_ [ "todos" ], newTaskForm model ]
     : htmlIfTasks model
-    [ section "main" $ toggleAllBtn model ++ [ todoList model ]
+    [ section "main" $ toggleAllBtn model <> [ todoList model ]
     , listFooter model
     ]
   , info
@@ -126,4 +210,3 @@
 main = do
   putStrLn "running app"
   runJSorWarp 8080 app
-
diff --git a/TODOMVC/Types.hs b/TODOMVC/Types.hs
deleted file mode 100644
--- a/TODOMVC/Types.hs
+++ /dev/null
@@ -1,40 +0,0 @@
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE OverloadedStrings          #-}
-
-
-module TODOMVC.Types where
-
-
-import           Data.String
-import           Data.Text
-
-
-newtype Description = Description { unDescription :: Text } deriving (Show, Read, Eq, IsString)
-newtype TaskId      = TaskId      { unTaskId      :: Int  } deriving (Show, Read, Eq, Ord, Num)
-
-
-data Completed  = Complete | Incomplete
-  deriving (Show, Read, Eq)
-
-
-data Visibility = All | Active | Completed
-  deriving (Show, Read, Eq, Ord, Enum, Bounded)
-
-
-data Task = Task
-  { description :: Description
-  , completed   :: Completed
-  , taskId      :: TaskId
-  } deriving (Show, Read, Eq)
-
-
-data Model = Model
-  { tasks      :: [Task]
-  , editing    :: Maybe TaskId
-  , visibility :: Visibility
-  , current    :: Description
-  } deriving (Show, Read, Eq)
-
-
-emptyModel :: Model
-emptyModel = Model [] Nothing All (Description "")
diff --git a/TODOMVC/Update.hs b/TODOMVC/Update.hs
deleted file mode 100644
--- a/TODOMVC/Update.hs
+++ /dev/null
@@ -1,59 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-
-
-module TODOMVC.Update where
-
-
-import           TODOMVC.Types
-
-
-appendItem :: Model -> Model
-appendItem m = if current m /= "" then m
-  { tasks = Task (current m) Incomplete ((+ 1)
-          $ Prelude.maximum $ 0 : (taskId <$> tasks m)) : tasks m
-  , current = "" }
-  else m
-
-
-updateDescription :: Model -> Description -> Model
-updateDescription m d = m { current = d }
-
-
-toggleCompleted :: Model -> TaskId -> Model
-toggleCompleted m tid = m { tasks =
-  (\t -> if taskId t == tid then t { completed = negC (completed t) } else t) <$> tasks m }
-  where negC Complete   = Incomplete
-        negC Incomplete = Complete
-
-
-toggleEditing :: Model -> Maybe TaskId -> Model
-toggleEditing m t = m { editing = t }
-
-
-updateTaskDescription :: Model -> TaskId -> Description -> Model
-updateTaskDescription m tid desc = m { tasks = f <$> tasks m}
-  where f t = if taskId t == tid then t { description = desc } else t
-
-
-removeTask :: Model -> TaskId -> Model
-removeTask m tid = m { tasks = filter ((/= tid) . taskId) $ tasks m}
-
-
-toggleAll :: Model -> Model
-toggleAll m = m { tasks = (\t -> t { completed = c }) <$> tasks m}
-  where c = if Prelude.all ((== Complete) . completed) $ tasks m then Incomplete else Complete
-
-
-count :: Completed -> [Task] -> Int
-count c = length . filter ((== c) . completed)
-
-
-clearComplete :: Model -> Model
-clearComplete m = m { tasks = filter ((== Incomplete) . completed) (tasks m) }
-
-
-toVisible :: Visibility -> [Task] -> [Task]
-toVisible v = case v of
-  All       -> id
-  Active    -> filter $ (== Incomplete) . completed
-  Completed -> filter $ (== Complete)   . completed
diff --git a/TODOMVCAtomic.hs b/TODOMVCAtomic.hs
deleted file mode 100644
--- a/TODOMVCAtomic.hs
+++ /dev/null
@@ -1,138 +0,0 @@
-{-# LANGUAGE CPP                  #-}
-{-# LANGUAGE ExtendedDefaultRules #-}
-{-# LANGUAGE OverloadedStrings    #-}
-{-# LANGUAGE RankNTypes           #-}
-{-# LANGUAGE ScopedTypeVariables  #-}
-{-# OPTIONS_GHC -fno-warn-type-defaults #-}
-
-
-module Main where
-
-
-import           Control.Lens                  (set, to, (%~), (.~), (?~), (^.),
-                                                _Wrapped)
-import           Data.Text                     hiding (count, filter, length)
-import           Prelude                       hiding (div, unwords)
-import           Shpadoinkle
-import           Shpadoinkle.Backend.Snabbdom
-import           Shpadoinkle.Html              hiding (main)
-import           Shpadoinkle.Html.LocalStorage
-import           Shpadoinkle.Html.Memo
-import           Shpadoinkle.Html.Utils
-
-import           TODOMVCAtomic.Types
-import           TODOMVCAtomic.Update
-
-
-default (Text)
-
-
-filterHtml :: Applicative m => Eq v => Show v => v -> v -> Html m v
-filterHtml = memo $ \cur item -> li_
-  [ a [href "#" , onClick item , className [("selected", cur == item)]] [ text . pack $ show item ]
-  ]
-
-
-htmlIfTasks :: [b] -> [Html m a] -> [Html m a]
-htmlIfTasks m h' = if Prelude.null m then [] else h'
-
-
-taskView :: MonadJSM m => Maybe TaskId -> Task -> Html m (Model -> Model)
-taskView = memo $ \ed (Task (Description d) c tid) ->
-  li [ id' . pack . show $ tid ^. _Wrapped
-     , className [ ("completed", c == Complete)
-                 , ("editing",   Just tid == ed) ]
-     ]
-  [ div "view"
-    [ input' [ type' "checkbox"
-             , className "toggle"
-             , onChange $ toggleCompleted tid
-             , checked $ c == Complete
-             ]
-    , label [ onDblclick $ editing ?~ tid ] [ text d ]
-   , button' [ className "destroy", onClick $ tasks %~ filter ((/= tid) . _taskId) ]
-    ]
-  , form [ onSubmit $ editing .~ Nothing ]
-    [ input' [ className "edit"
-             , value d
-             , onInput $ updateTaskDescription tid . Description
-             , autofocus True
-             , onBlur $ editing .~ Nothing
-             ]
-    ]
-  ]
-
-
-listFooter :: Applicative m => Int -> Int -> Visibility -> Html m (Model -> Model)
-listFooter = memo $ \ic cc v -> footer "footer" $
-  [ Shpadoinkle.Html.span "todo-count"
-    [ strong_ [ text . pack $ show ic ]
-    , text $ " item" <> (if ic == 1 then "" else "s") <> " left"
-    ]
-  , ul "filters" $ fmap (set visibility) . filterHtml v <$> [minBound..maxBound]
-  ] ++ (if cc == 0 then [] else
-  [ button [ className "clear-completed", onClick clearComplete ] [ "Clear completed" ]
-  ])
-
-
-
-info :: Html m a
-info = footer "info"
-  [ p_ [ "Double-click to edit a todo" ]
-  , p_ [ "Credits ", a [ href "https://twitter.com/fresheyeball" ] [ "Isaac Shapira" ] ]
-  , p_ [ "Part of ", a [ href "http://todomvc.com" ] [ "TodoMVC" ] ]
-  ]
-
-
-newTaskForm :: MonadJSM m => Description -> Html m (Model -> Model)
-newTaskForm = memo $ \desc -> form [ className "todo-form", onSubmit appendItem' ]
-  [ input' [ className "new-todo"
-           , value $ desc ^. _Wrapped
-           , onInput . set $ current . _Wrapped
-           , placeholder "What needs to be done?" ]
-  ]
-
-
-todoList :: MonadJSM m => Maybe TaskId -> Visibility -> [Task] -> Html m (Model -> Model)
-todoList = memo $ \ed v ts -> ul "todo-list" $ taskView ed <$> toVisible v ts
-
-
-toggleAllBtn :: Applicative m => [Html m (Model -> Model)]
-toggleAllBtn =
-  [ input' [ id' "toggle-all", className "toggle-all", type' "checkbox", onChange toggleAll ]
-  , label [ for' "toggle-all" ] [ "Mark all as complete" ]
-  ]
-
-
-apply :: Functor m => a -> Html m (a -> b) -> Html m b
-apply m v = ($ m) <$> v
-
-
-
-render :: MonadJSM m => Model -> Html m Model
-render m = div_
-  [ section "todoapp" $
-    header "header"
-      [ h1_ [ "todos" ], apply m . newTaskForm $ m ^. current ]
-    : htmlIfTasks (m ^. tasks)
-    [ section "main" $ (apply m <$> toggleAllBtn) ++ [ apply m $ todoList (m ^. editing) (m ^. visibility) (m ^. tasks) ]
-    , apply m $ listFooter (m ^. tasks . to (count Incomplete))
-                           (m ^. tasks . to (count Complete))
-                           (m ^. visibility)
-    ]
-  , info
-  ]
-
-
-app :: JSM ()
-app = do
-  model <- manageLocalStorage "todo" emptyModel
-  addStyle "https://cdn.jsdelivr.net/npm/todomvc-common@1.0.5/base.css"
-  addStyle "https://cdn.jsdelivr.net/npm/todomvc-app-css@2.2.0/index.css"
-  initial <- readTVarIO model
-  shpadoinkle id runSnabbdom initial model render stage
-
-
-main :: IO ()
-main = runJSorWarp 8080 app
-
diff --git a/TODOMVCAtomic/Types.hs b/TODOMVCAtomic/Types.hs
deleted file mode 100644
--- a/TODOMVCAtomic/Types.hs
+++ /dev/null
@@ -1,61 +0,0 @@
-{-# LANGUAGE FlexibleInstances          #-}
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE MultiParamTypeClasses      #-}
-{-# LANGUAGE OverloadedStrings          #-}
-{-# LANGUAGE TemplateHaskell            #-}
-{-# LANGUAGE TypeFamilies               #-}
-
-
-module TODOMVCAtomic.Types where
-
-
-import           Control.Lens.TH
-import           Data.String
-import           Data.Text
-
-
-newtype Description = Description { _unDescription :: Text } deriving (Show, Read, Eq, IsString)
-newtype TaskId      = TaskId      { _unTaskId      :: Int  } deriving (Show, Read, Eq, Ord, Num)
-
-
-makeWrapped ''Description
-makeWrapped ''TaskId
-
-
-data Completed  = Complete | Incomplete
-  deriving (Show, Read, Eq)
-
-
-makePrisms ''Completed
-
-
-data Visibility = All | Active | Completed
-  deriving (Show, Read, Eq, Ord, Enum, Bounded)
-
-
-makePrisms ''Visibility
-
-
-data Task = Task
-  { _description :: Description
-  , _completed   :: Completed
-  , _taskId      :: TaskId
-  } deriving (Show, Read, Eq)
-
-
-makeLenses ''Task
-
-
-data Model = Model
-  { _tasks      :: [Task]
-  , _editing    :: Maybe TaskId
-  , _visibility :: Visibility
-  , _current    :: Description
-  } deriving (Show, Read, Eq)
-
-
-makeLenses ''Model
-
-
-emptyModel :: Model
-emptyModel = Model [] Nothing All (Description "")
diff --git a/TODOMVCAtomic/Update.hs b/TODOMVCAtomic/Update.hs
deleted file mode 100644
--- a/TODOMVCAtomic/Update.hs
+++ /dev/null
@@ -1,46 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-
-
-module TODOMVCAtomic.Update where
-
-
-import           TODOMVCAtomic.Types
-
-
-appendItem' :: Model -> Model
-appendItem' m = if _current m /= "" then m
-  { _tasks = Task (_current m) Incomplete ((+ 1)
-          $ Prelude.maximum $ 0 : (_taskId <$> _tasks m)) : _tasks m
-  , _current = "" }
-  else m
-
-toggleCompleted :: TaskId -> Model -> Model
-toggleCompleted tid m = m { _tasks =
-  (\t -> if _taskId t == tid then t { _completed = negC (_completed t) } else t) <$> _tasks m }
-  where negC Complete   = Incomplete
-        negC Incomplete = Complete
-
-
-updateTaskDescription :: TaskId -> Description -> Model -> Model
-updateTaskDescription tid desc m = m { _tasks = f <$> _tasks m}
-  where f t = if _taskId t == tid then t { _description = desc } else t
-
-
-toggleAll :: Model -> Model
-toggleAll m = m { _tasks = (\t -> t { _completed = c }) <$> _tasks m}
-  where c = if Prelude.all ((== Complete) . _completed) $ _tasks m then Incomplete else Complete
-
-
-count :: Completed -> [Task] -> Int
-count c = length . filter ((== c) . _completed)
-
-
-clearComplete :: Model -> Model
-clearComplete m = m { _tasks = filter ((== Incomplete) . _completed) (_tasks m) }
-
-
-toVisible :: Visibility -> [Task] -> [Task]
-toVisible v = case v of
-  All       -> id
-  Active    -> filter $ (== Incomplete) . _completed
-  Completed -> filter $ (== Complete)   . _completed
diff --git a/ThrottleAndDebounce.hs b/ThrottleAndDebounce.hs
new file mode 100644
--- /dev/null
+++ b/ThrottleAndDebounce.hs
@@ -0,0 +1,52 @@
+{-# LANGUAGE OverloadedStrings   #-}
+{-# LANGUAGE RankNTypes          #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TupleSections       #-}
+
+
+module Main where
+
+
+import           Control.Monad.IO.Class
+import           Data.Text                   (Text, pack)
+import           Shpadoinkle
+import           Shpadoinkle.Backend.ParDiff
+import           Shpadoinkle.Html
+
+
+type Model = (Int, Text)
+
+
+type App = ParDiffT Model JSM
+
+
+data Control m = Control
+  (Debounce m Model Model)
+  (Debounce m (Text -> Model) Model)
+  (Throttle m Model Model)
+  (Throttle m (Text -> Model) Model)
+
+
+view :: Control m -> Model -> Html m Model
+view (Control debouncer1 debouncer2 throttler1 throttler2) (count, txt) = div_
+  [ text ("Count: " <> pack (show count))
+  , div_ [ button [ onClick (count+1, txt) ] [ text "Increment" ] ]
+  , div_ [ button [ runThrottle throttler1 onClick (count+1, txt) ] [ text "Increment (throttle)" ] ]
+  , div_ [ button [ runDebounce debouncer1 onClick (count+1, txt) ] [ text "Increment (debounce)" ] ]
+  , div_ [ text "Debounced input", input [ runDebounce debouncer2 onInput (count,) ] [ ] ]
+  , div_ [ text txt ]
+  , div_ [ text "Throttled input", input [ runThrottle throttler2 onInput (count,) ] [ ] ]
+  ]
+
+
+app :: Control App -> JSM ()
+app control = do
+  model <- liftIO $ newTVarIO initial
+  shpadoinkle id runParDiff initial model (view control) getBody
+  where initial = (0, "")
+
+
+main :: IO ()
+main = do
+  control <- Control <$> debounce 1 <*> debounce 2 <*> throttle 1 <*> throttle 2
+  runJSorWarp 8080 (app control)
diff --git a/servant-crud/Client.hs b/servant-crud/Client.hs
--- a/servant-crud/Client.hs
+++ b/servant-crud/Client.hs
@@ -12,11 +12,15 @@
 import           Control.Monad.Catch         (MonadThrow)
 import           Control.Monad.Reader        (MonadIO)
 import           Data.Proxy                  (Proxy (..))
+import           Language.Javascript.JSaddle (askJSM, runJSM)
 import           Servant.API                 ((:<|>) (..))
 import           Shpadoinkle
 import           Shpadoinkle.Backend.ParDiff (runParDiff)
 import           Shpadoinkle.Html.Utils      (getBody)
 import           Shpadoinkle.Router          (fullPageSPA, withHydration)
+#ifndef ghcjs_HOST_OS
+import           Shpadoinkle.Router          (MonadJSM)
+#endif
 import           Shpadoinkle.Router.Client   (client, runXHR)
 import           UnliftIO                    (MonadUnliftIO (..), UnliftIO (..))
 
@@ -37,11 +41,11 @@
 
 
 instance CRUDSpaceCraft App where
-  listSpaceCraft       = runXHR App listSpaceCraftM
-  getSpaceCraft        = runXHR App . getSpaceCraftM
-  updateSpaceCraft x y = runXHR App $ updateSpaceCraftM x y
-  createSpaceCraft     = runXHR App . createSpaceCraftM
-  deleteSpaceCraft     = runXHR App . deleteSpaceCraftM
+  listSpaceCraft       = App $ runXHR listSpaceCraftM
+  getSpaceCraft x      = App . runXHR $ getSpaceCraftM x
+  updateSpaceCraft x y = App . runXHR $ updateSpaceCraftM x y
+  createSpaceCraft x   = App . runXHR $ createSpaceCraftM x
+  deleteSpaceCraft x   = App . runXHR $ deleteSpaceCraftM x
 
 
 (listSpaceCraftM :<|> getSpaceCraftM :<|> updateSpaceCraftM :<|> createSpaceCraftM :<|> deleteSpaceCraftM)
@@ -49,7 +53,7 @@
 
 
 app :: JSM ()
-app = fullPageSPA @ SPA runApp runParDiff (withHydration start) view getBody (const . start) routes
+app = fullPageSPA @ SPA runApp runParDiff (withHydration start) view getBody start routes
 
 
 main :: IO ()
diff --git a/servant-crud/Server.hs b/servant-crud/Server.hs
--- a/servant-crud/Server.hs
+++ b/servant-crud/Server.hs
@@ -27,6 +27,7 @@
 import           Servant.Server
 
 import           Shpadoinkle
+import           Shpadoinkle.Router        (MonadJSM)
 import           Shpadoinkle.Router.Server
 
 import           Types
diff --git a/servant-crud/Types.hs b/servant-crud/Types.hs
--- a/servant-crud/Types.hs
+++ b/servant-crud/Types.hs
@@ -155,7 +155,7 @@
 instance (FromJSON (Column [SpaceCraft])) => FromJSON Frontend
 
 
-makeLenses ''Frontend
+makePrisms ''Frontend
 
 
 data Route
@@ -244,7 +244,7 @@
 
   toRows = fmap SpaceCraftRow
 
-  toCell xs (SpaceCraftRow SpaceCraft {..}) = \case
+  toCell _ (SpaceCraftRow SpaceCraft {..}) = \case
     SKUT          -> present _sku
     DescriptionT  -> present _description
     SerialNumberT -> present _serial
@@ -252,9 +252,12 @@
     OperableT     -> present _operable
     ToolsT        ->
       [ H.div "btn-group"
-        [ H.button [ H.class' "btn btn-sm btn-secondary", H.onClick' (xs <$ navigate @ SPA (RExisting _identity)) ] [ "Edit" ]
-        , H.button [ H.class' "btn btn-sm btn-secondary", H.onClick' (Prelude.filter (\x -> x ^. identity /= _identity) xs
-                          <$ deleteSpaceCraft _identity) ] [ "Delete" ]
+        [ H.button [ H.className "btn btn-sm btn-secondary",
+                     H.onClickM_ $ navigate @ SPA (RExisting _identity) ] [ "Edit" ]
+        , H.button [ H.className "btn btn-sm btn-secondary",
+                     H.onClickM $ do
+                       deleteSpaceCraft _identity
+                       return . Prelude.filter $ \x -> x ^. identity /= _identity ] [ "Delete" ]
         ]
       ]
 
diff --git a/servant-crud/View.hs b/servant-crud/View.hs
--- a/servant-crud/View.hs
+++ b/servant-crud/View.hs
@@ -29,7 +29,7 @@
 import           Data.Text                         as T
 import           Shpadoinkle                       (Html, MonadJSM, text)
 import qualified Shpadoinkle.Html                  as H
-import           Shpadoinkle.Lens                  ((<%), (<+))
+import           Shpadoinkle.Lens
 import           Shpadoinkle.Router                (navigate, toHydration)
 import           Shpadoinkle.Widgets.Form.Dropdown as Dropdown (Dropdown (..),
                                                                 Theme (..),
@@ -45,7 +45,7 @@
                                                     Selected, Status (..),
                                                     Toggle (..), Validated (..),
                                                     fullset, fuzzySearch,
-                                                    getValid, humanize,
+                                                    getValid, humanize, present,
                                                     validate, withOptions')
 
 import           Types
@@ -78,7 +78,7 @@
   , H.div "col-sm-10" $
     [ ef <% l . mapping (fromMaybe "" `iso` noEmpty) $ Input.text
       [ H.name' hName
-      , H.className ("form-control":controlClass (errs ^. l) (ef ^. l .hygiene))
+      , H.class' ("form-control":controlClass (errs ^. l) (ef ^. l .hygiene))
       ]
     ]
     <> invalid (errs ^. l) (ef ^. l . hygiene)
@@ -95,9 +95,9 @@
 intControl l msg errs ef = formGroup
   [ H.label [ H.for' hName, H.class' "col-sm-2 col-form-label" ] [ text msg ]
   , H.div "col-sm-10" $
-    [ ef <% l $ Input.integral @m
+    [ ef <% l $ Input.integral
       $ [ H.name' hName, H.step "1", H.min "0"
-        , H.className ("form-control":controlClass (errs ^. l) (ef ^. l .hygiene))
+        , H.class' ("form-control":controlClass (errs ^. l) (ef ^. l .hygiene))
         ]
     ]
     <> invalid (errs ^. l) (ef ^. l . hygiene)
@@ -108,7 +108,7 @@
   :: forall p x m a
    . MonadJSM m => Control (Dropdown p)
   => Considered p ~ Maybe => Consideration ConsideredChoice p
-  => Present (Selected p x) => Present x => Ord x
+  => Present (Selected p x) => Ord x
   => (forall v. Lens' (a v) (Field v Text (Dropdown p) x))
   -> Text -> a 'Errors -> a 'Edit -> Html m (a 'Edit)
 selectControl l msg errs ef = formGroup
@@ -121,19 +121,19 @@
   where
   bootstrap Dropdown {..} = Dropdown.Theme
     { _wrapper = H.div
-      [ H.className [ ("dropdown", True)
-                    , ("show", _toggle == Open) ]
+      [ H.class' [ ("dropdown", True)
+                 , ("show", _toggle == Open) ]
       ]
-    , _header  = pure . H.button
-      [ H.className ([ "btn", "btn-secondary", "dropdown-toggle" ] :: [Text])
+    , _header  = \cs -> pure $ H.button
+      [ H.class' ([ "btn", "btn-secondary", "dropdown-toggle" ] :: [Text])
       , H.type' "button"
-      ]
+      ] (present cs)
     , _list    = H.div
-      [ H.className [ ("dropdown-menu", True)
-                    , ("show", _toggle == Open) ]
+      [ H.class' [ ("dropdown-menu", True)
+                 , ("show", _toggle == Open) ]
       ]
-    , _item    = H.a [ H.className "dropdown-item"
-                     , H.textProperty "style" "cursor:pointer" ]
+    , _item    = const $ H.a' [ H.className "dropdown-item"
+                              , H.textProperty "style" "cursor:pointer" ]
     }
 
 
@@ -163,17 +163,17 @@
   , H.div "d-flex flex-row justify-content-end"
 
     [ H.button
-      [ H.onClick' (ef <$ navigate @SPA (RList mempty))
+      [ H.onClickM_ . navigate @SPA $ RList mempty
       , H.class' "btn btn-secondary"
       ] [ "Cancel" ]
 
     , H.button
-      [ H.onClick' $ case isValid of
-         Nothing -> return ef
+      [ H.onClickM_ $ case isValid of
+         Nothing -> return ()
          Just up -> do
            case mid of Nothing  -> () <$ createSpaceCraft up
                        Just sid -> updateSpaceCraft sid up
-           ef <$ navigate @SPA (RList mempty)
+           navigate @SPA (RList mempty)
       , H.class' "btn btn-primary"
       , H.disabled $ isNothing isValid
       ] [ "Save" ]
@@ -197,8 +197,8 @@
 
 tableCfg :: Table.Theme m [SpaceCraft]
 tableCfg = mempty
-  { tableProps = [ H.class' "table table-striped table-bordered" ]
-  , tdProps    = \case
+  { tableProps = const . const . pure $ H.class' "table table-striped table-bordered"
+  , tdProps    = const . const . const $ \case
       ToolsT -> [ H.width 1 ]
       _ -> "align-middle"
   }
@@ -217,7 +217,7 @@
 view :: (MonadJSM m, CRUDSpaceCraft m) => Frontend -> Html m Frontend
 view fe = case fe of
 
-  MList r -> MList <$> H.div "container-fluid"
+  MList r -> onSum _MList $ H.div "container-fluid"
    [ H.div "row justify-content-between align-items-center"
      [ H.h2_ [ "Space Craft Roster" ]
      , H.div [ H.class' "input-group"
@@ -225,16 +225,16 @@
              ]
        [ r <% search $ Input.search [ H.class' "form-control", H.placeholder "Search" ]
        , H.div "input-group-append mr-3"
-         [ H.button [ H.onClick' (r <$ navigate @SPA RNew), H.class' "btn btn-primary" ] [ "Register" ]
+         [ H.button [ H.onClickM_ $ navigate @SPA RNew, H.class' "btn btn-primary" ] [ "Register" ]
          ]
        ]
      ]
-   , r <+ lensProduct table sort $ Table.viewWith tableCfg
-    (r ^. table . to (fuzzySearch fuzzy $ r ^. search . value))
-    (r ^. sort)
+   , onRecord (lensProduct table sort) $ Table.viewWith tableCfg
+       (r ^. table . to (fuzzySearch fuzzy $ r ^. search . value))
+       (r ^. sort)
    ]
 
-  MDetail sid form -> MDetail sid <$> H.div "row"
+  MDetail sid form -> onSum (_MDetail . _2) $ H.div "row"
     [ H.div "col-sm-8 offset-sm-2"
       [ H.h2_ [ text $ maybe "Register New Space Craft" (const "Edit Space Craft") sid
               ]
@@ -244,7 +244,7 @@
 
   MEcho t -> H.div_
     [ maybe (text "Erie silence") text t
-    , H.a [ H.onClick' (fe <$ navigate @SPA (RList $ Input Clean "")) ] [ "Go To Space Craft Roster" ]
+    , H.a [ H.onClickM_ . navigate @SPA $ RList mempty ] [ "Go To Space Craft Roster" ]
     ]
 
   M404 -> text "404"
@@ -255,7 +255,7 @@
   [ H.head_
     [ H.link'
       [ H.rel "stylesheet"
-      , H.href "https://cdn.usebootstrap.com/bootstrap/4.3.1/css/bootstrap.min.css"
+      , H.href "https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/css/bootstrap.min.css"
       ]
     , H.meta [ H.charset "ISO-8859-1" ] []
     , toHydration fe
diff --git a/widgets/Widgets.hs b/widgets/Widgets.hs
--- a/widgets/Widgets.hs
+++ b/widgets/Widgets.hs
@@ -1,25 +1,30 @@
 {-# LANGUAGE DataKinds            #-}
 {-# LANGUAGE ExtendedDefaultRules #-}
+{-# LANGUAGE FlexibleContexts     #-}
 {-# LANGUAGE FlexibleInstances    #-}
+{-# LANGUAGE MonoLocalBinds       #-}
 {-# LANGUAGE OverloadedStrings    #-}
 {-# LANGUAGE RecordWildCards      #-}
+{-# LANGUAGE TemplateHaskell      #-}
 {-# OPTIONS_GHC -fno-warn-type-defaults #-}
 
 
 module Main where
 
 
+import           Control.Lens                      hiding (view)
 import           Control.Monad.IO.Class            (liftIO)
 import           Data.Text
 import           Prelude                           hiding (div)
 
 import           Shpadoinkle
 import           Shpadoinkle.Backend.ParDiff
-import           Shpadoinkle.Html                  as H (a, button, className,
-                                                         div, div_, href, id',
-                                                         link', rel,
-                                                         textProperty, type')
+import           Shpadoinkle.Html                  as H (a, button, class', div,
+                                                         div_, href, id', link',
+                                                         rel, textProperty,
+                                                         type')
 import           Shpadoinkle.Html.Utils
+import           Shpadoinkle.Lens
 import           Shpadoinkle.Widgets.Form.Dropdown as Dropdown
 import           Shpadoinkle.Widgets.Types
 
@@ -43,34 +48,35 @@
   { _pickOne        :: Dropdown 'One Cheese
   , _pickAtleastOne :: Dropdown 'AtleastOne Cheese
   } deriving (Eq, Show)
+makeLenses ''Model
 
 
-view :: MonadJSM m => Model -> Html m Model
+view :: Monad m => Model -> Html m Model
 view m = div_
   [ link' [ rel "stylesheet", href "https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" ]
-  , (\x -> m {_pickOne = x}) <$>
-    dropdown bootstrap defConfig { _attrs = [ id' "One" ] } (_pickOne m)
-  , (\x -> m {_pickAtleastOne = x}) <$>
-    dropdown bootstrap defConfig { _attrs = [ id' "AtleastOne" ] } (_pickAtleastOne m)
+  , generalize pickOne $ dropdown bootstrap defConfig { _attrs = [ id' "One" ] } (_pickOne m)
+  , generalize pickAtleastOne $ dropdown bootstrap defConfig { _attrs = [ id' "AtleastOne" ] } (_pickAtleastOne m)
   ]
   where
+  bootstrap :: Present b => Present (Selected p b) => Dropdown p b -> Theme m p b
   bootstrap Dropdown {..} = Dropdown.Theme
     { _wrapper = div
-      [ className [ ("dropdown", True)
-                  , ("show", _toggle == Open) ]
+      [ class' [ ("dropdown", True)
+               , ("show", _toggle == Open) ]
       ]
     , _header  = pure . button
-      [ className ([ "btn", "btn-secondary", "dropdown-toggle" ] :: [Text])
+      [ class' ([ "btn", "btn-secondary", "dropdown-toggle" ] :: [Text])
       , type' "button"
       ]
+      . present
     , _list    = div
-      [ className [ ("dropdown-menu", True)
-                  , ("show", _toggle == Open) ]
+      [ class' [ ("dropdown-menu", True)
+               , ("show", _toggle == Open) ]
       ]
-    , _item    = a [ className "dropdown-item"
+    , _item    = a [ class' "dropdown-item"
                    , textProperty "style" "cursor:pointer" ]
+                 . present
     }
-
 
 
 initial :: Model
