diff --git a/docs/badge/application-programming.rst b/docs/badge/application-programming.rst
index f6ad0c6eb6558d94a6a098ab0fe6e323b9c5a947..dd3284f1db74c7af4bc4a579342cd2271458c408 100644
--- a/docs/badge/application-programming.rst
+++ b/docs/badge/application-programming.rst
@@ -156,3 +156,286 @@ This becomes important if you need exact timings in your application,
 as the Reactor makes no explicit guarantee about how often `think()` will
 be called. Currently we are shooting for once every 20 milliseconds, but if something in the system
 takes a bit longer to process something, this number can change from one call to the next.
+
+
+Example 1d: Automatic input processing
+--------------------------------------
+
+Working on the bare state of the buttons and the captouch petals can be cumbersome and error prone.
+the flow3r application framework gives you a bit of help in the form of the :py:class:`InputController`
+which processes an input state and gives you higher level information about what is happening.
+
+The following example shows how to properly react to single button presses without having to
+think about what happens if the user presses the button for a long time. It uses the `InputController`
+to detect single button presses and switches between showing a circle (by drawing a 360 deg arc) and
+a square.
+
+
+.. code-block:: python
+
+    from st3m.reactor import Responder
+    from st3m.input import InputController
+    from st3m.utils import tau
+
+    from hardware import BUTTON_PRESSED_LEFT, BUTTON_PRESSED_RIGHT, BUTTON_PRESSED_DOWN
+    import st3m.run
+
+    class Example(Responder):
+        def __init__(self) -> None:
+            self.input = InputController()
+            self._x = -20.
+            self._draw_rectangle = True
+
+        def draw(self, ctx: Context) -> None:
+            # Paint the background black
+            ctx.rgb(0, 0, 0).rectangle(-120, -120, 240, 240).fill()
+
+            # Paint a red square in the middle of the display
+            if self._draw_rectangle:
+                ctx.rgb(255, 0, 0).rectangle(self._x, -20, 40, 40).fill()
+            else:
+                ctx.rgb(255, 0, 0).arc(self._x, -20, 40, 0, tau, 0).fill()
+
+        def think(self, ins: InputState, delta_ms: int) -> None:
+            self.input.think(ins, delta_ms) # let the input controller to its magic
+
+            direction = ins.left_button # -1 (left), 1 (right), or 2 (pressed)
+
+            if self.input.right_shoulder.middle.pressed:
+                self._draw_rectangle = not self._draw_rectangle
+
+            if direction == BUTTON_PRESSED_LEFT:
+                self._x -= 20 * delta_ms / 1000
+            elif direction == BUTTON_PRESSED_RIGHT:
+                self._x += 40 * delta_ms / 1000
+
+
+    st3m.run.run_responder(Example())
+
+
+Managing multiple views
+----------------------------------------
+
+If you want to write a more advanced application you probably also want to display more than
+one screen (or view as we call them).
+With just the Responder class this can become a bit tricky as it never knows when it is visible and
+when it is not. It also doesn't directly allow you to launch a new screen.
+
+To help you with that you can use a :py:class:`View` instead. It can tell you when
+it becomes visible and you can also use it to bring a new screen or widget into the foreground or remove
+it again from the screen.
+
+Example 2a: Managing two views
+--------------------------------
+
+In this example we use a basic `View` to switch between to different screens using a button. One screen
+shows a red square, the other one a green square. You can of course put any kind of complex processing
+into the two different views. We make use of an `InputController` again to handle the button presses.
+
+
+.. code-block:: python
+
+    from st3m.input import InputController
+    from st3m.ui.view import View
+    import st3m.run
+
+    class SecondScreen(View):
+        def __init__(self) -> None:
+            self.input = InputController()
+            self._vm = None
+
+        def on_enter(self, vm: Optional[ViewManager]) -> None:
+            self._vm = vm
+
+            # Ignore the button which brought us here until it is released
+            self.input._ignore_pressed()
+
+        def draw(self, ctx: Context) -> None:
+            # Paint the background black
+            ctx.rgb(0, 0, 0).rectangle(-120, -120, 240, 240).fill()
+            # Green square
+            ctx.rgb(0, 255, 0).rectangle(-20, -20, 40, 40).fill()
+
+        def think(self, ins: InputState, delta_ms: int) -> None:
+            self.input.think(ins, delta_ms) # let the input controller to its magic
+
+            if self.input.right_shoulder.middle.pressed:
+                self._vm.pop()
+
+
+    class Example(View):
+        def __init__(self) -> None:
+            self.input = InputController()
+            self._vm = None
+
+        def draw(self, ctx: Context) -> None:
+            # Paint the background black
+            ctx.rgb(0, 0, 0).rectangle(-120, -120, 240, 240).fill()
+            # Red square
+            ctx.rgb(255, 0, 0).rectangle(-20, -20, 40, 40).fill()
+
+
+        def on_enter(self, vm: Optional[ViewManager]) -> None:
+            self._vm = vm
+
+        def think(self, ins: InputState, delta_ms: int) -> None:
+            self.input.think(ins, delta_ms) # let the input controller to its magic
+
+            if self.input.right_shoulder.middle.pressed:
+                self._vm.push(SecondScreen())
+
+    st3m.run.run_view(Example())
+
+Try it using `mpremote`. The right shoulder button switches between the two views. To avoid that
+the still pressed button immediately closes `SecondScreen` we make us of a special method of the
+`InputController` which hides the pressed button from the view until it is released again.
+
+Example 2b: Easier view management
+----------------------------------
+
+The idea that a button (physical or captouch) is used to enter / exit a view is so universal that
+there is a special view which helps you with that: :py:class:`ViewWithInputState`. It integrates an
+`InputController` and handles the ignoring of extra presses:
+
+.. code-block:: python
+
+    from st3m.ui.view import ViewWithInputState
+    import st3m.run
+
+    class SecondScreen(ViewWithInputState):
+        def __init__(self) -> None:
+            super().__init__()
+            self._vm = None
+
+        def on_enter(self, vm: Optional[ViewManager]) -> None:
+            super().on_enter(vm) # Let ViewWithInputState do its thing
+            self._vm = vm
+
+        def draw(self, ctx: Context) -> None:
+            # Paint the background black
+            ctx.rgb(0, 0, 0).rectangle(-120, -120, 240, 240).fill()
+            # Green square
+            ctx.rgb(0, 255, 0).rectangle(-20, -20, 40, 40).fill()
+
+        def think(self, ins: InputState, delta_ms: int) -> None:
+            super().think(ins, delta_ms) # Let ViewWithInputState do its thing
+
+            if self.input.right_shoulder.middle.pressed:
+                self._vm.pop()
+
+
+    class Example(ViewWithInputState):
+        def __init__(self) -> None:
+            super().__init__()
+            self._vm = None
+
+        def draw(self, ctx: Context) -> None:
+            # Paint the background black
+            ctx.rgb(0, 0, 0).rectangle(-120, -120, 240, 240).fill()
+            # Red square
+            ctx.rgb(255, 0, 0).rectangle(-20, -20, 40, 40).fill()
+
+
+        def on_enter(self, vm: Optional[ViewManager]) -> None:
+            self._vm = vm
+
+        def think(self, ins: InputState, delta_ms: int) -> None:
+            super().think(ins, delta_ms) # Let ViewWithInputState do its thing
+
+            if self.input.right_shoulder.middle.pressed:
+                self._vm.push(SecondScreen())
+
+    st3m.run.run_view(Example())
+
+
+
+Writing an application for the menu system
+------------------------------------------
+
+All fine and good, you were able to write an application that you can run with `mpremote`,
+but certainly you also want to run it from flow3r's menu system.
+
+Let's introduce the final class you should actually be using for application development:
+:py:class:`Application` (yeah, right).
+
+.. code-block:: python
+
+    from st3m.application import Application
+    import st3m.run
+
+    class SecondScreen(ViewWithInputState):
+        def __init__(self) -> None:
+            super().__init__()
+            self._vm = None
+
+        def on_enter(self, vm: Optional[ViewManager]) -> None:
+            super().on_enter(vm) # Let ViewWithInputState do its thing
+            self._vm = vm
+
+        def draw(self, ctx: Context) -> None:
+            # Paint the background black
+            ctx.rgb(0, 0, 0).rectangle(-120, -120, 240, 240).fill()
+            # Green square
+            ctx.rgb(0, 255, 0).rectangle(-20, -20, 40, 40).fill()
+
+        def think(self, ins: InputState, delta_ms: int) -> None:
+            super().think(ins, delta_ms) # Let ViewWithInputState do its thing
+
+            if self.input.right_shoulder.middle.pressed:
+                self._vm.pop()
+
+
+    class MyDemo(Application):
+        def __init__(self) -> None:
+            super().__init__(name="My demo")
+
+        def draw(self, ctx: Context) -> None:
+            # Paint the background black
+            ctx.rgb(0, 0, 0).rectangle(-120, -120, 240, 240).fill()
+            # Red square
+            ctx.rgb(255, 0, 0).rectangle(-20, -20, 40, 40).fill()
+
+        def on_enter(self, vm: Optional[ViewManager]) -> None:
+            super().on_enter(vm) # Let Application do its thing
+
+        def think(self, ins: InputState, delta_ms: int) -> None:
+            super().think(ins, delta_ms) # Let Application do its thing
+
+            if self.input.right_shoulder.middle.pressed:
+                self._view_manager.push(SecondScreen())
+
+    st3m.run.run_view(Example())
+
+The `Application` class gives you the following extras:
+
+ - Pressing the down the left shoulder button exits the app
+ - You get a `_view_manager` member to manager your views
+ - It can be picked up by the main menu system
+
+
+To add the application to the menu we are missing one more thing: a `flow3r.toml`
+file which describes the application so flow3r knows where to put it in the menu system.
+Together with the Python code this file forms a so called bundle
+(see also :py:class:`BundleMetadata`).
+
+-- code-block::
+
+    [app]
+    name = "My Demo"
+    menu = "Apps"
+
+    [entry]
+    class = "MyDemo"
+
+    [metadata]
+    author = "You :)"
+    license = "pick one, LGPL/MIT maybe?"
+    url = "https://git.flow3r.garden/you/mydemo"
+
+
+Save this as `flow3r.toml` together with the Python code as `__init__.py` in a folder and put
+that folder into the `apps` folder on your flow3r (if there is no `apps` folder visible,
+there might be an `apps` folder in the `sys` folder). Restart the flow3r and it should pick up your
+new application.
+
+
diff --git a/python_payload/st3m/ui/view.py b/python_payload/st3m/ui/view.py
index a3560a87f920a7ebc531771778638fd4cc563808..8a8d050a257797c24eb8aed16f02174f97674b10 100644
--- a/python_payload/st3m/ui/view.py
+++ b/python_payload/st3m/ui/view.py
@@ -28,7 +28,7 @@ class ViewWithInputState(View):
 
     Derive this class, then use self.input to access the InputController.
 
-    Remember to call super().think() in think()!
+    Remember to call super().__init__() in __init__() and super().think() in think()!
     """
 
     __slots__ = ("input",)