Repository: mpl-extensions/mpl-interactions (language: python)

Issue:
docs!: switch to MyST
Closes #206 

**Warning**: Notebooks are now rendered with [`myst-nb`](https://myst-nb.readthedocs.io), but this has no equivalent to [`nbsphinx_prolog`](https://nbsphinx.readthedocs.io/en/latest/prolog-and-epilog.html), so the prologue message is not visible in the example pages anymore.

![image](https://user-images.githubusercontent.com/29308176/126877109-1acc03a6-022d-47b4-87ff-8eeaed0f4af7.png)

A solution would be to switch to the [sphinx-book-theme](https://sphinx-book-theme.readthedocs.io) for its [launch buttons](https://sphinx-book-theme.readthedocs.io/en/latest/launch.html). That requires some more modifications though, so I split that into a follow-up PR (#209).

Some related improvements:
- 2bb7581 Rendered the `Controlls` class in the API
- 26ade4e Show-cased some advantages of MyST in the notebooks.
- fc7810d Links are checked with [`linkcheck`](https://www.sphinx-doc.org/en/master/_modules/sphinx/builders/linkcheck.html) (see #202)
- 609059f Enforced [Prettier](https://prettier.io) formatting. That's useful now that all doc files are Markdown.


Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index c037973c..2582d65a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -130,18 +130,5 @@ dmypy.json
 
 *.swp
 
-_build
-
-# autoapi stuff - may be bad to ignore this idk
-autoapi
-
-# notebooks in the docs folder
-# this is to enable the madness with adding gifs
-docs/examples/tidbits/*.ipynb
-docs/examples/*.ipynb
-docs/examples/*.gif
-docs/*.ipynb
-docs/gallery
-
 # Test output
 animation-*.gif
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index fe0ebf8e..5a101108 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -9,6 +9,17 @@ repos:
     hooks:
       - id: isort
 
+  - repo: https://github.com/nbQA-dev/nbQA
+    rev: 1.0.0
+    hooks:
+      - id: nbqa-black
+      - id: nbqa-isort
+
+  - repo: https://github.com/kynan/nbstripout
+    rev: 0.5.0
+    hooks:
+      - id: nbstripout
+
   - repo: https://github.com/pre-commit/mirrors-prettier
     rev: v2.3.2
     hooks:
diff --git a/MANIFEST.in b/MANIFEST.in
index 58a5590f..16f20ee7 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -16,7 +16,7 @@ prune docs/dist
 prune docs/docs
 
 # Examples
-graft examples
+graft docs/examples
 
 # Tests
 graft tests
diff --git a/README.md b/README.md
index 8c5714e4..b176284c 100644
--- a/README.md
+++ b/README.md
@@ -39,7 +39,7 @@ I use the GitHub [issues](https://github.com/ianhi/mpl-interactions/issues) to k
 
 ## Documentation
 
-The fuller narrative documentation can be found on [ReadTheDocs](https://mpl-interactions.readthedocs.io/en/latest/). You may also find it helpful to check out the [examples directory](https://github.com/ianhi/mpl-interactions/tree/master/examples).
+The fuller narrative documentation can be found on [ReadTheDocs](https://mpl-interactions.readthedocs.io/en/latest/). You may also find it helpful to check out the [examples directory](https://github.com/ianhi/mpl-interactions/tree/master/docs/examples).
 
 ## Contributors ✨
 
diff --git a/docs/.gitignore b/docs/.gitignore
new file mode 100644
index 00000000..70cd29a4
--- /dev/null
+++ b/docs/.gitignore
@@ -0,0 +1,4 @@
+/api/
+/examples/*.gif
+/gallery/
+_build/
diff --git a/docs/Contributing.md b/docs/Contributing.md
deleted file mode 100644
index 5e4c4716..00000000
--- a/docs/Contributing.md
+++ /dev/null
@@ -1,86 +0,0 @@
-# Contributing
-
-Thanks for thinking of a way to help improve this library! Remember that contributions come in all shapes and sizes beyond writing bug fixes. Contributing to documentation, opening new [issues](https://github.com/ianhi/mpl-interactions/issues) for bugs, asking for clarification on things you find unclear, and requesting new features, are all super valuable contributions.
-
-## Code Improvements
-
-All development for this library happens on Github at [mpl_interactions](https://github.com/ianhi/mpl-interactions).
-
-```bash
-git clone <your fork>
-cd mpl-interactions
-pip install -e ".[dev, doc, test, jupyter]"
-pre-commit install
-```
-
-```bash
-conda install nodejs=12
-jupyter labextension install @jupyter-widgets/jupyterlab-manager
-```
-
-### Seeing your changes
-
-If you are working in a Jupyter Notebook, then in order to see your code changes you will need to either:
-
-- Restart the Kernel every time you make a change to the code.
-- **Or:** Make the function reload from the source file every time you run it by using [autoreload](https://ipython.readthedocs.io/en/stable/config/extensions/autoreload.html).
-
-```python
-%load_ext autoreload
-%autoreload 2
-
-from mpl_interactions import ....
-```
-
-### Working with Git
-
-Using Git/Github can confusing (<https://xkcd.com/1597/>) so if you're new to Git, you may find it helpful to use a program like [Github Desktop](https://desktop.github.com) and to follow a [guide](https://github.com/firstcontributions/first-contributions#first-contributions).
-
-Also feel free to ask for help/advice on the relevant Github [issue](https://github.com/ianhi/mpl-interactions/issues).
-
-## Documentation
-
-For the most part the Documentation is generated from the Notebooks in the `examples` folder. So to write new examples work in a notebook in the `examples` directory. When the docs are generated then they will be converted to html via [myst-nb](https://myst-nb.readthedocs.io).
-
-If you are adding a new example then you need to include it in the toctree in `docs/index.md`.
-
-Following changes to the source files, you can view recent adjustments by building the documentation.
-
-1. Make sure you have installed the requirements for building the documentation:
-
-```bash
-cd mpl-interactions
-pip install -e ".[doc, jupyter]"
-```
-
-2. Run the following commands:
-
-```bash
-cd docs
-make html
-```
-
-If you open the `index.html` file in your browser you should now be able to see the rendered documentation.
-
-### Embedding GIFs
-
-Unfortunately the interactive plots are not interactive on readthedocs, this is because they require an active Python kernel to work. So instead we short gifs or apngs of interacting with the plots and embed them. To accomplish this the GIFs are stored in `docs/_static/images` and they are automatically embedded when the notebooks are converted to html based on the file names in the cell metadata. For example here is the process of setting the metadata of a cell when using JupyterLab.
-
-```{image} _static/images/how-to-embed-gif.apng
-
-```
-
-### Autobuild the documentation
-
-Alternatively, you can use [sphinx-autobuild](https://github.com/GaretJax/sphinx-autobuild) to continuously watch the documentation for changes and rebuild it for you. Sphinx-autobuild will be installed automatically by the above `pip` command, and we've added it to the `Makefile`. All you need to do is:
-
-```bash
-cd docs
-make watch
-```
-
-In a few seconds your web browser should open up the documentation. Now whenever you save a file the documentation will automatically regenerate and the webpage will refresh for you!
-
-## Thank you to our current team!
-
-This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributors members can be found on `mpl_interactions`' [README](https://github.com/ianhi/mpl-interactions#contributors-) page.
diff --git a/docs/Makefile b/docs/Makefile
index aee27949..9569fcb5 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -25,4 +25,4 @@ help:
 	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
 
 watch:
-	sphinx-autobuild . _build/html --open-browser --watch ../examples
+	sphinx-autobuild . _build/html --open-browser --watch examples
diff --git a/examples/freq-plot-1.gif b/docs/_static/images/freq-plot-1.gif
similarity index 100%
rename from examples/freq-plot-1.gif
rename to docs/_static/images/freq-plot-1.gif
diff --git a/examples/freq-plot-2.gif b/docs/_static/images/freq-plot-2.gif
similarity index 100%
rename from examples/freq-plot-2.gif
rename to docs/_static/images/freq-plot-2.gif
diff --git a/docs/_static/images/how-to-embed-gif.apng b/docs/_static/images/how-to-embed-gif.apng
deleted file mode 100644
index b0a155fd..00000000
Binary files a/docs/_static/images/how-to-embed-gif.apng and /dev/null differ
diff --git a/docs/Backends.md b/docs/backends.md
similarity index 100%
rename from docs/Backends.md
rename to docs/backends.md
diff --git a/docs/compare-to-ipywidgets.md b/docs/comparison.md
similarity index 100%
rename from docs/compare-to-ipywidgets.md
rename to docs/comparison.md
diff --git a/docs/conf.py b/docs/conf.py
index a1ed49b8..e72aee60 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -17,16 +17,10 @@
 import subprocess
 import sys
 
-import sphinx_rtd_theme
-
 import mpl_interactions as mpl_inter
 
 sys.path.insert(0, os.path.abspath("../mpl_interactions"))
 sys.path.insert(0, os.path.abspath("."))
-from gifmaker import gogogo_all
-
-gogogo_all("../examples", "examples/")
-gogogo_all("../examples/tidbits", "examples/tidbits/")
 
 release = mpl_inter.__version__
 
@@ -40,8 +34,8 @@
 # The full version, including alpha/beta/rc tags
 
 # -- Generate API ------------------------------------------------------------
-api_folder_name = "autoapi"
-shutil.rmtree(api_folder_name, ignore_errors=True)
+api_folder_name = "api"
+shutil.rmtree(api_folder_name, ignore_errors=True)  # in case of new or renamed modules
 subprocess.call(
     " ".join(
         [
@@ -80,12 +74,15 @@
     "sphinx.ext.napoleon",
     "sphinx_copybutton",
     "sphinx_gallery.gen_gallery",
+    "sphinx_panels",
+    "sphinx_thebe",
+    "sphinx_togglebutton",
 ]
 
 from mpl_playback.scraper import matplotlib_scraper
 
 sphinx_gallery_conf = {
-    "examples_dirs": "../examples/gallery",  # path to your example scripts
+    "examples_dirs": "examples/gallery",  # path to your example scripts
     "gallery_dirs": "gallery",  # path to where to save gallery generated output
     "filename_pattern": "/.*",
     "ignore_pattern": "/_.*",  # https://www.debuggex.com/
@@ -124,6 +121,9 @@
     "python": ("https://docs.python.org/3", None),
 }
 
+# remove panels css to get wider main content
+panels_add_bootstrap_css = False
+
 # Settings for copybutton
 copybutton_prompt_is_regexp = True
 copybutton_prompt_text = r">>> |\.\.\. "  # doctest
@@ -133,7 +133,14 @@
 linkcheck_ignore = []
 
 # Settings for myst-nb
+execution_excludepatterns = [
+    "examples/devlop/*",
+]
+execution_timeout = -1
 jupyter_execute_notebooks = "off"
+if "EXECUTE_NB" in os.environ:
+    print("\033[93;1mWill run Jupyter notebooks!\033[0m")
+    jupyter_execute_notebooks = "force"
 
 # Settings for myst-parser
 myst_enable_extensions = [
@@ -147,11 +154,6 @@
     "myst.header",
 ]
 
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ["_static"]
-
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ["_templates"]
 
@@ -163,6 +165,8 @@
     ".DS_Store",
     "Thumbs.db",
     "_build",
+    "examples/devlop",
+    "examples/gallery",
     "examples/non-ipympl-backends.ipynb",
     "gallery/*.ipynb",
     "gallery/*.md5",
@@ -176,22 +180,36 @@
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
 #
-html_theme = "sphinx_rtd_theme"
-html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
-
+html_copy_source = True  # needed for download notebook button
+html_css_files = [
+    "custom.css",
+]
+html_sourcelink_suffix = ""
+html_static_path = ["_static"]
+html_theme = "sphinx_book_theme"
 html_theme_options = {
-    # Toc options
-    "collapse_navigation": False,
-    "sticky_navigation": True,
-    "navigation_depth": 4,
+    "launch_buttons": {
+        "binderhub_url": "https://mybinder.org",
+        "colab_url": "https://colab.research.google.com",
+        "notebook_interface": "jupyterlab",
+        "thebe": True,
+        "thebelab": True,
+    },
+    "path_to_docs": "docs",
+    "repository_branch": "master",
+    "repository_url": "https://github.com/ianhi/mpl-interactions",
+    "use_download_button": True,
+    "use_edit_page_button": True,
+    "use_issues_button": True,
+    "use_repository_button": True,
 }
-
+html_title = "mpl-interactions"
 
 master_doc = "index"
-
-
-def setup(app):
-    app.add_css_file("custom.css")
+thebe_config = {
+    "repository_url": html_theme_options["repository_url"],
+    "repository_branch": html_theme_options["repository_branch"],
+}
 
 
 # based on pandas/doc/source/conf.py
diff --git a/docs/contributing.md b/docs/contributing.md
new file mode 100644
index 00000000..28cb0c24
--- /dev/null
+++ b/docs/contributing.md
@@ -0,0 +1,75 @@
+# Contributing
+
+Thanks for thinking of a way to help improve this library! Remember that contributions come in all shapes and sizes beyond writing bug fixes. Contributing to [documentation](#documentation), opening new [issues](https://github.com/ianhi/mpl-interactions/issues) for bugs, asking for clarification on things you find unclear, and requesting new features, are all super valuable contributions.
+
+## Code Improvements
+
+All development for this library happens on GitHub at [mpl_interactions](https://github.com/ianhi/mpl-interactions). We recommend you work with a [Conda](https://www.anaconda.com/products/individual) environment (or an alternative virtual environment like [`venv`](https://docs.python.org/3/library/venv.html)).
+
+```bash
+git clone <your fork>
+cd mpl-interactions
+conda env create
+conda activate mpl-interactions
+pre-commit install
+```
+
+The `conda env create` command installs all Python packages that are useful when working on the source code of `mpl_interactions` and its documentation. You can also install these packages separately:
+
+```bash
+pip install -e .[dev]
+```
+
+The {command}`-e .` flag installs the `mpl_interactions` folder in ["editable" mode](https://pip.pypa.io/en/stable/cli/pip_install/#editable-installs) and {command}`[dev]` installs the [optional dependencies](https://setuptools.readthedocs.io/en/latest/userguide/dependency_management.html#optional-dependencies) you need for developing `mpl_interacions`.
+
+### Seeing your changes
+
+If you are working in a Jupyter Notebook, then in order to see your code changes you will need to either:
+
+- Restart the Kernel every time you make a change to the code.
+- Make the function reload from the source file every time you run it by using [autoreload](https://ipython.readthedocs.io/en/stable/config/extensions/autoreload.html), e.g.:
+
+  ```python
+  %load_ext autoreload
+  %autoreload 2
+
+  from mpl_interactions import ....
+  ```
+
+### Working with Git
+
+Using Git/GitHub can confusing (<https://xkcd.com/1597>), so if you're new to Git, you may find it helpful to use a program like [GitHub Desktop](https://desktop.github.com) and to follow a [guide](https://github.com/firstcontributions/first-contributions#first-contributions).
+
+Also feel free to ask for help/advice on the relevant GitHub [issue](https://github.com/ianhi/mpl-interactions/issues).
+
+## Documentation
+
+Our documentation on Read the Docs ([mpl-interactions.rtfd.io](https://mpl-interactions.readthedocs.io)) is built with [Sphinx](https://www.sphinx-doc.org) from the notebooks in the `docs` folder. It contains both Markdown files and Jupyter notebooks.
+
+Examples are best written as Jupyter notebooks. To write a new example, create in a notebook in the `docs/examples` directory and list its path under one of the [`toctree`s](https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html#directive-toctree) in the `index.md` file. When the docs are generated, they will be rendered as static html pages by [myst-nb](https://myst-nb.readthedocs.io).
+
+If you have installed all developer dependencies (see [above](#contributing)), you can view recent modifications to the source files the following simple tox command:
+
+```bash
+tox -e doc
+```
+
+If you open the `index.html` file in your browser you should now be able to see the rendered documentation.
+
+Alternatively, you can use [sphinx-autobuild](https://github.com/executablebooks/sphinx-autobuild) to continuously watch source files for changes and rebuild the documentation for you. Sphinx-autobuild will be installed automatically by the above `pip` command, so all you need to do is run:
+
+```bash
+tox -e doclive
+```
+
+In a few seconds your web browser should open up the documentation. Now whenever you save a file the documentation will automatically regenerate and the webpage will refresh for you!
+
+### Embedding GIFs
+
+Unfortunately, interactive plots are not interactive on [Read the Docs](https://readthedocs.org/projects/mpl-interactions), because widgets require an active Python kernel to work. Instead, we therefore shoot [gif](https://docs.fileformat.com/image/gif) or [apng](https://docs.fileformat.com/image/apng) screencasts of interacting with the plots and embed them in the Jupyter notebooks.
+
+To accomplish, these GIFs are best stored in `docs/_static/images`. Then, below the input cell of which you want to render cell output, you create a Markdown cell below that code cell and insert the GIF there [as an image](https://www.markdownguide.org/basic-syntax/#images).
+
+## Thank you to our current team!
+
+This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributors members can be found on `mpl_interactions`' [README](https://github.com/ianhi/mpl-interactions#contributors-) page.
diff --git a/examples/ada-mask.npy b/docs/examples/ada-mask.npy
similarity index 100%
rename from examples/ada-mask.npy
rename to docs/examples/ada-mask.npy
diff --git a/examples/animations.ipynb b/docs/examples/animations.ipynb
similarity index 94%
rename from examples/animations.ipynb
rename to docs/examples/animations.ipynb
index 1642100e..d2f3e644 100644
--- a/examples/animations.ipynb
+++ b/docs/examples/animations.ipynb
@@ -46,7 +46,7 @@
     "\n",
     "# Create the plot as normal\n",
     "fig, ax = plt.subplots()\n",
-    "controls = iplt.plot(x, f, freq=(0.05, 10, 250), amp=(1,10))\n",
+    "controls = iplt.plot(x, f, freq=(0.05, 10, 250), amp=(1, 10))\n",
     "_ = iplt.title(\"the Frequency is: {freq:.2f}\", controls=controls[\"freq\"])"
    ]
   },
@@ -66,7 +66,7 @@
    "source": [
     "### Which Generates this GIF\n",
     "\n",
-    "![gif of sin animated over frequency](freq-plot-1.gif)"
+    "![](../_static/images/freq-plot-1.gif)"
    ]
   },
   {
@@ -94,7 +94,11 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {},
+   "metadata": {
+    "tags": [
+     "remove-output"
+    ]
+   },
    "outputs": [],
    "source": [
     "# NBVAL_IGNORE_OUTPUT\n",
@@ -153,15 +157,8 @@
    "source": [
     "### Gives this GIF:\n",
     "\n",
-    "![freq-plot-2.gif](freq-plot-2.gif)"
+    "![](../_static/images/freq-plot-2.gif)"
    ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": []
   }
  ],
  "metadata": {
diff --git a/examples/custom-callbacks.ipynb b/docs/examples/custom-callbacks.ipynb
similarity index 98%
rename from examples/custom-callbacks.ipynb
rename to docs/examples/custom-callbacks.ipynb
index 04f5ac4e..d85f20fe 100644
--- a/examples/custom-callbacks.ipynb
+++ b/docs/examples/custom-callbacks.ipynb
@@ -56,6 +56,7 @@
     "# if running from a script you can just delete the widgets.Output and associated code\n",
     "\n",
     "import ipywidgets as widgets\n",
+    "\n",
     "out = widgets.Output()\n",
     "display(out)\n",
     "\n",
@@ -180,13 +181,6 @@
     "\n",
     "After manually tuning the sliders you may want to access the values you have chosen permanently for usage in other parts of you code. The current parameter values are stored in a dictionary in the controls object that can be accessed by {attr}`.Controls.params`."
    ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": []
   }
  ],
  "metadata": {
diff --git a/examples/data/stock-metadata.pickle b/docs/examples/data/stock-metadata.pickle
similarity index 100%
rename from examples/data/stock-metadata.pickle
rename to docs/examples/data/stock-metadata.pickle
diff --git a/examples/data/stock-prices.npz b/docs/examples/data/stock-prices.npz
similarity index 100%
rename from examples/data/stock-prices.npz
rename to docs/examples/data/stock-prices.npz
diff --git a/examples/devlop/README.md b/docs/examples/devlop/README.md
similarity index 100%
rename from examples/devlop/README.md
rename to docs/examples/devlop/README.md
diff --git a/docs/examples/devlop/devlop-axline.ipynb b/docs/examples/devlop/devlop-axline.ipynb
new file mode 100644
index 00000000..77472997
--- /dev/null
+++ b/docs/examples/devlop/devlop-axline.ipynb
@@ -0,0 +1,278 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%matplotlib ipympl\n",
+    "import ipywidgets as widgets\n",
+    "import matplotlib.pyplot as plt\n",
+    "import numpy as np\n",
+    "\n",
+    "%load_ext autoreload\n",
+    "%autoreload 2\n",
+    "from mpl_interactions import *"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "fig, ax = plt.subplots()\n",
+    "\n",
+    "\n",
+    "def y(heck):\n",
+    "    return heck\n",
+    "\n",
+    "\n",
+    "def xmin(heck):\n",
+    "    return heck\n",
+    "\n",
+    "\n",
+    "# controls = interactive_axhline(y,0,1, heck=(0,1))\n",
+    "controls = interactive_axhline(y, xmin, 1, heck=(0, 1))\n",
+    "# ret.set_ydata([.5,1])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "plt.axhline()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def interactive_axhline(\n",
+    "    y=0,\n",
+    "    xmin=0,\n",
+    "    xmax=1,\n",
+    "    ax=None,\n",
+    "    slider_formats=None,\n",
+    "    title=None,\n",
+    "    force_ipywidgets=False,\n",
+    "    play_buttons=False,\n",
+    "    play_button_pos=\"right\",\n",
+    "    controls=None,\n",
+    "    display_controls=True,\n",
+    "    **kwargs,\n",
+    "):\n",
+    "    \"\"\"\n",
+    "    Control an horizontal line using widgets.\n",
+    "\n",
+    "    parameters\n",
+    "    ----------\n",
+    "    y : float or function\n",
+    "        y position in data coordinates of the horizontal line.\n",
+    "    xmin : float or function\n",
+    "        Should be between 0 and 1, 0 being the far left of the plot, 1 the\n",
+    "        far right of the plot.\n",
+    "    xmax : float or function\n",
+    "        Should be between 0 and 1, 0 being the far left of the plot, 1 the\n",
+    "        far right of the plot.\n",
+    "    ax : matplotlib axis, optional\n",
+    "        If None a new figure and axis will be created\n",
+    "    slider_formats : None, string, or dict\n",
+    "        If None a default value of decimal points will be used. Uses the new {} style formatting\n",
+    "    force_ipywidgets : boolean\n",
+    "        If True ipywidgets will always be used, even if not using the ipympl backend.\n",
+    "        If False the function will try to detect if it is ok to use ipywidgets\n",
+    "        If ipywidgets are not used the function will fall back on matplotlib widgets\n",
+    "    play_buttons : bool or dict, optional\n",
+    "        Whether to attach an ipywidgets.Play widget to any sliders that get created.\n",
+    "        If a boolean it will apply to all kwargs, if a dictionary you choose which sliders you\n",
+    "        want to attach play buttons too.\n",
+    "    play_button_pos : str, or dict, or list(str)\n",
+    "        'left' or 'right'. Whether to position the play widget(s) to the left or right of the slider(s)\n",
+    "    controls : mpl_interactions.controller.Controls\n",
+    "        An existing controls object if you want to tie multiple plot elements to the same set of\n",
+    "        controls\n",
+    "    display_controls : boolean\n",
+    "        Whether the controls should display on creation. Ignored if controls is specified.\n",
+    "\n",
+    "    returns\n",
+    "    -------\n",
+    "    controls\n",
+    "    \"\"\"\n",
+    "    ipympl = notebook_backend()\n",
+    "    fig, ax = gogogo_figure(ipympl, ax)\n",
+    "    use_ipywidgets = ipympl or force_ipywidgets\n",
+    "    slider_formats = create_slider_format_dict(slider_formats)\n",
+    "\n",
+    "    controls, params = gogogo_controls(\n",
+    "        kwargs, controls, display_controls, slider_formats, play_buttons, play_button_pos\n",
+    "    )\n",
+    "\n",
+    "    def update(params, indices, cache):\n",
+    "        if title is not None:\n",
+    "            ax.set_title(title.format(**params))\n",
+    "        y_ = callable_else_value(y, params, cache).item()\n",
+    "        line.set_ydata([y_, y_])\n",
+    "        xmin_ = callable_else_value(xmin, params, cache).item()\n",
+    "        xmax_ = callable_else_value(xmax, params, cache).item()\n",
+    "        line.set_xdata([xmin_, xmax_])\n",
+    "        # TODO consider updating just the ydatalim here\n",
+    "\n",
+    "    controls.register_function(update, fig, params)\n",
+    "    line = ax.axhline(\n",
+    "        callable_else_value(y, params).item(),\n",
+    "        callable_else_value(xmin, params).item(),\n",
+    "        callable_else_value(xmax, params).item(),\n",
+    "    )\n",
+    "    return controls\n",
+    "\n",
+    "\n",
+    "def interactive_axvline(\n",
+    "    x=0,\n",
+    "    ymin=0,\n",
+    "    ymax=1,\n",
+    "    ax=None,\n",
+    "    slider_formats=None,\n",
+    "    title=None,\n",
+    "    force_ipywidgets=False,\n",
+    "    play_buttons=False,\n",
+    "    play_button_pos=\"right\",\n",
+    "    controls=None,\n",
+    "    display_controls=True,\n",
+    "    **kwargs,\n",
+    "):\n",
+    "    ipympl = notebook_backend()\n",
+    "    fig, ax = gogogo_figure(ipympl, ax)\n",
+    "    use_ipywidgets = ipympl or force_ipywidgets\n",
+    "    slider_formats = create_slider_format_dict(slider_formats)\n",
+    "\n",
+    "    controls, params = gogogo_controls(\n",
+    "        kwargs, controls, display_controls, slider_formats, play_buttons, play_button_pos\n",
+    "    )\n",
+    "\n",
+    "    def update(params, indices, cache):\n",
+    "        if title is not None:\n",
+    "            ax.set_title(title.format(**params))\n",
+    "        x_ = callable_else_value(x, params, cache).item()\n",
+    "        line.set_ydata([x_, x_])\n",
+    "        ymin_ = callable_else_value(ymin, params, cache).item()\n",
+    "        ymax_ = callable_else_value(ymax, params, cache).item()\n",
+    "        line.set_xdata([ymin_, ymax_])\n",
+    "        # TODO consider updating just the ydatalim here\n",
+    "\n",
+    "    controls.register_function(update, fig, params)\n",
+    "    line = ax.axvline(\n",
+    "        callable_else_value(x, params).item(),\n",
+    "        callable_else_value(ymin, params).item(),\n",
+    "        callable_else_value(ymax, params).item(),\n",
+    "    )\n",
+    "    return controls"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "ret.set_ydata([0.5, 1])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "ret = ax.axhline(0, np.array(0), 1)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "arr = np.array(0)\n",
+    "arr.item()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "np.asscalar(arr)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "ret.set_xdata([0, 1.5])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def axhline(y=0, xmin=0, xmax=1):\n",
+    "    "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "ret.get_ydata()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.8.5"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/devlop/devlop-base.ipynb b/docs/examples/devlop/devlop-base.ipynb
similarity index 78%
rename from examples/devlop/devlop-base.ipynb
rename to docs/examples/devlop/devlop-base.ipynb
index e8a30d4c..d9f9dc37 100644
--- a/examples/devlop/devlop-base.ipynb
+++ b/docs/examples/devlop/devlop-base.ipynb
@@ -7,21 +7,15 @@
    "outputs": [],
    "source": [
     "%matplotlib ipympl\n",
+    "import ipywidgets as widgets\n",
     "import matplotlib.pyplot as plt\n",
     "import numpy as np\n",
-    "import ipywidgets as widgets\n",
+    "\n",
     "%load_ext autoreload\n",
     "%autoreload 2\n",
-    "from mpl_interactions import *\n",
-    "import mpl_interactions.ipyplot as iplt"
+    "import mpl_interactions.ipyplot as iplt\n",
+    "from mpl_interactions import *"
    ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": []
   }
  ],
  "metadata": {
diff --git a/examples/devlop/devlop-controller.ipynb b/docs/examples/devlop/devlop-controller.ipynb
similarity index 84%
rename from examples/devlop/devlop-controller.ipynb
rename to docs/examples/devlop/devlop-controller.ipynb
index bb33ea7b..912f78c3 100644
--- a/examples/devlop/devlop-controller.ipynb
+++ b/docs/examples/devlop/devlop-controller.ipynb
@@ -25,15 +25,17 @@
    "outputs": [],
    "source": [
     "%matplotlib ipympl\n",
+    "import ipywidgets as widgets\n",
     "import matplotlib.pyplot as plt\n",
     "import numpy as np\n",
-    "import ipywidgets as widgets\n",
+    "\n",
     "%load_ext autoreload\n",
     "%autoreload 2\n",
-    "from mpl_interactions import *\n",
     "from collections import defaultdict\n",
+    "from collections.abc import Callable\n",
     "from functools import partial\n",
-    "from collections.abc import Callable"
+    "\n",
+    "from mpl_interactions import *"
    ]
   },
   {
@@ -43,29 +45,29 @@
    "outputs": [],
    "source": [
     "class Controls:\n",
-    "    def __init__(self, out=None, play_buttons=False, play_button_pos='right', **kwargs):\n",
+    "    def __init__(self, out=None, play_buttons=False, play_button_pos=\"right\", **kwargs):\n",
     "        # it might make sense to also accept kwargs as a straight up arg\n",
     "        # to allow for passing the dictionary, but then it would need a different name\n",
     "        # and we'd have to combine dicitonarys which looks like a hassle\n",
-    "        \n",
+    "\n",
     "        self.out = out\n",
-    "        self.kwargs = kwargs \n",
+    "        self.kwargs = kwargs\n",
     "        slider_format_string = None\n",
     "        self.slider_format_strings = create_slider_format_dict(slider_format_string, True)\n",
     "        self.controls = {}\n",
     "        self.params = {}\n",
-    "        self.figs = defaultdict(list) # maybe should only store weakrefs?\n",
+    "        self.figs = defaultdict(list)  # maybe should only store weakrefs?\n",
     "        self.indices = defaultdict(lambda: 0)\n",
     "        self._update_funcs = defaultdict(list)\n",
     "        self.controller_list = []\n",
     "        self.vbox = widgets.VBox([])\n",
     "        self.add_kwargs(kwargs, play_buttons, play_button_pos)\n",
     "\n",
-    "    def add_kwargs(self, kwargs, play_buttons=False, play_button_pos='right'):\n",
+    "    def add_kwargs(self, kwargs, play_buttons=False, play_button_pos=\"right\"):\n",
     "        \"\"\"\n",
     "        If you pass a redundant kwarg it will just be overwritten\n",
     "        maybe should only raise a warning rather than an error?\n",
-    "        \n",
+    "\n",
     "        need to implement matplotlib widgets\n",
     "        also a big question is how to dynamically update the display of matplotlib widgets.\n",
     "        \"\"\"\n",
@@ -86,29 +88,36 @@
     "            if k in self.params:\n",
     "                raise ValueError(\"can't overwrite an existing param in the controller\")\n",
     "            # create slider\n",
-    "            self.params[k], control = kwarg_to_ipywidget(k,v, partial(self.slider_updated, key=k), '{:.2f}',\n",
-    "                                                         play_button = has_play_button[k], play_button_pos=play_button_pos)\n",
+    "            self.params[k], control = kwarg_to_ipywidget(\n",
+    "                k,\n",
+    "                v,\n",
+    "                partial(self.slider_updated, key=k),\n",
+    "                \"{:.2f}\",\n",
+    "                play_button=has_play_button[k],\n",
+    "                play_button_pos=play_button_pos,\n",
+    "            )\n",
     "            if control:\n",
     "                self.controls[k] = control\n",
     "                self.indices\n",
-    "                self.vbox.children = list(self.vbox.children) + [control] \n",
-    "        \n",
+    "                self.vbox.children = list(self.vbox.children) + [control]\n",
+    "\n",
     "    def slider_updated(self, change, key, values):\n",
     "        \"\"\"\n",
     "        gotta also give the indices in order to support hyperslicer without horrifying contortions\n",
     "        \"\"\"\n",
     "        with self.out:\n",
-    "            self.params[key] = values[change['new']]\n",
-    "            self.indices[key] = change['new']\n",
+    "            self.params[key] = values[change[\"new\"]]\n",
+    "            self.indices[key] = change[\"new\"]\n",
     "            for f, params in self._update_funcs[key]:\n",
     "                ps = {}\n",
     "                idxs = {}\n",
     "                for k in params:\n",
     "                    ps[k] = self.params[k]\n",
     "                    idxs[k] = self.indices[k]\n",
-    "                f(params = ps, indices = idxs)\n",
+    "                f(params=ps, indices=idxs)\n",
     "            for f in self.figs[key]:\n",
     "                f.canvas.draw_idle()\n",
+    "\n",
     "    def register_function(self, f, fig, params=None):\n",
     "        \"\"\"\n",
     "        if params is None use the entire current set of params\n",
@@ -120,7 +129,7 @@
     "        params = list(params)\n",
     "        for p in params:\n",
     "            self._update_funcs[p].append((f, params))\n",
-    "            self.figs[p].append(fig) # maybe should use a weakref?\n",
+    "            self.figs[p].append(fig)  # maybe should use a weakref?\n",
     "\n",
     "    def __getitem__(self, key):\n",
     "        \"\"\"\n",
@@ -129,7 +138,7 @@
     "        also allows [None] to grab None of the current params\n",
     "        to imply that we only want tau from the existing set of commands\n",
     "        \"\"\"\n",
-    "        \n",
+    "\n",
     "        # make sure keys is a list\n",
     "        # bc in gogogo_controls it may get added to another list\n",
     "        if isinstance(key, str):\n",
@@ -137,9 +146,11 @@
     "        elif key is None:\n",
     "            key = []\n",
     "        return self, key\n",
-    "            \n",
+    "\n",
     "    def _ipython_display_(self):\n",
     "        display(self.vbox)\n",
+    "\n",
+    "\n",
     "#         display(widgets.VBox(list(self.controls.values())))\n",
     "# kwargs = {'param1':(0,1), 'param2':(1,10), 'tau':(0,2*np.pi)}\n",
     "# out = widgets.Output()\n",
@@ -158,7 +169,6 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "\n",
     "def gogogo_controls(kwargs, controls, display_controls, play_buttons, play_button_pos):\n",
     "    if controls:\n",
     "        if isinstance(controls, tuple):\n",
@@ -166,13 +176,15 @@
     "            extra_keys = controls[1]\n",
     "            controls = controls[0]\n",
     "            controls.add_kwargs(kwargs, play_buttons, play_button_pos)\n",
-    "            params = {k:controls.params[k] for k in list(kwargs.keys()) + list(extra_keys)}\n",
+    "            params = {k: controls.params[k] for k in list(kwargs.keys()) + list(extra_keys)}\n",
     "        else:\n",
     "            controls.add_kwargs(kwargs)\n",
     "            params = controls.params\n",
     "    else:\n",
     "        out = widgets.Output()\n",
-    "        controls = Controls(out,play_buttons=play_buttons, play_button_pos=play_button_pos, **kwargs)\n",
+    "        controls = Controls(\n",
+    "            out, play_buttons=play_buttons, play_button_pos=play_button_pos, **kwargs\n",
+    "        )\n",
     "        params = controls.params\n",
     "        if display_controls:\n",
     "            display(controls)\n",
@@ -185,8 +197,9 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "\n",
     "out = widgets.Output()\n",
+    "\n",
+    "\n",
     "def interactive_imshow(\n",
     "    X,\n",
     "    cmap=None,\n",
@@ -220,7 +233,10 @@
     "    use_ipywidgets = ipympl or force_ipywidgets\n",
     "    slider_format_strings = create_slider_format_dict(slider_format_string, use_ipywidgets)\n",
     "\n",
-    "    controls, params = gogogo_controls(kwargs, controls, display_controls, play_buttons, play_button_pos)\n",
+    "    controls, params = gogogo_controls(\n",
+    "        kwargs, controls, display_controls, play_buttons, play_button_pos\n",
+    "    )\n",
+    "\n",
     "    def update(params, indices):\n",
     "        if title is not None:\n",
     "            ax.set_title(title.format(**params))\n",
@@ -238,7 +254,7 @@
     "    controls.register_function(update, fig, params.keys())\n",
     "\n",
     "    # make it once here so we can use the dims in update\n",
-    "    new_data = callable_else_value(X, {k:controls.params[k] for k in kwargs})\n",
+    "    new_data = callable_else_value(X, {k: controls.params[k] for k in kwargs})\n",
     "    im = ax.imshow(\n",
     "        new_data,\n",
     "        cmap=cmap,\n",
@@ -268,7 +284,6 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "\n",
     "def interactive_plot2(\n",
     "    f,\n",
     "    x=None,\n",
@@ -290,7 +305,9 @@
     "    fig, ax = gogogo_figure(ipympl, ax=ax)\n",
     "    funcs = np.atleast_1d(f)\n",
     "    slider_format_strings = create_slider_format_dict(slider_format_string, use_ipywidgets)\n",
-    "    controls, params = gogogo_controls(kwargs, controls, display_controls, play_buttons, play_button_pos)\n",
+    "    controls, params = gogogo_controls(\n",
+    "        kwargs, controls, display_controls, play_buttons, play_button_pos\n",
+    "    )\n",
     "\n",
     "    def update(params, indices):\n",
     "\n",
@@ -380,8 +397,7 @@
     "    if hasattr(fig.canvas, \"toolbar\") and fig.canvas.toolbar is not None:\n",
     "        fig.canvas.toolbar.push_current()\n",
     "\n",
-    "    return controls\n",
-    "        "
+    "    return controls"
    ]
   },
   {
@@ -392,18 +408,28 @@
    "source": [
     "fig, ax = plt.subplots()\n",
     "\n",
-    "x = np.linspace(0,np.pi,100)\n",
-    "tau = np.linspace(.5, 10, 100)\n",
+    "x = np.linspace(0, np.pi, 100)\n",
+    "tau = np.linspace(0.5, 10, 100)\n",
     "beta = np.linspace(1, 10, 100)\n",
+    "\n",
+    "\n",
     "def f1(x, tau, beta):\n",
-    "    return np.sin(x*tau)*x*beta\n",
+    "    return np.sin(x * tau) * x * beta\n",
+    "\n",
+    "\n",
     "def f2(x, tau, beta):\n",
-    "    return np.sin(x*beta)*x*tau\n",
+    "    return np.sin(x * beta) * x * tau\n",
+    "\n",
+    "\n",
     "def f3(x, tau, beta):\n",
-    "    return 3*np.sin(x*beta)*x*tau\n",
-    "def f4(x, tau,beep):\n",
-    "    return 3*np.sin(x*beta)*x**beep\n",
-    "controls = interactive_plot2([f1, f2], x=x, tau = tau, beta = beta, play_buttons=True)\n",
+    "    return 3 * np.sin(x * beta) * x * tau\n",
+    "\n",
+    "\n",
+    "def f4(x, tau, beep):\n",
+    "    return 3 * np.sin(x * beta) * x ** beep\n",
+    "\n",
+    "\n",
+    "controls = interactive_plot2([f1, f2], x=x, tau=tau, beta=beta, play_buttons=True)\n",
     "heck = interactive_plot2(f3, x=x, controls=controls)\n",
     "# display(controls)\n",
     "controls.out"
@@ -415,7 +441,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "heck = interactive_plot2(f4, x=x, beep = (0,1), controls=controls['tau'])\n"
+    "heck = interactive_plot2(f4, x=x, beep=(0, 1), controls=controls[\"tau\"])"
    ]
   },
   {
@@ -424,14 +450,17 @@
    "metadata": {},
    "outputs": [],
    "source": [
+    "x = np.linspace(0, np.pi, 200)\n",
+    "y = np.linspace(0, 10, 200)\n",
+    "X, Y = np.meshgrid(x, y)\n",
+    "\n",
     "\n",
-    "x = np.linspace(0,np.pi,200)\n",
-    "y = np.linspace(0,10,200)\n",
-    "X, Y = np.meshgrid(x,y)\n",
     "def f(param1, param2):\n",
-    "    return np.sin(X)*param2 + np.exp(np.cos(Y*param1)) + param2\n",
+    "    return np.sin(X) * param2 + np.exp(np.cos(Y * param1)) + param2\n",
+    "\n",
+    "\n",
     "fig2, ax = plt.subplots()\n",
-    "controls = interactive_imshow(f, param1 = (-5,5), param2 = (-3,12), controls = controls[None])"
+    "controls = interactive_imshow(f, param1=(-5, 5), param2=(-3, 12), controls=controls[None])"
    ]
   },
   {
@@ -440,17 +469,12 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "fig, (ax1, ax2) = plt.subplots(1,2)\n",
-    "controls = interactive_plot2([f1, f2], x=x, tau = tau, beta = beta, ax = ax1)\n",
-    "controls = interactive_imshow(f, param1 = (-5,5), param2 = (-3,12), controls = controls[None], ax=ax2, play_buttons=True)"
+    "fig, (ax1, ax2) = plt.subplots(1, 2)\n",
+    "controls = interactive_plot2([f1, f2], x=x, tau=tau, beta=beta, ax=ax1)\n",
+    "controls = interactive_imshow(\n",
+    "    f, param1=(-5, 5), param2=(-3, 12), controls=controls[None], ax=ax2, play_buttons=True\n",
+    ")"
    ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": []
   }
  ],
  "metadata": {
diff --git a/examples/devlop/devlop-imshow.ipynb b/docs/examples/devlop/devlop-imshow.ipynb
similarity index 79%
rename from examples/devlop/devlop-imshow.ipynb
rename to docs/examples/devlop/devlop-imshow.ipynb
index 9f8311ac..c155d3c6 100644
--- a/examples/devlop/devlop-imshow.ipynb
+++ b/docs/examples/devlop/devlop-imshow.ipynb
@@ -6,11 +6,11 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "\n",
     "%matplotlib ipympl\n",
+    "import ipywidgets as widgets\n",
     "import matplotlib.pyplot as plt\n",
     "import numpy as np\n",
-    "import ipywidgets as widgets\n",
+    "\n",
     "%load_ext autoreload\n",
     "%autoreload 2\n",
     "from mpl_interactions import *"
@@ -22,21 +22,24 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "\n",
     "import numpy as np\n",
-    "parameter1 = np.linspace(-5,5)\n",
-    "parameter2 = np.linspace(-5,5, 25)\n",
-    "x = np.linspace(0,np.pi,200)\n",
-    "y = np.linspace(0,10,200)\n",
-    "X,Y = np.meshgrid(x,y)\n",
-    "image_stack1 = np.sin(X)[None,:,:]+np.exp(np.cos(Y[None,:,:]*parameter1[:,None,None]))\n",
+    "\n",
+    "parameter1 = np.linspace(-5, 5)\n",
+    "parameter2 = np.linspace(-5, 5, 25)\n",
+    "x = np.linspace(0, np.pi, 200)\n",
+    "y = np.linspace(0, 10, 200)\n",
+    "X, Y = np.meshgrid(x, y)\n",
+    "image_stack1 = np.sin(X)[None, :, :] + np.exp(np.cos(Y[None, :, :] * parameter1[:, None, None]))\n",
     "print(f\"image_stack1.shape : {image_stack1.shape}\")\n",
     "# image_stack1.shape : (50, 200, 200)\n",
     "\n",
     "# create another image stack with shape (50, 25, 200, 200)\n",
-    "image_stack2 = np.sin(X)[None,None,:,:]*parameter2[:,None,None] + np.exp(np.cos(Y[None,:,:]*parameter1[:,None,None]))[:,None, ...]\n",
+    "image_stack2 = (\n",
+    "    np.sin(X)[None, None, :, :] * parameter2[:, None, None]\n",
+    "    + np.exp(np.cos(Y[None, :, :] * parameter1[:, None, None]))[:, None, ...]\n",
+    ")\n",
     "print(f\"image_stack2.shape : {image_stack2.shape}\")\n",
-    "# image_stack2.shape : (50, 25, 200, 200)\n"
+    "# image_stack2.shape : (50, 25, 200, 200)"
    ]
   },
   {
@@ -55,7 +58,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "im.norm.vmin=4"
+    "im.norm.vmin = 4"
    ]
   },
   {
@@ -97,7 +100,9 @@
    "source": [
     "def f(i):\n",
     "    return image_stack1[i] + i\n",
-    "fig, ax, controls = interactive_imshow(f, i = np.arange(0,50),cmap='plasma')"
+    "\n",
+    "\n",
+    "fig, ax, controls = interactive_imshow(f, i=np.arange(0, 50), cmap=\"plasma\")"
    ]
   },
   {
@@ -127,13 +132,6 @@
    "source": [
     "im.norm._stale = True"
    ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": []
   }
  ],
  "metadata": {
diff --git a/examples/devlop/devlop-scatter.ipynb b/docs/examples/devlop/devlop-scatter.ipynb
similarity index 68%
rename from examples/devlop/devlop-scatter.ipynb
rename to docs/examples/devlop/devlop-scatter.ipynb
index 8c388488..e7e04379 100644
--- a/examples/devlop/devlop-scatter.ipynb
+++ b/docs/examples/devlop/devlop-scatter.ipynb
@@ -14,9 +14,10 @@
    "outputs": [],
    "source": [
     "%matplotlib ipympl\n",
+    "import ipywidgets as widgets\n",
     "import matplotlib.pyplot as plt\n",
     "import numpy as np\n",
-    "import ipywidgets as widgets\n",
+    "\n",
     "%load_ext autoreload\n",
     "%autoreload 2\n",
     "from mpl_interactions import *"
@@ -36,28 +37,21 @@
     "x = np.random.rand(N)\n",
     "y = np.random.rand(N)\n",
     "colors = np.random.rand(N)\n",
-    "area = (30 * np.random.rand(N))**2  # 0 to 15 point radii\n",
+    "area = (30 * np.random.rand(N)) ** 2  # 0 to 15 point radii\n",
     "\n",
-    "scat = plt.scatter(x, y, s=area, c=colors, alpha=0.5,label='yikes',cmap='viridis')\n",
+    "scat = plt.scatter(x, y, s=area, c=colors, alpha=0.5, label=\"yikes\", cmap=\"viridis\")\n",
     "plt.legend()\n",
-    "plt.show()\n"
+    "plt.show()"
    ]
   },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": []
-  },
   {
    "cell_type": "code",
    "execution_count": null,
    "metadata": {},
    "outputs": [],
    "source": [
-    "x_new = np.random.randn(N+1000)\n",
-    "y_new =  np.random.randn(N+1000)\n",
+    "x_new = np.random.randn(N + 1000)\n",
+    "y_new = np.random.randn(N + 1000)\n",
     "\n",
     "new = np.array([x_new, y_new]).T\n",
     "scat.set_offsets(new)"
@@ -73,14 +67,14 @@
     "    \"\"\"\n",
     "    should be able to return either:\n",
     "    x, y\n",
-    "    \n",
+    "\n",
     "    or arr where arr.shape = (N, 2 )\n",
     "    I should check that\n",
     "    \"\"\"\n",
     "    print(mean)\n",
     "    x = np.random.rand(N) * mean\n",
     "    y = np.random.rand(N) * mean\n",
-    "    return x, y  "
+    "    return x, y"
    ]
   },
   {
@@ -89,7 +83,6 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "\n",
     "fig, ax = plt.subplots()\n",
     "zoom_factory(ax)\n",
     "ph = panhandler(fig)\n",
@@ -97,9 +90,9 @@
     "x = np.random.rand(N)\n",
     "y = np.random.rand(N)\n",
     "colors = np.random.rand(N)\n",
-    "area = (30 * np.random.rand(N))**2  # 0 to 15 point radii\n",
+    "area = (30 * np.random.rand(N)) ** 2  # 0 to 15 point radii\n",
     "\n",
-    "scat = plt.scatter(x, y, s=area, c=colors, alpha=0.5,label='yikes')\n",
+    "scat = plt.scatter(x, y, s=area, c=colors, alpha=0.5, label=\"yikes\")\n",
     "plt.legend()\n",
     "plt.show()"
    ]
@@ -110,25 +103,29 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "slider = widgets.FloatSlider(min=-.5,max = 1.5,step=.01)\n",
-    "ax.plot([-10,10],[0,10])\n",
+    "slider = widgets.FloatSlider(min=-0.5, max=1.5, step=0.01)\n",
+    "ax.plot([-10, 10], [0, 10])\n",
+    "\n",
+    "\n",
     "def update(change):\n",
-    "#     print(change)\n",
-    "    out = f(change['new'])\n",
-    "    \n",
+    "    #     print(change)\n",
+    "    out = f(change[\"new\"])\n",
+    "\n",
     "    out = np.asanyarray(out)\n",
     "    if out.shape[0] == 2 and out.shape[1] != 2:\n",
     "        # check if transpose is necessary\n",
     "        # but not way to check if shape is 2x2\n",
     "        out = out.T\n",
-    "#     print(out.shape)\n",
+    "    #     print(out.shape)\n",
     "    scat.set_offsets(out)\n",
-    "#     ax.ignore_existing_data_limits = True\n",
+    "    #     ax.ignore_existing_data_limits = True\n",
     "    ax.update_datalim(scat.get_datalim(ax.transData))\n",
     "    ax.autoscale_view()\n",
     "\n",
     "    fig.canvas.draw()\n",
-    "slider.observe(update, names=['value'])\n",
+    "\n",
+    "\n",
+    "slider.observe(update, names=[\"value\"])\n",
     "slider"
    ]
   },
@@ -138,23 +135,25 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "from ipywidgets import widgets\n",
-    "from IPython.display import display\n",
     "import matplotlib.pyplot as plt\n",
     "import numpy as np\n",
+    "from IPython.display import display\n",
+    "from ipywidgets import widgets\n",
     "\n",
     "x = np.arange(10)\n",
     "\n",
     "fig, ax = plt.subplots()\n",
-    "scatter = ax.scatter(x,x, label=\"y = a*x+b\")\n",
+    "scatter = ax.scatter(x, x, label=\"y = a*x+b\")\n",
     "\n",
     "ax.legend()\n",
     "\n",
-    "line = ax.plot([-10,10],[0,1])[0]\n",
+    "line = ax.plot([-10, 10], [0, 1])[0]\n",
+    "\n",
+    "\n",
     "def update_plot(a, b):\n",
-    "    y = a*x+b\n",
-    "    scatter.set_offsets(np.c_[x,y])\n",
-    "    line.set_data(x-3,y)\n",
+    "    y = a * x + b\n",
+    "    scatter.set_offsets(np.c_[x, y])\n",
+    "    line.set_data(x - 3, y)\n",
     "\n",
     "    ax.relim()\n",
     "    ax.ignore_existing_data_limits = True\n",
@@ -163,8 +162,9 @@
     "\n",
     "    fig.canvas.draw_idle()\n",
     "\n",
-    "a = widgets.FloatSlider(min=0.5, max=4, value=1, description= 'a:')\n",
-    "b = widgets.FloatSlider(min=0, max=40, value=10, description= 'b:')\n",
+    "\n",
+    "a = widgets.FloatSlider(min=0.5, max=4, value=1, description=\"a:\")\n",
+    "b = widgets.FloatSlider(min=0, max=40, value=10, description=\"b:\")\n",
     "\n",
     "widgets.interactive(update_plot, a=a, b=b)"
    ]
@@ -176,15 +176,21 @@
    "outputs": [],
    "source": [
     "N = 50\n",
+    "\n",
+    "\n",
     "def f(mean):\n",
     "    x = np.random.rand(N) + mean\n",
     "    y = np.random.rand(N) + mean\n",
-    "    return x, y  \n",
+    "    return x, y\n",
+    "\n",
+    "\n",
     "def f2(mean):\n",
     "    x = np.random.rand(N) - mean\n",
     "    y = np.random.rand(N) - mean\n",
-    "    return x, y  \n",
-    "blarg = interactive_scatter([f,f2], mean = (0, 1, 100), c = [np.random.randn(N),np.random.randn(N)])"
+    "    return x, y\n",
+    "\n",
+    "\n",
+    "blarg = interactive_scatter([f, f2], mean=(0, 1, 100), c=[np.random.randn(N), np.random.randn(N)])"
    ]
   },
   {
@@ -194,29 +200,32 @@
    "outputs": [],
    "source": [
     "N = 50\n",
+    "\n",
+    "\n",
     "def f(mean):\n",
-    "    x = np.random.rand(N) + mean - .5\n",
-    "    y = np.random.rand(N) + mean - .5\n",
-    "    return x, y  \n",
-    "def c_func(x,y, mean):\n",
+    "    x = np.random.rand(N) + mean - 0.5\n",
+    "    y = np.random.rand(N) + mean - 0.5\n",
+    "    return x, y\n",
+    "\n",
+    "\n",
+    "def c_func(x, y, mean):\n",
     "    return x\n",
-    "def s_func(x,y, mean):\n",
-    "    return 40/x\n",
-    "def ec_func(x,y,mean):\n",
-    "    if np.random.rand() >.5:\n",
-    "        return 'black'\n",
+    "\n",
+    "\n",
+    "def s_func(x, y, mean):\n",
+    "    return 40 / x\n",
+    "\n",
+    "\n",
+    "def ec_func(x, y, mean):\n",
+    "    if np.random.rand() > 0.5:\n",
+    "        return \"black\"\n",
     "    else:\n",
-    "        return 'red'\n",
-    "blarg = interactive_scatter(f, mean = (0, 1, 100), c= c_func, s=s_func, alpha=.9, edgecolors=ec_func)"
+    "        return \"red\"\n",
+    "\n",
+    "\n",
+    "blarg = interactive_scatter(f, mean=(0, 1, 100), c=c_func, s=s_func, alpha=0.9, edgecolors=ec_func)"
    ]
   },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": []
-  },
   {
    "cell_type": "code",
    "execution_count": null,
@@ -225,7 +234,11 @@
    "source": [
     "def alpha_func(mean):\n",
     "    return mean / 1\n",
-    "blarg2 = interactive_scatter((x,y), mean = (0, 1, 100), c= c_func, s=s_func, alpha=alpha_func, edgecolors=ec_func)"
+    "\n",
+    "\n",
+    "blarg2 = interactive_scatter(\n",
+    "    (x, y), mean=(0, 1, 100), c=c_func, s=s_func, alpha=alpha_func, edgecolors=ec_func\n",
+    ")"
    ]
   },
   {
@@ -235,31 +248,31 @@
    "outputs": [],
    "source": [
     "N = 500\n",
+    "\n",
+    "\n",
     "def f(mean):\n",
-    "    x = (np.random.rand(N)-.5) + mean\n",
-    "    y = 10*(np.random.rand(N)-.5) + mean\n",
-    "    return x, y  \n",
-    "(x,y) = f(.5)\n",
-    "def threshold(x,y,mean):\n",
+    "    x = (np.random.rand(N) - 0.5) + mean\n",
+    "    y = 10 * (np.random.rand(N) - 0.5) + mean\n",
+    "    return x, y\n",
+    "\n",
+    "\n",
+    "(x, y) = f(0.5)\n",
+    "\n",
+    "\n",
+    "def threshold(x, y, mean):\n",
     "    colors = np.zeros((len(x), 4))\n",
-    "    colors[:,-1] = 1\n",
+    "    colors[:, -1] = 1\n",
     "    deltas = np.abs(y - mean)\n",
-    "    idx = deltas < .01\n",
+    "    idx = deltas < 0.01\n",
     "    deltas /= deltas.max()\n",
-    "    colors[~idx, -1] = np.clip(.8-deltas[~idx],0,1)\n",
-    "#     print(colors)\n",
+    "    colors[~idx, -1] = np.clip(0.8 - deltas[~idx], 0, 1)\n",
+    "    #     print(colors)\n",
     "    return colors\n",
     "\n",
-    "blarg2 = interactive_scatter((x,y), mean = (0, 1, 100), c= threshold)"
+    "\n",
+    "blarg2 = interactive_scatter((x, y), mean=(0, 1, 100), c=threshold)"
    ]
   },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": []
-  },
   {
    "cell_type": "code",
    "execution_count": null,
@@ -268,9 +281,11 @@
    "source": [
     "from inspect import signature\n",
     "\n",
+    "\n",
     "def someMethod(arg1, kwarg1=None):\n",
     "    pass\n",
     "\n",
+    "\n",
     "sig = signature(someMethod)"
    ]
   },
@@ -290,7 +305,8 @@
    "outputs": [],
    "source": [
     "from matplotlib.colors import is_color_like\n",
-    "is_color_like(threshold(x,y,4)[0])"
+    "\n",
+    "is_color_like(threshold(x, y, 4)[0])"
    ]
   },
   {
@@ -299,7 +315,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "scats.setscat.cmap([[0],[1],[23]]).shape"
+    "scats.setscat.cmap([[0], [1], [23]]).shape"
    ]
   },
   {
@@ -326,15 +342,8 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "mcolors.to_rgba_array('red')"
+    "mcolors.to_rgba_array(\"red\")"
    ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": []
   }
  ],
  "metadata": {
diff --git a/examples/embed_heatmap_slicer_tk.py b/docs/examples/embed_heatmap_slicer_tk.py
similarity index 100%
rename from examples/embed_heatmap_slicer_tk.py
rename to docs/examples/embed_heatmap_slicer_tk.py
diff --git a/examples/gallery/README.md b/docs/examples/gallery/README.md
similarity index 100%
rename from examples/gallery/README.md
rename to docs/examples/gallery/README.md
diff --git a/examples/gallery/_gen_images.py b/docs/examples/gallery/_gen_images.py
similarity index 100%
rename from examples/gallery/_gen_images.py
rename to docs/examples/gallery/_gen_images.py
diff --git a/examples/gallery/_heatmap_slicer-playback.json b/docs/examples/gallery/_heatmap_slicer-playback.json
similarity index 100%
rename from examples/gallery/_heatmap_slicer-playback.json
rename to docs/examples/gallery/_heatmap_slicer-playback.json
diff --git a/examples/gallery/_mpl-sliders-same-figure-playback.json b/docs/examples/gallery/_mpl-sliders-same-figure-playback.json
similarity index 100%
rename from examples/gallery/_mpl-sliders-same-figure-playback.json
rename to docs/examples/gallery/_mpl-sliders-same-figure-playback.json
diff --git a/examples/gallery/_playback.py b/docs/examples/gallery/_playback.py
similarity index 100%
rename from examples/gallery/_playback.py
rename to docs/examples/gallery/_playback.py
diff --git a/examples/gallery/heatmap_slicer.py b/docs/examples/gallery/heatmap_slicer.py
similarity index 100%
rename from examples/gallery/heatmap_slicer.py
rename to docs/examples/gallery/heatmap_slicer.py
diff --git a/examples/gallery/mpl-sliders-same-figure.py b/docs/examples/gallery/mpl-sliders-same-figure.py
similarity index 100%
rename from examples/gallery/mpl-sliders-same-figure.py
rename to docs/examples/gallery/mpl-sliders-same-figure.py
diff --git a/docs/examples/heatmap-slicer.ipynb b/docs/examples/heatmap-slicer.ipynb
new file mode 100644
index 00000000..486e1f6d
--- /dev/null
+++ b/docs/examples/heatmap-slicer.ipynb
@@ -0,0 +1,91 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Heatmap Slicer"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%matplotlib widget\n",
+    "import matplotlib.pyplot as plt\n",
+    "import numpy as np\n",
+    "\n",
+    "from mpl_interactions import heatmap_slicer"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Comparing heatmaps\n",
+    "\n",
+    "Sometimes I find myself wanting to compare horizontal or vertical slices across two different heatmaps with the same shape. The function {func}`.heatmap_slicer` makes this easy and should work for any number of heatmaps from 1 to many (likely not all the way $\\infty$ though). \n",
+    "\n",
+    "The most important options to play with are:\n",
+    "- `slices`: `\"both\"`, `\"vertical\"`, or `\"horizontal\"`\n",
+    "- `interaction_type`: `\"move\"` or `\"click\"`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "tags": [
+     "remove-output"
+    ]
+   },
+   "outputs": [],
+   "source": [
+    "x = np.linspace(0, np.pi, 100)\n",
+    "y = np.linspace(0, 10, 200)\n",
+    "X, Y = np.meshgrid(x, y)\n",
+    "data1 = np.sin(X) + np.exp(np.cos(Y))\n",
+    "data2 = np.cos(X) + np.exp(np.sin(Y))\n",
+    "fig, axes = heatmap_slicer(\n",
+    "    x,\n",
+    "    y,\n",
+    "    (data1, data2),\n",
+    "    slices=\"both\",\n",
+    "    heatmap_names=(\"dataset 1\", \"dataset 2\"),\n",
+    "    labels=(\"Some wild X variable\", \"Y axis\"),\n",
+    "    interaction_type=\"move\",\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/tight-layout-heatmap-slicer.gif)"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.7.8"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/hist.ipynb b/docs/examples/hist.ipynb
similarity index 91%
rename from examples/hist.ipynb
rename to docs/examples/hist.ipynb
index f8d328df..a26ca5f1 100644
--- a/examples/hist.ipynb
+++ b/docs/examples/hist.ipynb
@@ -73,17 +73,8 @@
    "outputs": [],
    "source": [
     "fig2, ax2 = plt.subplots()\n",
-    "controls2 = interactive_hist(\n",
-    "    f, loc=(0, 10, 100), scale=(0.5, 5), bins=100, density=True\n",
-    ")"
+    "controls2 = interactive_hist(f, loc=(0, 10, 100), scale=(0.5, 5), bins=100, density=True)"
    ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": []
   }
  ],
  "metadata": {
diff --git a/examples/hyperslicer.ipynb b/docs/examples/hyperslicer.ipynb
similarity index 85%
rename from examples/hyperslicer.ipynb
rename to docs/examples/hyperslicer.ipynb
index 2f847148..766ff86d 100644
--- a/examples/hyperslicer.ipynb
+++ b/docs/examples/hyperslicer.ipynb
@@ -41,9 +41,7 @@
    "outputs": [],
    "source": [
     "# Get the dataset directly from github\n",
-    "response = requests.get(\n",
-    "    \"https://github.com/jrussell25/data-sharing/raw/master/srs_beads.npy\"\n",
-    ")\n",
+    "response = requests.get(\"https://github.com/jrussell25/data-sharing/raw/master/srs_beads.npy\")\n",
     "response.raise_for_status()\n",
     "beads = np.load(io.BytesIO(response.content))"
    ]
@@ -61,15 +59,22 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "hyperslicer1.gif"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
     "# Pass vmin and vmax for 8bit images otherwise the linear intensity slider appears useless\n",
     "fig1, ax1 = plt.subplots()\n",
-    "control1 = hyperslicer(\n",
-    "    beads, vmin=0, vmax=255, play_buttons=True, play_button_pos=\"left\"\n",
-    ")"
+    "control1 = hyperslicer(beads, vmin=0, vmax=255, play_buttons=True, play_button_pos=\"left\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/hyperslicer1.gif)"
    ]
   },
   {
@@ -85,9 +90,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "beads4d = np.linspace(0, 1, 25)[:, None, None, None] * np.stack(\n",
-    "    [beads for i in range(25)]\n",
-    ")"
+    "beads4d = np.linspace(0, 1, 25)[:, None, None, None] * np.stack([beads for i in range(25)])"
    ]
   },
   {
@@ -103,7 +106,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "hyperslicer2.gif"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -111,6 +116,13 @@
     "controls2 = hyperslicer(beads4d, vmin=0, vmax=255)"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/hyperslicer2.gif)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -137,7 +149,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "hyperslicer3.gif"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -147,6 +161,13 @@
     ")"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/hyperslicer3.gif)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -157,13 +178,15 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {},
+   "metadata": {
+    "tags": [
+     "remove-output"
+    ]
+   },
    "outputs": [],
    "source": [
     "fig4, ax4 = plt.subplots()\n",
-    "controls4 = hyperslicer(\n",
-    "    beads4d, vmin=0, vmax=255, axes=((\"linear\", (0, 1)), (\"wavenums\", wns))\n",
-    ")"
+    "controls4 = hyperslicer(beads4d, vmin=0, vmax=255, axes=((\"linear\", (0, 1)), (\"wavenums\", wns)))"
    ]
   },
   {
@@ -177,7 +200,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "hyperslicer5.gif"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -192,6 +217,13 @@
     ")"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/hyperslicer5.gif)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -208,9 +240,7 @@
    "outputs": [],
    "source": [
     "fig6, ax6 = plt.subplots()\n",
-    "controls6 = hyperslicer(\n",
-    "    beads4d, vmin=0, vmax=255, axes=((\"linear\", (0, 1)), \"wavenums\")\n",
-    ")"
+    "controls6 = hyperslicer(beads4d, vmin=0, vmax=255, axes=((\"linear\", (0, 1)), \"wavenums\"))"
    ]
   },
   {
@@ -263,8 +293,12 @@
     "# Intensity is arbiratrily made to be 0-1\n",
     "# Wns = Wns is relevant spectroscopic unit in cm^-1 as above\n",
     "# X,Y = actual dimensions of the images in microns from microscope metadata\n",
-    "coords = {'linear':np.linspace(0,1,beads4d.shape[0]), 'wavenums':wns,\n",
-    "          'X':np.linspace(0, 386.44, 512), 'Y':np.linspace(0, 386.44,512)}"
+    "coords = {\n",
+    "    \"linear\": np.linspace(0, 1, beads4d.shape[0]),\n",
+    "    \"wavenums\": wns,\n",
+    "    \"X\": np.linspace(0, 386.44, 512),\n",
+    "    \"Y\": np.linspace(0, 386.44, 512),\n",
+    "}"
    ]
   },
   {
@@ -273,14 +307,16 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "x_beads4d = xr.DataArray(beads4d, dims=coords.keys(),coords=coords)"
+    "x_beads4d = xr.DataArray(beads4d, dims=coords.keys(), coords=coords)"
    ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "hyperslicer4.gif"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -292,15 +328,15 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "Hyperslicer also supports bare dask arrays with the same logic as numpy arrays."
+    "![](../_static/images/hyperslicer4.gif)"
    ]
   },
   {
-   "cell_type": "code",
-   "execution_count": null,
+   "cell_type": "markdown",
    "metadata": {},
-   "outputs": [],
-   "source": []
+   "source": [
+    "Hyperslicer also supports bare dask arrays with the same logic as numpy arrays."
+   ]
   }
  ],
  "metadata": {
diff --git a/examples/image-segmentation.ipynb b/docs/examples/image-segmentation.ipynb
similarity index 91%
rename from examples/image-segmentation.ipynb
rename to docs/examples/image-segmentation.ipynb
index 08f32dc4..7e4f6c2b 100644
--- a/examples/image-segmentation.ipynb
+++ b/docs/examples/image-segmentation.ipynb
@@ -28,7 +28,9 @@
    "outputs": [],
    "source": [
     "# load a sample image\n",
-    "image = plt.imread(\"https://github.com/matplotlib/matplotlib/raw/v3.3.0/lib/matplotlib/mpl-data/sample_data/ada.png\")"
+    "image = plt.imread(\n",
+    "    \"https://github.com/matplotlib/matplotlib/raw/v3.3.0/lib/matplotlib/mpl-data/sample_data/ada.png\"\n",
+    ")"
    ]
   },
   {
@@ -44,7 +46,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "segment.gif"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -52,6 +56,13 @@
     "display(segmenter)"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/segment.gif)"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
@@ -165,10 +176,12 @@
     "N_classes = 3\n",
     "class_selector = widgets.Dropdown(options=list(range(1, N_classes + 1)), description=\"class\")\n",
     "\n",
+    "\n",
     "def update(change):\n",
     "    multi_class_segmenter.current_class = class_selector.value\n",
     "    multi_class_segmenter.erasing = erasing_button.value\n",
     "\n",
+    "\n",
     "erasing_button = widgets.Checkbox(value=False, description=\"Erasing\")\n",
     "erasing_button.observe(update, names=\"value\")\n",
     "\n",
@@ -178,13 +191,6 @@
     "display(multi_class_segmenter)"
    ]
   },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": []
-  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -201,7 +207,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "segment-preload-mask.png"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -210,6 +218,13 @@
     "display(preloaded)"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/segment-preload-mask.png)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -252,17 +267,12 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "lineprops = {\"color\":\"red\", \"linewidth\":10}\n",
-    "gray = image_segmenter(grayscale_image, nclasses=3, mask=mask, figsize=(5, 5), cmap=\"gray\",lineprops = lineprops)\n",
+    "lineprops = {\"color\": \"red\", \"linewidth\": 10}\n",
+    "gray = image_segmenter(\n",
+    "    grayscale_image, nclasses=3, mask=mask, figsize=(5, 5), cmap=\"gray\", lineprops=lineprops\n",
+    ")\n",
     "display(gray)"
    ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": []
   }
  ],
  "metadata": {
diff --git a/examples/imshow.ipynb b/docs/examples/imshow.ipynb
similarity index 84%
rename from examples/imshow.ipynb
rename to docs/examples/imshow.ipynb
index 6e04b41d..1849ca78 100644
--- a/examples/imshow.ipynb
+++ b/docs/examples/imshow.ipynb
@@ -41,7 +41,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "imshow-basic.apng"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -49,6 +51,13 @@
     "controls = iplt.imshow(f, param1=(-5, 5), param2=(-3, 12))"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/imshow-basic.apng)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -62,7 +71,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "imshow-subplots.apng"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -72,6 +83,13 @@
     "controls2 = iplt.imshow(f, param1=(-5, 5), param2=(-3, 12), ax=ax2)"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/imshow-subplots.apng)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -85,7 +103,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "imshow-no-cmap-auto.apng"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -93,6 +113,13 @@
     "controls3 = iplt.imshow(f, param1=(-5, 5), param2=(-3, 12), autoscale_cmap=False)"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/imshow-no-cmap-auto.apng)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -106,7 +133,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "imshow-threshold.apng"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -118,11 +147,11 @@
    ]
   },
   {
-   "cell_type": "code",
-   "execution_count": null,
+   "cell_type": "markdown",
    "metadata": {},
-   "outputs": [],
-   "source": []
+   "source": [
+    "![](../_static/images/imshow-threshold.apng)"
+   ]
   }
  ],
  "metadata": {
diff --git a/examples/Lotka-Volterra.ipynb b/docs/examples/lotka-volterra.ipynb
similarity index 92%
rename from examples/Lotka-Volterra.ipynb
rename to docs/examples/lotka-volterra.ipynb
index b00f3d87..89ff6337 100644
--- a/examples/Lotka-Volterra.ipynb
+++ b/docs/examples/lotka-volterra.ipynb
@@ -40,9 +40,10 @@
     "\n",
     "# use `c_` instead of `c` because `c` is an argument to plt.scatter\n",
     "\n",
+    "\n",
     "def f(a, b, c_, d):\n",
     "    def dX_dt(X, t=0):\n",
-    "        \"\"\" Return the growth rate of fox and rabbit populations. \"\"\"\n",
+    "        \"\"\"Return the growth rate of fox and rabbit populations.\"\"\"\n",
     "        rabbits, foxes = X\n",
     "        dRabbit_dt = a * rabbits - b * foxes * rabbits\n",
     "        dFox_dt = -c_ * foxes + d * b * rabbits * foxes\n",
@@ -65,14 +66,14 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "lotka-volterra1.gif"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
     "fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(10, 4.8))\n",
-    "controls = iplt.plot(\n",
-    "    f, ax=ax1, a=(0.5, 2), b=(0.1, 3), c_=(1, 3), d=(0.1, 2), parametric=True\n",
-    ")\n",
+    "controls = iplt.plot(f, ax=ax1, a=(0.5, 2), b=(0.1, 3), c_=(1, 3), d=(0.1, 2), parametric=True)\n",
     "ax1.set_xlabel(\"rabbits\")\n",
     "ax1.set_ylabel(\"foxes\")\n",
     "\n",
@@ -82,6 +83,13 @@
     "_ = ax2.legend()"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/lotka-volterra1.gif)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -94,13 +102,6 @@
     "controls.use_cache = False\n",
     "```"
    ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": []
   }
  ],
  "metadata": {
diff --git a/examples/mpl-sliders.ipynb b/docs/examples/mpl-sliders.ipynb
similarity index 92%
rename from examples/mpl-sliders.ipynb
rename to docs/examples/mpl-sliders.ipynb
index 55cb2c48..7589e697 100644
--- a/examples/mpl-sliders.ipynb
+++ b/docs/examples/mpl-sliders.ipynb
@@ -12,7 +12,7 @@
     "\n",
     ":::\n",
     "\n",
-    "This page is dedicated to showing how to use the `interactive_*` functions---such as {func}`.interactive_plot`---with Matplotlib widgets. For a more general discussion of how to use the `ipyplot` submodule, see the [Usage-Guide](./Usage-Guide.ipynb) page.\n",
+    "This page is dedicated to showing how to use the `interactive_*` functions---such as {func}`.interactive_plot`---with Matplotlib widgets. For a more general discussion of how to use the `ipyplot` submodule, see {doc}`usage`.\n",
     "\n",
     "\n",
     "## Differences from ipywidgets sliders\n",
@@ -42,7 +42,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "mpl-sliders.gif"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -65,6 +67,13 @@
     "plt.show()"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/mpl-sliders.gif)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -81,7 +90,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "same-fig-mpl-slider.png"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -107,11 +118,11 @@
    ]
   },
   {
-   "cell_type": "code",
-   "execution_count": null,
+   "cell_type": "markdown",
    "metadata": {},
-   "outputs": [],
-   "source": []
+   "source": [
+    "![](../_static/images/same-fig-mpl-slider.png)"
+   ]
   }
  ],
  "metadata": {
diff --git a/examples/mpl-sliders.py b/docs/examples/mpl-sliders.py
similarity index 100%
rename from examples/mpl-sliders.py
rename to docs/examples/mpl-sliders.py
diff --git a/examples/nations.json b/docs/examples/nations.json
similarity index 100%
rename from examples/nations.json
rename to docs/examples/nations.json
diff --git a/examples/non-ipympl-backends.ipynb b/docs/examples/non-ipympl-backends.ipynb
similarity index 96%
rename from examples/non-ipympl-backends.ipynb
rename to docs/examples/non-ipympl-backends.ipynb
index f09ae1b9..745355cf 100644
--- a/examples/non-ipympl-backends.ipynb
+++ b/docs/examples/non-ipympl-backends.ipynb
@@ -25,9 +25,9 @@
    "metadata": {},
    "outputs": [],
    "source": [
+    "import matplotlib.pyplot as plt\n",
     "import numpy as np\n",
     "\n",
-    "import matplotlib.pyplot as plt\n",
     "from mpl_interactions import interactive_plot"
    ]
   },
@@ -96,13 +96,6 @@
     "    interaction_type=\"move\",\n",
     ")"
    ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": []
   }
  ],
  "metadata": {
diff --git a/examples/plot.ipynb b/docs/examples/plot.ipynb
similarity index 93%
rename from examples/plot.ipynb
rename to docs/examples/plot.ipynb
index d05b2447..8915ba39 100644
--- a/examples/plot.ipynb
+++ b/docs/examples/plot.ipynb
@@ -28,7 +28,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "plot-simple.gif"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -45,6 +47,13 @@
     "controls = iplt.plot(x, f, tau=tau, beta=beta)"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/plot-simple.gif)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -73,7 +82,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "plot-simple.gif"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -81,6 +92,13 @@
     "controls2 = iplt.plot(x, f, tau=(1, 10, 100), beta=(1, 10))"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/plot-simple.gif)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -94,7 +112,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "plot-categorical.apng"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -115,6 +135,13 @@
     ")"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/plot-categorical.apng)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -178,9 +205,7 @@
   },
   {
    "cell_type": "markdown",
-   "metadata": {
-    "gif": "plot-multiple-functions.gif"
-   },
+   "metadata": {},
    "source": [
     "## Styling of plot\n",
     "\n",
@@ -196,7 +221,11 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {},
+   "metadata": {
+    "tags": [
+     "remove-output"
+    ]
+   },
    "outputs": [],
    "source": [
     "x_ = np.linspace(0, np.pi, 100)\n",
@@ -231,6 +260,13 @@
     "_ = plt.legend()  # _ to capture the annoying output that would otherwise appear"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/plot-multiple-functions.gif)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -301,13 +337,6 @@
     "\n",
     "_ = plt.legend()  # _ to capture the annoying output that would otherwise appear"
    ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": []
   }
  ],
  "metadata": {
diff --git a/examples/range-sliders.ipynb b/docs/examples/range-sliders.ipynb
similarity index 97%
rename from examples/range-sliders.ipynb
rename to docs/examples/range-sliders.ipynb
index 2fcced30..67dee268 100644
--- a/examples/range-sliders.ipynb
+++ b/docs/examples/range-sliders.ipynb
@@ -126,13 +126,6 @@
     "iplt.axvline(ctrls[\"vmin\"], ax=axs[1], c=\"k\")\n",
     "iplt.axvline(ctrls[\"vmax\"], ax=axs[1], c=\"k\")"
    ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": []
   }
  ],
  "metadata": {
diff --git a/examples/rossler-attractor.ipynb b/docs/examples/rossler-attractor.ipynb
similarity index 96%
rename from examples/rossler-attractor.ipynb
rename to docs/examples/rossler-attractor.ipynb
index 603808b5..732f6240 100644
--- a/examples/rossler-attractor.ipynb
+++ b/docs/examples/rossler-attractor.ipynb
@@ -20,11 +20,12 @@
    "outputs": [],
    "source": [
     "%matplotlib ipympl\n",
+    "from functools import lru_cache\n",
+    "\n",
     "import ipywidgets as widgets\n",
     "import matplotlib.pyplot as plt\n",
     "import numpy as np\n",
     "from scipy.integrate import solve_ivp\n",
-    "from functools import lru_cache\n",
     "\n",
     "import mpl_interactions.ipyplot as iplt"
    ]
@@ -65,6 +66,8 @@
     "\n",
     "\n",
     "cache = {}\n",
+    "\n",
+    "\n",
     "def f(a, b, c_):\n",
     "    def deriv(t, cur_pos):\n",
     "        x, y, z = cur_pos\n",
@@ -94,7 +97,7 @@
     "    \".-\",\n",
     "    a=(0.05, 0.3, 1000),\n",
     "    b=0.2,\n",
-    "    c_=(1, 20), # we can't use `c` because that is a kwarg for matplotlib that controls color\n",
+    "    c_=(1, 20),  # we can't use `c` because that is a kwarg for matplotlib that controls color\n",
     "    parametric=True,\n",
     "    alpha=0.5,\n",
     "    play_buttons=True,\n",
@@ -167,13 +170,6 @@
     "plt.colorbar().set_label(\"Time Point\")\n",
     "plt.tight_layout()"
    ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": []
   }
  ],
  "metadata": {
diff --git a/examples/scalar-arguments.ipynb b/docs/examples/scalar-arguments.ipynb
similarity index 97%
rename from examples/scalar-arguments.ipynb
rename to docs/examples/scalar-arguments.ipynb
index d1add2ac..aec8dcc0 100644
--- a/examples/scalar-arguments.ipynb
+++ b/docs/examples/scalar-arguments.ipynb
@@ -110,13 +110,6 @@
     "# or when in a jupyter notebook\n",
     "display(ctrls)"
    ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": []
   }
  ],
  "metadata": {
diff --git a/examples/scatter-selector.ipynb b/docs/examples/scatter-selector.ipynb
similarity index 95%
rename from examples/scatter-selector.ipynb
rename to docs/examples/scatter-selector.ipynb
index f486d243..975f1fb3 100644
--- a/examples/scatter-selector.ipynb
+++ b/docs/examples/scatter-selector.ipynb
@@ -64,9 +64,7 @@
     "    meta = pickle.load(f)\n",
     "prices = np.load(\"data/stock-prices.npz\")[\"prices\"]\n",
     "names = meta[\"names\"]\n",
-    "good_idx = meta[\n",
-    "    \"good_idx\"\n",
-    "]  # only plot the ones for which we were able to parse sector info\n",
+    "good_idx = meta[\"good_idx\"]  # only plot the ones for which we were able to parse sector info\n",
     "data_colors = meta[\"data_colors\"]\n",
     "\n",
     "# calculate the daily price difference\n",
@@ -118,7 +116,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "scatter-selector-stocks.apng"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -143,6 +143,13 @@
     "ph = panhandler(fig)"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/scatter-selector-stocks.apng)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -186,9 +193,7 @@
     "\n",
     "# processing names and sector info\n",
     "\n",
-    "arr = np.loadtxt(\n",
-    "    \"data/SP500_names.csv\", delimiter=\"|\", skiprows=1, dtype=str, encoding=\"utf-8\"\n",
-    ")\n",
+    "arr = np.loadtxt(\"data/SP500_names.csv\", delimiter=\"|\", skiprows=1, dtype=str, encoding=\"utf-8\")\n",
     "name_dict = {a[0].strip(): a[[1, 2, 3]] for a in arr}\n",
     "# idx_to_info = {i:name_dict[real_names[i]] for i in range(468)}\n",
     "good_names = []\n",
diff --git a/examples/scatter.ipynb b/docs/examples/scatter.ipynb
similarity index 75%
rename from examples/scatter.ipynb
rename to docs/examples/scatter.ipynb
index 7bbdd37b..561d2b12 100644
--- a/examples/scatter.ipynb
+++ b/docs/examples/scatter.ipynb
@@ -14,12 +14,13 @@
    "outputs": [],
    "source": [
     "%matplotlib ipympl\n",
+    "import ipywidgets as widgets\n",
     "import matplotlib.pyplot as plt\n",
     "import numpy as np\n",
-    "import mpl_interactions.ipyplot as iplt\n",
-    "import ipywidgets as widgets\n",
     "import pandas as pd\n",
-    "from matplotlib.colors import to_rgba_array, TABLEAU_COLORS, XKCD_COLORS"
+    "from matplotlib.colors import TABLEAU_COLORS, XKCD_COLORS, to_rgba_array\n",
+    "\n",
+    "import mpl_interactions.ipyplot as iplt"
    ]
   },
   {
@@ -33,17 +34,29 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "scatter1.png"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
-    "\n",
     "N = 50\n",
     "x = np.random.rand(N)\n",
+    "\n",
+    "\n",
     "def f_y(x, tau):\n",
-    "    return np.sin(x*tau)**2 + np.random.randn(N)*.01\n",
+    "    return np.sin(x * tau) ** 2 + np.random.randn(N) * 0.01\n",
+    "\n",
+    "\n",
     "fig, ax = plt.subplots()\n",
-    "controls = iplt.scatter(x,f_y, tau = (1, 2*np.pi, 100)) "
+    "controls = iplt.scatter(x, f_y, tau=(1, 2 * np.pi, 100))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/scatter1.png)"
    ]
   },
   {
@@ -58,19 +71,34 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "scatter2.png"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
     "N = 50\n",
     "x = np.random.rand(N)\n",
+    "\n",
+    "\n",
     "def f_y1(x, tau):\n",
-    "    return np.sin(x*tau)**2 + np.random.randn(N)*.01\n",
+    "    return np.sin(x * tau) ** 2 + np.random.randn(N) * 0.01\n",
+    "\n",
+    "\n",
     "def f_y2(x, tau):\n",
-    "    return np.cos(x*tau)**2 + np.random.randn(N)*.1\n",
+    "    return np.cos(x * tau) ** 2 + np.random.randn(N) * 0.1\n",
+    "\n",
+    "\n",
     "fig, ax = plt.subplots()\n",
-    "controls = iplt.scatter(x,f_y1, tau = (1, 2*np.pi, 100), c = 'blue', s = 5) \n",
-    "_ = iplt.scatter(x,f_y2, controls= controls, c = 'red', s = 20) "
+    "controls = iplt.scatter(x, f_y1, tau=(1, 2 * np.pi, 100), c=\"blue\", s=5)\n",
+    "_ = iplt.scatter(x, f_y2, controls=controls, c=\"red\", s=20)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/scatter2.png)"
    ]
   },
   {
@@ -86,17 +114,32 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "scatter3.png"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
     "N = 50\n",
+    "\n",
+    "\n",
     "def f_x(mean):\n",
     "    return np.random.rand(N) + mean\n",
+    "\n",
+    "\n",
     "def f_y(x, mean):\n",
     "    return np.random.rand(N) - mean\n",
+    "\n",
+    "\n",
     "fig, ax = plt.subplots()\n",
-    "controls = iplt.scatter(f_x, f_y, mean = (0, 1, 100), s = None, c = np.random.randn(N))"
+    "controls = iplt.scatter(f_x, f_y, mean=(0, 1, 100), s=None, c=np.random.randn(N))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/scatter3.png)"
    ]
   },
   {
@@ -114,7 +157,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "scatter4.png"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -126,12 +171,15 @@
     "def f(x, mean):\n",
     "    return np.random.rand(N) + mean - 0.5\n",
     "\n",
+    "\n",
     "def c_func(x, y, mean):\n",
     "    return x\n",
     "\n",
+    "\n",
     "def s_func(x, y, mean):\n",
     "    return np.abs(40 / (x + 0.001))\n",
     "\n",
+    "\n",
     "def ec_func(x, y, mean):\n",
     "    if np.random.rand() > 0.5:\n",
     "        return \"black\"\n",
@@ -151,6 +199,13 @@
     ")"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/scatter4.png)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -162,7 +217,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "scatter5.png"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -191,6 +248,13 @@
     "sliders = iplt.scatter(x, y, mean=(0, 1, 100), alpha=None, c=threshold)"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/scatter5.png)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -211,30 +275,40 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "# this cell was taken wholesale from the bqplot example \n",
+    "# this cell was taken wholesale from the bqplot example\n",
     "# bqplot is under the apache license, see their license file here:\n",
     "# https://github.com/bqplot/bqplot/blob/55152feb645b523faccb97ea4083ca505f26f6a2/LICENSE\n",
-    "data = pd.read_json('nations.json')\n",
+    "data = pd.read_json(\"nations.json\")\n",
+    "\n",
+    "\n",
     "def clean_data(data):\n",
-    "    for column in ['income', 'lifeExpectancy', 'population']:\n",
+    "    for column in [\"income\", \"lifeExpectancy\", \"population\"]:\n",
     "        data = data.drop(data[data[column].apply(len) <= 4].index)\n",
     "    return data\n",
     "\n",
+    "\n",
     "def extrap_interp(data):\n",
     "    data = np.array(data)\n",
-    "    x_range = np.arange(1800, 2009, 1.)\n",
+    "    x_range = np.arange(1800, 2009, 1.0)\n",
     "    y_range = np.interp(x_range, data[:, 0], data[:, 1])\n",
     "    return y_range\n",
     "\n",
+    "\n",
     "def extrap_data(data):\n",
-    "    for column in ['income', 'lifeExpectancy', 'population']:\n",
+    "    for column in [\"income\", \"lifeExpectancy\", \"population\"]:\n",
     "        data[column] = data[column].apply(extrap_interp)\n",
     "    return data\n",
+    "\n",
+    "\n",
     "data = clean_data(data)\n",
     "data = extrap_data(data)\n",
-    "income_min, income_max = np.min(data['income'].apply(np.min)), np.max(data['income'].apply(np.max))\n",
-    "life_exp_min, life_exp_max = np.min(data['lifeExpectancy'].apply(np.min)), np.max(data['lifeExpectancy'].apply(np.max))\n",
-    "pop_min, pop_max = np.min(data['population'].apply(np.min)), np.max(data['population'].apply(np.max))"
+    "income_min, income_max = np.min(data[\"income\"].apply(np.min)), np.max(data[\"income\"].apply(np.max))\n",
+    "life_exp_min, life_exp_max = np.min(data[\"lifeExpectancy\"].apply(np.min)), np.max(\n",
+    "    data[\"lifeExpectancy\"].apply(np.max)\n",
+    ")\n",
+    "pop_min, pop_max = np.min(data[\"population\"].apply(np.min)), np.max(\n",
+    "    data[\"population\"].apply(np.max)\n",
+    ")"
    ]
   },
   {
@@ -278,7 +352,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "scatter6.png"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -303,11 +379,11 @@
    ]
   },
   {
-   "cell_type": "code",
-   "execution_count": null,
+   "cell_type": "markdown",
    "metadata": {},
-   "outputs": [],
-   "source": []
+   "source": [
+    "![](../_static/images/scatter6.png)"
+   ]
   }
  ],
  "metadata": {
diff --git a/docs/examples/tidbits/play-buttons.ipynb b/docs/examples/tidbits/play-buttons.ipynb
new file mode 100644
index 00000000..35dcb730
--- /dev/null
+++ b/docs/examples/tidbits/play-buttons.ipynb
@@ -0,0 +1,173 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Attaching play buttons to sliders\n",
+    "\n",
+    ":::{note}\n",
+    "\n",
+    "The labels will not update as that requires a Python kernel.\n",
+    "\n",
+    ":::\n",
+    "\n",
+    "If you are working in Jupyter then you can add an [`ipywidgets.Play`](https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20List.html#Play-(Animation)-widget) widget to the sliders for any of the `interactive_*` functions.\n",
+    "\n",
+    "In this tutorial all the functions are {func}`~matplotlib.pyplot.scatter` but this will work for {func}`~matplotlib.pyplot.plot`, {func}`~matplotlib.pyplot.hist`, {func}`~matplotlib.pyplot.imshow`, etc..."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Specifying Which Sliders Get Play buttons"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%matplotlib ipympl\n",
+    "import matplotlib.pyplot as plt\n",
+    "import numpy as np\n",
+    "\n",
+    "import mpl_interactions.ipyplot as iplt\n",
+    "\n",
+    "# turn off interactive mode so that broken\n",
+    "# plots don't render in the docs\n",
+    "plt.ioff()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### {obj}`bool`: All get a button"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "N = 50\n",
+    "x = np.random.rand(N)\n",
+    "\n",
+    "\n",
+    "def f_y(x, tau, beta):\n",
+    "    return np.sin(x * tau) ** 2 + np.random.randn(N) * 0.01 * beta\n",
+    "\n",
+    "\n",
+    "fig, ax = plt.subplots()\n",
+    "controls = iplt.scatter(x, f_y, tau=(1, 2 * np.pi, 100), beta=(0, 2), play_buttons=True)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### {obj}`str`: Position the button\n",
+    "You make a play button for all sliders on either the left or the right using a string argument."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "fig, ax = plt.subplots()\n",
+    "controls = iplt.scatter(x, f_y, tau=(1, 2 * np.pi, 100), beta=(0, 2), play_buttons=\"left\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "fig, ax = plt.subplots()\n",
+    "controls = iplt.scatter(x, f_y, tau=(1, 2 * np.pi, 100), beta=(0, 2), play_buttons=\"right\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### {obj}`list`: Choose by name"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "fig, ax = plt.subplots()\n",
+    "controls = iplt.scatter(x, f_y, tau=(1, 2 * np.pi, 100), beta=(0, 2), play_buttons=[\"tau\"])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### {obj}`~collections.defaultdict`: Specify by name and choose default\n",
+    "\n",
+    "If you have many parameters and you want the most, but not all, of them to have a Play button then\n",
+    "you should use a {obj}`~collections.defaultdict`:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from collections import defaultdict\n",
+    "\n",
+    "\n",
+    "def f(x, **kwargs):\n",
+    "    return x\n",
+    "\n",
+    "\n",
+    "play_buttons = defaultdict(lambda: True)\n",
+    "play_buttons[\"tau\"] = False\n",
+    "fig, ax = plt.subplots()\n",
+    "controls = iplt.scatter(\n",
+    "    x,\n",
+    "    f,\n",
+    "    tau=(1, 2 * np.pi, 100),\n",
+    "    beta=(0, 2),\n",
+    "    zeta=(0, 1),\n",
+    "    psi=(0, 1),\n",
+    "    play_buttons=play_buttons,\n",
+    ")"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.5"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/Usage-Guide.ipynb b/docs/examples/usage.ipynb
similarity index 87%
rename from examples/Usage-Guide.ipynb
rename to docs/examples/usage.ipynb
index 05a289f6..3728be45 100644
--- a/examples/Usage-Guide.ipynb
+++ b/docs/examples/usage.ipynb
@@ -59,7 +59,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "usage-basic.apng"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -69,6 +71,13 @@
     "controls = iplt.plot(x, f, tau=tau, beta=beta)"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/usage-basic.apng)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -88,7 +97,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "usage-multi-func.apng"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -108,6 +119,13 @@
     "_ = plt.legend()"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/usage-multi-func.apng)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -128,7 +146,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "usage-index-controls.apng"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -152,6 +172,13 @@
     "_ = iplt.plot(f2, controls=controls, ax=ax2)"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/usage-index-controls.apng)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -165,7 +192,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "usage-mixed-types.apng"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -185,6 +214,13 @@
     "_ = iplt.scatter(x, f_scatter, s=0.5, controls=controls[\"tau\"])"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/usage-mixed-types.apng)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -204,7 +240,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "usage-tuple-params.apng"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -212,6 +250,13 @@
     "controls = iplt.plot(x, f, tau=(5, 10, 5), beta=(0.25, 1))"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/usage-tuple-params.apng)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -225,7 +270,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "usage-rangeslider.apng"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -234,10 +281,21 @@
     "    min_, max_ = min_max\n",
     "    return np.linspace(min_, max_, 100)\n",
     "    # break up the tuple\n",
+    "\n",
+    "\n",
     "def f_range(x, tau, **kwargs):\n",
-    "    return np.sin(x*tau)\n",
+    "    return np.sin(x * tau)\n",
+    "\n",
+    "\n",
     "fig, ax = plt.subplots()\n",
-    "controls = iplt.plot(f_x, f_range, tau=(5, 10, 5), min_max = (\"r\", 0, 10))"
+    "controls = iplt.plot(f_x, f_range, tau=(5, 10, 5), min_max=(\"r\", 0, 10))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/usage-rangeslider.apng)"
    ]
   },
   {
@@ -263,7 +321,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "usage-categorical.apng"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -282,6 +342,13 @@
     "controls = iplt.plot(x, f, tau=tau, beta=(0.25, 1), type_={(\"sin\", \"cos\")})"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/usage-categorical.apng)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -300,7 +367,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "usage-fixed.apng"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -316,6 +385,13 @@
     "controls = iplt.plot(x, f, tau=tau, beta=(0.25, 1))"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/usage-fixed.apng)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -347,7 +423,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "usage-styling.apng"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -373,12 +451,14 @@
     "    label=\"interactive!\",\n",
     ")\n",
     "\n",
-    "iplt.title(\"the value of tau is: {tau:.2f}\", controls=controls['tau'])\n",
+    "iplt.title(\"the value of tau is: {tau:.2f}\", controls=controls[\"tau\"])\n",
     "# you can still use plt commands if this is the active figure\n",
     "def ylabel(tau):\n",
     "    return f\"tau/2 is {np.round(tau/2,3)}\"\n",
-    "iplt.ylabel(ylabel, controls = controls['tau'])\n",
-    "iplt.xlabel(\"This xlabel also changes with tau! tau~={tau:.0f}\", controls = controls['tau'])\n",
+    "\n",
+    "\n",
+    "iplt.ylabel(ylabel, controls=controls[\"tau\"])\n",
+    "iplt.xlabel(\"This xlabel also changes with tau! tau~={tau:.0f}\", controls=controls[\"tau\"])\n",
     "\n",
     "# you can new lines - though they won't be updated interactively.\n",
     "plt.plot(x, np.sin(x), label=\"Added after, not interactive\")\n",
@@ -386,6 +466,13 @@
     "_ = plt.legend()"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "![](../_static/images/usage-styling.apng)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -397,13 +484,6 @@
     "\n",
     "For matplotlib sliders, the {obj}`.Controls` object stores a list of all the figures it uses for controls in {attr}`.Controls.control_figures`"
    ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": []
   }
  ],
  "metadata": {
diff --git a/examples/zoom-factory.ipynb b/docs/examples/zoom-factory.ipynb
similarity index 89%
rename from examples/zoom-factory.ipynb
rename to docs/examples/zoom-factory.ipynb
index 4dea4a74..74a6706f 100644
--- a/examples/zoom-factory.ipynb
+++ b/docs/examples/zoom-factory.ipynb
@@ -33,7 +33,9 @@
    "outputs": [],
    "source": [
     "# A sample image\n",
-    "image = plt.imread(\"https://github.com/matplotlib/matplotlib/raw/v3.3.0/lib/matplotlib/mpl-data/sample_data/ada.png\")\n"
+    "image = plt.imread(\n",
+    "    \"https://github.com/matplotlib/matplotlib/raw/v3.3.0/lib/matplotlib/mpl-data/sample_data/ada.png\"\n",
+    ")"
    ]
   },
   {
@@ -74,7 +76,9 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "gif": "zoom-and-pan.gif"
+    "tags": [
+     "remove-output"
+    ]
    },
    "outputs": [],
    "source": [
@@ -83,11 +87,11 @@
    ]
   },
   {
-   "cell_type": "code",
-   "execution_count": null,
+   "cell_type": "markdown",
    "metadata": {},
-   "outputs": [],
-   "source": []
+   "source": [
+    "![](../_static/images/zoom-and-pan.gif)"
+   ]
   }
  ],
  "metadata": {
diff --git a/docs/gifmaker.py b/docs/gifmaker.py
deleted file mode 100644
index 6626a4e3..00000000
--- a/docs/gifmaker.py
+++ /dev/null
@@ -1,49 +0,0 @@
-import base64
-import glob
-import json
-import os
-import shutil
-
-
-def gogogo_all(source_dir, dest_dir):
-    """
-    copy and render all the notebooks from one dir to another
-    """
-    notebooks = glob.glob(os.path.join(source_dir, "*.ipynb"))
-    gifs = glob.glob(os.path.join(source_dir, "*.gif"))
-    for nb in notebooks:
-        to = os.path.join(dest_dir, os.path.basename(nb))
-        gogogo_gif(nb, to)
-    for gif in gifs:
-        to = os.path.join(dest_dir, os.path.basename(gif))
-        shutil.copyfile(gif, to)
-
-
-def gogogo_gif(notebook_from, notebook_to):
-    """
-    convert the output of cells with `gif` in their metadata tag to be those
-    gifs
-
-    parameters
-    ----------
-    notebook_from : string
-        path of source notebook
-    notebook_to : string
-        where to put the rendered notebook
-    """
-    with open(notebook_from) as f:
-        nb = json.load(f)
-    for i, cell in enumerate(nb["cells"]):
-        if "gif" in cell["metadata"]:
-            gif = cell["metadata"]["gif"]
-            gif = os.path.join("../_static/images/", gif)
-            cell["outputs"] = [
-                {
-                    "data": {"text/html": f'<img src="{gif}">'},
-                    "execution_count": 1,
-                    "metadata": {},
-                    "output_type": "execute_result",
-                }
-            ]
-    with open(notebook_to, "w") as f:
-        json.dump(nb, f, indent=2)
diff --git a/docs/index.md b/docs/index.md
index bb2be9ba..657bfcb6 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -25,7 +25,7 @@ To also install version of [`ipympl`](https://github.com/matplotlib/ipympl) and
 pip install mpl_interactions[jupyter]
 ```
 
-Further instructions for installation from JupyterLab can be found on the {doc}`Installation` page.
+Further instructions for installation from JupyterLab can be found on the {doc}`install` page.
 
 ## Getting Help
 
@@ -75,26 +75,26 @@ plt.show()
 
 ## Matplotlib backends
 
-mpl_interactions' functions will work in any Matplotlib backend. In most backends they will use the Matplotlib Slider and Radio button widgets. However, if you are working in a Jupyter notebook the [`ipympl`](https://github.com/matplotlib/ipympl) backend then ipywidgets sliders will be used as the controls. Further discussion of the behavior as a function of backend can be found on the {doc}`Backends` page.
+mpl_interactions' functions will work in any Matplotlib backend. In most backends they will use the Matplotlib Slider and Radio button widgets. However, if you are working in a Jupyter notebook the [`ipympl`](https://github.com/matplotlib/ipympl) backend then ipywidgets sliders will be used as the controls. Further discussion of the behavior as a function of backend can be found on the {doc}`backends` page.
 
 _Follow the links below for further information on installation, functions, and plot examples._
 
 ```{toctree}
 :maxdepth: 3
 
-Installation
-Backends
-compare-to-ipywidgets
-API <autoapi/mpl_interactions>
+install
+backends
+comparison
+API <api/mpl_interactions>
 gallery/index
-Contributing
+contributing
 ```
 
 ```{toctree}
 :caption: Tutorials
 :maxdepth: 1
 
-examples/Usage-Guide.ipynb
+examples/usage.ipynb
 examples/mpl-sliders.ipynb
 examples/custom-callbacks.ipynb
 examples/animations.ipynb
@@ -122,7 +122,7 @@ examples/heatmap-slicer.ipynb
 :caption: Showcase
 :maxdepth: 1
 
-examples/Lotka-Volterra.ipynb
+examples/lotka-volterra.ipynb
 examples/rossler-attractor.ipynb
 ```
 
diff --git a/docs/Installation.md b/docs/install.md
similarity index 100%
rename from docs/Installation.md
rename to docs/install.md
diff --git a/environment.yml b/environment.yml
new file mode 100644
index 00000000..c7ba2b50
--- /dev/null
+++ b/environment.yml
@@ -0,0 +1,9 @@
+name: mpl-interactions
+channels:
+  - defaults
+dependencies:
+  - pandoc
+  - pip
+  - python
+  - pip:
+      - -e .[dev]
diff --git a/examples/devlop/devlop-axline.ipynb b/examples/devlop/devlop-axline.ipynb
deleted file mode 100644
index 0c5e2346..00000000
--- a/examples/devlop/devlop-axline.ipynb
+++ /dev/null
@@ -1,527 +0,0 @@
-{
- "cells": [
-  {
-   "cell_type": "code",
-   "execution_count": 1,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "%matplotlib ipympl\n",
-    "import ipywidgets as widgets\n",
-    "import matplotlib.pyplot as plt\n",
-    "import numpy as np\n",
-    "\n",
-    "%load_ext autoreload\n",
-    "%autoreload 2\n",
-    "from mpl_interactions import *"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 2,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "application/vnd.jupyter.widget-view+json": {
-       "model_id": "8ecf6b7a6b3741afb0f228e0ece0997c",
-       "version_major": 2,
-       "version_minor": 0
-      },
-      "text/plain": [
-       "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "data": {
-      "application/vnd.jupyter.widget-view+json": {
-       "model_id": "d760d944f599429c979d7e11b7f0679d",
-       "version_major": 2,
-       "version_minor": 0
-      },
-      "text/plain": [
-       "VBox(children=(HBox(children=(IntSlider(value=0, description='heck', max=49, readout=False), Label(value='0.00…"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    }
-   ],
-   "source": [
-    "fig, ax = plt.subplots()\n",
-    "def y(heck):\n",
-    "    return heck\n",
-    "def xmin(heck):\n",
-    "    return heck\n",
-    "# controls = interactive_axhline(y,0,1, heck=(0,1))\n",
-    "controls = interactive_axhline(y,xmin,1, heck=(0,1))\n",
-    "# ret.set_ydata([.5,1])"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "plt.axhline()"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "\n",
-    "\n",
-    "def interactive_axhline(\n",
-    "    y=0,\n",
-    "    xmin=0,\n",
-    "    xmax=1,\n",
-    "    ax=None,\n",
-    "    slider_formats=None,\n",
-    "    title=None,\n",
-    "    force_ipywidgets=False,\n",
-    "    play_buttons=False,\n",
-    "    play_button_pos=\"right\",\n",
-    "    controls=None,\n",
-    "    display_controls=True,\n",
-    "    **kwargs,\n",
-    "):\n",
-    "    \"\"\"\n",
-    "    Control an horizontal line using widgets.\n",
-    "\n",
-    "    parameters\n",
-    "    ----------\n",
-    "    y : float or function\n",
-    "        y position in data coordinates of the horizontal line.\n",
-    "    xmin : float or function\n",
-    "        Should be between 0 and 1, 0 being the far left of the plot, 1 the\n",
-    "        far right of the plot.\n",
-    "    xmax : float or function\n",
-    "        Should be between 0 and 1, 0 being the far left of the plot, 1 the\n",
-    "        far right of the plot.\n",
-    "    ax : matplotlib axis, optional\n",
-    "        If None a new figure and axis will be created\n",
-    "    slider_formats : None, string, or dict\n",
-    "        If None a default value of decimal points will be used. Uses the new {} style formatting\n",
-    "    force_ipywidgets : boolean\n",
-    "        If True ipywidgets will always be used, even if not using the ipympl backend.\n",
-    "        If False the function will try to detect if it is ok to use ipywidgets\n",
-    "        If ipywidgets are not used the function will fall back on matplotlib widgets\n",
-    "    play_buttons : bool or dict, optional\n",
-    "        Whether to attach an ipywidgets.Play widget to any sliders that get created.\n",
-    "        If a boolean it will apply to all kwargs, if a dictionary you choose which sliders you\n",
-    "        want to attach play buttons too.\n",
-    "    play_button_pos : str, or dict, or list(str)\n",
-    "        'left' or 'right'. Whether to position the play widget(s) to the left or right of the slider(s)\n",
-    "    controls : mpl_interactions.controller.Controls\n",
-    "        An existing controls object if you want to tie multiple plot elements to the same set of\n",
-    "        controls\n",
-    "    display_controls : boolean\n",
-    "        Whether the controls should display on creation. Ignored if controls is specified.\n",
-    "\n",
-    "    returns\n",
-    "    -------\n",
-    "    controls\n",
-    "    \"\"\"\n",
-    "    ipympl = notebook_backend()\n",
-    "    fig, ax = gogogo_figure(ipympl, ax)\n",
-    "    use_ipywidgets = ipympl or force_ipywidgets\n",
-    "    slider_formats = create_slider_format_dict(slider_formats)\n",
-    "\n",
-    "    controls, params = gogogo_controls(\n",
-    "        kwargs, controls, display_controls, slider_formats, play_buttons, play_button_pos\n",
-    "    )\n",
-    "\n",
-    "    def update(params, indices, cache):\n",
-    "        if title is not None:\n",
-    "            ax.set_title(title.format(**params))\n",
-    "        y_ = callable_else_value(y, params, cache).item()\n",
-    "        line.set_ydata([y_, y_])\n",
-    "        xmin_ = callable_else_value(xmin, params, cache).item()\n",
-    "        xmax_ = callable_else_value(xmax, params, cache).item()\n",
-    "        line.set_xdata([xmin_, xmax_])\n",
-    "        # TODO consider updating just the ydatalim here\n",
-    "\n",
-    "    controls.register_function(update, fig, params)\n",
-    "    line = ax.axhline(\n",
-    "        callable_else_value(y, params).item(),\n",
-    "        callable_else_value(xmin, params).item(),\n",
-    "        callable_else_value(xmax, params).item(),\n",
-    "    )\n",
-    "    return controls\n",
-    "\n",
-    "\n",
-    "def interactive_axvline(\n",
-    "    x=0,\n",
-    "    ymin=0,\n",
-    "    ymax=1,\n",
-    "    ax=None,\n",
-    "    slider_formats=None,\n",
-    "    title=None,\n",
-    "    force_ipywidgets=False,\n",
-    "    play_buttons=False,\n",
-    "    play_button_pos=\"right\",\n",
-    "    controls=None,\n",
-    "    display_controls=True,\n",
-    "    **kwargs,\n",
-    "):\n",
-    "    ipympl = notebook_backend()\n",
-    "    fig, ax = gogogo_figure(ipympl, ax)\n",
-    "    use_ipywidgets = ipympl or force_ipywidgets\n",
-    "    slider_formats = create_slider_format_dict(slider_formats)\n",
-    "\n",
-    "    controls, params = gogogo_controls(\n",
-    "        kwargs, controls, display_controls, slider_formats, play_buttons, play_button_pos\n",
-    "    )\n",
-    "\n",
-    "    def update(params, indices, cache):\n",
-    "        if title is not None:\n",
-    "            ax.set_title(title.format(**params))\n",
-    "        x_ = callable_else_value(x, params, cache).item()\n",
-    "        line.set_ydata([x_, x_])\n",
-    "        ymin_ = callable_else_value(ymin, params, cache).item()\n",
-    "        ymax_ = callable_else_value(ymax, params, cache).item()\n",
-    "        line.set_xdata([ymin_, ymax_])\n",
-    "        # TODO consider updating just the ydatalim here\n",
-    "\n",
-    "    controls.register_function(update, fig, params)\n",
-    "    line = ax.axvline(\n",
-    "        callable_else_value(x, params).item(),\n",
-    "        callable_else_value(ymin, params).item(),\n",
-    "        callable_else_value(ymax, params).item(),\n",
-    "    )\n",
-    "    return controls"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 18,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "ret.set_ydata([0.5, 1])"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 14,
-   "metadata": {},
-   "outputs": [
-    {
-     "ename": "ValueError",
-     "evalue": "xmin must be a single scalar value, but got 0",
-     "output_type": "error",
-     "traceback": [
-      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
-      "\u001b[0;31mValueError\u001b[0m                                Traceback (most recent call last)",
-      "\u001b[0;32m<ipython-input-14-748f618dad05>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mret\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0max\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0maxhline\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
-      "\u001b[0;32m~/Documents/jupyter/matplotlib/lib/matplotlib/axes/_axes.py\u001b[0m in \u001b[0;36maxhline\u001b[0;34m(self, y, xmin, xmax, **kwargs)\u001b[0m\n\u001b[1;32m    703\u001b[0m             \u001b[0;34m>>\u001b[0m\u001b[0;34m>\u001b[0m \u001b[0maxhline\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0my\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m.5\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mxmin\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0.25\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mxmax\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0.75\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    704\u001b[0m         \"\"\"\n\u001b[0;32m--> 705\u001b[0;31m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_check_no_units\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mxmin\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mxmax\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m'xmin'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'xmax'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    706\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0;34m\"transform\"\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mkwargs\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    707\u001b[0m             raise ValueError(\"'transform' is not allowed as a keyword \"\n",
-      "\u001b[0;32m~/Documents/jupyter/matplotlib/lib/matplotlib/axes/_axes.py\u001b[0m in \u001b[0;36m_check_no_units\u001b[0;34m(vals, names)\u001b[0m\n\u001b[1;32m    791\u001b[0m         \u001b[0;32mfor\u001b[0m \u001b[0mval\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvals\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnames\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    792\u001b[0m             \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mmunits\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_is_natively_supported\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mval\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 793\u001b[0;31m                 raise ValueError(f\"{name} must be a single scalar value, \"\n\u001b[0m\u001b[1;32m    794\u001b[0m                                  f\"but got {val}\")\n\u001b[1;32m    795\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
-      "\u001b[0;31mValueError\u001b[0m: xmin must be a single scalar value, but got 0"
-     ]
-    }
-   ],
-   "source": [
-    "ret = ax.axhline(0,np.array(0),1)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 13,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "0"
-      ]
-     },
-     "execution_count": 13,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "arr = np.array(0)\n",
-    "arr.item()"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 12,
-   "metadata": {},
-   "outputs": [
-    {
-     "ename": "IndexError",
-     "evalue": "too many indices for array: array is 0-dimensional, but 1 were indexed",
-     "output_type": "error",
-     "traceback": [
-      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
-      "\u001b[0;31mIndexError\u001b[0m                                Traceback (most recent call last)",
-      "\u001b[0;32m<ipython-input-12-d3e0db857085>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0marr\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
-      "\u001b[0;31mIndexError\u001b[0m: too many indices for array: array is 0-dimensional, but 1 were indexed"
-     ]
-    }
-   ],
-   "source": [
-    "np.asscalar(arr)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 21,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "ret.set_xdata([0,1.5])"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 19,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "\u001b[0;31mSignature:\u001b[0m \u001b[0max\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0maxhline\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0my\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mxmin\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mxmax\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
-       "\u001b[0;31mDocstring:\u001b[0m\n",
-       "Add a horizontal line across the axis.\n",
-       "\n",
-       "Parameters\n",
-       "----------\n",
-       "y : float, default: 0\n",
-       "    y position in data coordinates of the horizontal line.\n",
-       "\n",
-       "xmin : float, default: 0\n",
-       "    Should be between 0 and 1, 0 being the far left of the plot, 1 the\n",
-       "    far right of the plot.\n",
-       "\n",
-       "xmax : float, default: 1\n",
-       "    Should be between 0 and 1, 0 being the far left of the plot, 1 the\n",
-       "    far right of the plot.\n",
-       "\n",
-       "Returns\n",
-       "-------\n",
-       "`~matplotlib.lines.Line2D`\n",
-       "\n",
-       "Other Parameters\n",
-       "----------------\n",
-       "**kwargs\n",
-       "    Valid keyword arguments are `.Line2D` properties, with the\n",
-       "    exception of 'transform':\n",
-       "\n",
-       "    Properties:\n",
-       "    agg_filter: a filter function, which takes a (m, n, 3) float array and a dpi value, and returns a (m, n, 3) array\n",
-       "    alpha: scalar or None\n",
-       "    animated: bool\n",
-       "    antialiased or aa: bool\n",
-       "    clip_box: `.Bbox`\n",
-       "    clip_on: bool\n",
-       "    clip_path: Patch or (Path, Transform) or None\n",
-       "    color or c: color\n",
-       "    contains: unknown\n",
-       "    dash_capstyle: {'butt', 'round', 'projecting'}\n",
-       "    dash_joinstyle: {'miter', 'round', 'bevel'}\n",
-       "    dashes: sequence of floats (on/off ink in points) or (None, None)\n",
-       "    data: (2, N) array or two 1D arrays\n",
-       "    drawstyle or ds: {'default', 'steps', 'steps-pre', 'steps-mid', 'steps-post'}, default: 'default'\n",
-       "    figure: `.Figure`\n",
-       "    fillstyle: {'full', 'left', 'right', 'bottom', 'top', 'none'}\n",
-       "    gid: str\n",
-       "    in_layout: bool\n",
-       "    label: object\n",
-       "    linestyle or ls: {'-', '--', '-.', ':', '', (offset, on-off-seq), ...}\n",
-       "    linewidth or lw: float\n",
-       "    marker: marker style string, `~.path.Path` or `~.markers.MarkerStyle`\n",
-       "    markeredgecolor or mec: color\n",
-       "    markeredgewidth or mew: float\n",
-       "    markerfacecolor or mfc: color\n",
-       "    markerfacecoloralt or mfcalt: color\n",
-       "    markersize or ms: float\n",
-       "    markevery: None or int or (int, int) or slice or List[int] or float or (float, float) or List[bool]\n",
-       "    path_effects: `.AbstractPathEffect`\n",
-       "    picker: unknown\n",
-       "    pickradius: float\n",
-       "    rasterized: bool or None\n",
-       "    sketch_params: (scale: float, length: float, randomness: float)\n",
-       "    snap: bool or None\n",
-       "    solid_capstyle: {'butt', 'round', 'projecting'}\n",
-       "    solid_joinstyle: {'miter', 'round', 'bevel'}\n",
-       "    transform: `matplotlib.transforms.Transform`\n",
-       "    url: str\n",
-       "    visible: bool\n",
-       "    xdata: 1D array\n",
-       "    ydata: 1D array\n",
-       "    zorder: float\n",
-       "\n",
-       "See Also\n",
-       "--------\n",
-       "hlines : Add horizontal lines in data coordinates.\n",
-       "axhspan : Add a horizontal span (rectangle) across the axis.\n",
-       "axline : Add a line with an arbitrary slope.\n",
-       "\n",
-       "Examples\n",
-       "--------\n",
-       "* draw a thick red hline at 'y' = 0 that spans the xrange::\n",
-       "\n",
-       "    >>> axhline(linewidth=4, color='r')\n",
-       "\n",
-       "* draw a default hline at 'y' = 1 that spans the xrange::\n",
-       "\n",
-       "    >>> axhline(y=1)\n",
-       "\n",
-       "* draw a default hline at 'y' = .5 that spans the middle half of\n",
-       "  the xrange::\n",
-       "\n",
-       "    >>> axhline(y=.5, xmin=0.25, xmax=0.75)\n",
-       "\u001b[0;31mSource:\u001b[0m   \n",
-       "    \u001b[0;34m@\u001b[0m\u001b[0mdocstring\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdedent_interpd\u001b[0m\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m    \u001b[0;32mdef\u001b[0m \u001b[0maxhline\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mxmin\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mxmax\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m        \u001b[0;34m\"\"\"\u001b[0m\n",
-       "\u001b[0;34m        Add a horizontal line across the axis.\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m        Parameters\u001b[0m\n",
-       "\u001b[0;34m        ----------\u001b[0m\n",
-       "\u001b[0;34m        y : float, default: 0\u001b[0m\n",
-       "\u001b[0;34m            y position in data coordinates of the horizontal line.\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m        xmin : float, default: 0\u001b[0m\n",
-       "\u001b[0;34m            Should be between 0 and 1, 0 being the far left of the plot, 1 the\u001b[0m\n",
-       "\u001b[0;34m            far right of the plot.\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m        xmax : float, default: 1\u001b[0m\n",
-       "\u001b[0;34m            Should be between 0 and 1, 0 being the far left of the plot, 1 the\u001b[0m\n",
-       "\u001b[0;34m            far right of the plot.\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m        Returns\u001b[0m\n",
-       "\u001b[0;34m        -------\u001b[0m\n",
-       "\u001b[0;34m        `~matplotlib.lines.Line2D`\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m        Other Parameters\u001b[0m\n",
-       "\u001b[0;34m        ----------------\u001b[0m\n",
-       "\u001b[0;34m        **kwargs\u001b[0m\n",
-       "\u001b[0;34m            Valid keyword arguments are `.Line2D` properties, with the\u001b[0m\n",
-       "\u001b[0;34m            exception of 'transform':\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m            %(_Line2D_docstr)s\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m        See Also\u001b[0m\n",
-       "\u001b[0;34m        --------\u001b[0m\n",
-       "\u001b[0;34m        hlines : Add horizontal lines in data coordinates.\u001b[0m\n",
-       "\u001b[0;34m        axhspan : Add a horizontal span (rectangle) across the axis.\u001b[0m\n",
-       "\u001b[0;34m        axline : Add a line with an arbitrary slope.\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m        Examples\u001b[0m\n",
-       "\u001b[0;34m        --------\u001b[0m\n",
-       "\u001b[0;34m        * draw a thick red hline at 'y' = 0 that spans the xrange::\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m            >>> axhline(linewidth=4, color='r')\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m        * draw a default hline at 'y' = 1 that spans the xrange::\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m            >>> axhline(y=1)\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m        * draw a default hline at 'y' = .5 that spans the middle half of\u001b[0m\n",
-       "\u001b[0;34m          the xrange::\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m            >>> axhline(y=.5, xmin=0.25, xmax=0.75)\u001b[0m\n",
-       "\u001b[0;34m        \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m        \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_check_no_units\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mxmin\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mxmax\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m'xmin'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'xmax'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m        \u001b[0;32mif\u001b[0m \u001b[0;34m\"transform\"\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mkwargs\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m            \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"'transform' is not allowed as a keyword \"\u001b[0m\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m                             \u001b[0;34m\"argument; axhline generates its own transform.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m        \u001b[0mymin\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mymax\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_ybound\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m        \u001b[0;31m# Strip away the units for comparison with non-unitized bounds.\u001b[0m\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m        \u001b[0myy\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_process_unit_info\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"y\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m        \u001b[0mscaley\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0myy\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0mymin\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0myy\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0mymax\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m        \u001b[0mtrans\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_yaxis_transform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mwhich\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'grid'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m        \u001b[0ml\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmlines\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mLine2D\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mxmin\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mxmax\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0my\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtransform\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mtrans\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m        \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_line\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ml\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m        \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_request_autoscale_view\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mscalex\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mscaley\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mscaley\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
-       "\u001b[0;34m\u001b[0m        \u001b[0;32mreturn\u001b[0m \u001b[0ml\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
-       "\u001b[0;31mFile:\u001b[0m      ~/Documents/jupyter/matplotlib/lib/matplotlib/axes/_axes.py\n",
-       "\u001b[0;31mType:\u001b[0m      method\n"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    }
-   ],
-   "source": [
-    "def axhline(y=0, xmin=0, xmax=1):\n",
-    "    "
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 13,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "[0, 1]"
-      ]
-     },
-     "execution_count": 13,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "ret.get_ydata()"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 14,
-   "metadata": {},
-   "outputs": [],
-   "source": []
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 15,
-   "metadata": {},
-   "outputs": [],
-   "source": []
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": []
-  }
- ],
- "metadata": {
-  "kernelspec": {
-   "display_name": "Python 3",
-   "language": "python",
-   "name": "python3"
-  },
-  "language_info": {
-   "codemirror_mode": {
-    "name": "ipython",
-    "version": 3
-   },
-   "file_extension": ".py",
-   "mimetype": "text/x-python",
-   "name": "python",
-   "nbconvert_exporter": "python",
-   "pygments_lexer": "ipython3",
-   "version": "3.8.5"
-  }
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
\ No newline at end of file
diff --git a/examples/heatmap-slicer.ipynb b/examples/heatmap-slicer.ipynb
deleted file mode 100644
index 6d6b5cc4..00000000
--- a/examples/heatmap-slicer.ipynb
+++ /dev/null
@@ -1,461 +0,0 @@
-{
- "cells": [
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "# Heatmap Slicer"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "%matplotlib widget\n",
-    "import matplotlib.pyplot as plt\n",
-    "import numpy as np\n",
-    "\n",
-    "from mpl_interactions import heatmap_slicer"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "## Comparing heatmaps\n",
-    "\n",
-    "Sometimes I find myself wanting to compare horizontal or vertical slices across two different heatmaps with the same shape. The function {func}`.heatmap_slicer` makes this easy and should work for any number of heatmaps from 1 to many (likely not all the way $\\infty$ though). \n",
-    "\n",
-    "The most important options to play with are:\n",
-    "- `slices`: `\"both\"`, `\"vertical\"`, or `\"horizontal\"`\n",
-    "- `interaction_type`: `\"move\"` or `\"click\"`"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "gif": "tight-layout-heatmap-slicer.gif"
-   },
-   "outputs": [],
-   "source": [
-    "x = np.linspace(0, np.pi, 100)\n",
-    "y = np.linspace(0, 10, 200)\n",
-    "X, Y = np.meshgrid(x, y)\n",
-    "data1 = np.sin(X) + np.exp(np.cos(Y))\n",
-    "data2 = np.cos(X) + np.exp(np.sin(Y))\n",
-    "fig, axes = heatmap_slicer(\n",
-    "    x,\n",
-    "    y,\n",
-    "    (data1, data2),\n",
-    "    slices=\"both\",\n",
-    "    heatmap_names=(\"dataset 1\", \"dataset 2\"),\n",
-    "    labels=(\"Some wild X variable\", \"Y axis\"),\n",
-    "    interaction_type=\"move\",\n",
-    ")"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": []
-  }
- ],
- "metadata": {
-  "kernelspec": {
-   "display_name": "Python 3",
-   "language": "python",
-   "name": "python3"
-  },
-  "language_info": {
-   "codemirror_mode": {
-    "name": "ipython",
-    "version": 3
-   },
-   "file_extension": ".py",
-   "mimetype": "text/x-python",
-   "name": "python",
-   "nbconvert_exporter": "python",
-   "pygments_lexer": "ipython3",
-   "version": "3.7.8"
-  },
-  "widgets": {
-   "application/vnd.jupyter.widget-state+json": {
-    "state": {
-     "01089f0a61ca4214a29a59abc12db8fa": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "071f5fd284394aaabb5a82ac42498a9a": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "1188013ad1af4b24b84c8ec65ecedc5b": {
-      "model_module": "jupyter-matplotlib",
-      "model_module_version": "^0.7.1",
-      "model_name": "ToolbarModel",
-      "state": {
-       "layout": "IPY_MODEL_721f8ce8b65e4f5888a696a336533c27",
-       "toolitems": [
-        [
-         "Home",
-         "Reset original view",
-         "home",
-         "home"
-        ],
-        [
-         "Back",
-         "Back to previous view",
-         "arrow-left",
-         "back"
-        ],
-        [
-         "Forward",
-         "Forward to next view",
-         "arrow-right",
-         "forward"
-        ],
-        [
-         "Pan",
-         "Pan axes with left mouse, zoom with right",
-         "arrows",
-         "pan"
-        ],
-        [
-         "Zoom",
-         "Zoom to rectangle",
-         "square-o",
-         "zoom"
-        ],
-        [
-         "Download",
-         "Download plot",
-         "floppy-o",
-         "save_figure"
-        ]
-       ]
-      }
-     },
-     "13126ef3af3e457db5b8e5e3da6a48ab": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "1347c00042e5454e8a504c94962d8096": {
-      "model_module": "jupyter-matplotlib",
-      "model_module_version": "^0.7.1",
-      "model_name": "MPLCanvasModel",
-      "state": {
-       "_figure_label": "Figure 4",
-       "_height": 899,
-       "_width": 1799,
-       "layout": "IPY_MODEL_ef3945e12d304cf39a9b5245d3f073f7",
-       "toolbar": "IPY_MODEL_acf3277ecc38448499fbd710ea1ab4d9",
-       "toolbar_position": "left"
-      }
-     },
-     "204dbbb0a02f4761b2ac63effe848d5c": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "2db56d505e9c4c8a860a8a4d0f22837e": {
-      "model_module": "jupyter-matplotlib",
-      "model_module_version": "^0.7.1",
-      "model_name": "MPLCanvasModel",
-      "state": {
-       "_cursor": "default",
-       "_figure_label": "Figure 2",
-       "_height": 899,
-       "_image_mode": "diff",
-       "_message": "zoom rect",
-       "_rubberband_x": -1,
-       "_rubberband_y": 900,
-       "_width": 1799,
-       "layout": "IPY_MODEL_4bef0958f5094b2ea624e1c93133e5c5",
-       "toolbar": "IPY_MODEL_e8a035657ae44c6d91c8c52daae8ebca",
-       "toolbar_position": "left"
-      }
-     },
-     "4a38a7c5e9ee47a1a685e0ca3621efe6": {
-      "model_module": "jupyter-matplotlib",
-      "model_module_version": "^0.7.1",
-      "model_name": "ToolbarModel",
-      "state": {
-       "layout": "IPY_MODEL_b2ac2d4e908c4920b9d4895c0c68d30c",
-       "toolitems": [
-        [
-         "Home",
-         "Reset original view",
-         "home",
-         "home"
-        ],
-        [
-         "Back",
-         "Back to previous view",
-         "arrow-left",
-         "back"
-        ],
-        [
-         "Forward",
-         "Forward to next view",
-         "arrow-right",
-         "forward"
-        ],
-        [
-         "Pan",
-         "Pan axes with left mouse, zoom with right",
-         "arrows",
-         "pan"
-        ],
-        [
-         "Zoom",
-         "Zoom to rectangle",
-         "square-o",
-         "zoom"
-        ],
-        [
-         "Download",
-         "Download plot",
-         "floppy-o",
-         "save_figure"
-        ]
-       ]
-      }
-     },
-     "4bef0958f5094b2ea624e1c93133e5c5": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "4c42a8bd630e46caba9ef47b8d301f97": {
-      "model_module": "jupyter-matplotlib",
-      "model_module_version": "^0.7.1",
-      "model_name": "MPLCanvasModel",
-      "state": {
-       "_figure_label": "Figure 3",
-       "_height": 899,
-       "_image_mode": "diff",
-       "_width": 1799,
-       "layout": "IPY_MODEL_01089f0a61ca4214a29a59abc12db8fa",
-       "toolbar": "IPY_MODEL_b3b0baf798764165bb8ef5cd6b2e7293",
-       "toolbar_position": "left"
-      }
-     },
-     "6ae28a8ad7354377b25eb81853c822ea": {
-      "model_module": "jupyter-matplotlib",
-      "model_module_version": "^0.7.1",
-      "model_name": "MPLCanvasModel",
-      "state": {
-       "_figure_label": "Figure 5",
-       "_height": 899,
-       "_image_mode": "diff",
-       "_width": 1799,
-       "layout": "IPY_MODEL_7b95b5427fcf4ea1b5e1287b6efa1038",
-       "toolbar": "IPY_MODEL_1188013ad1af4b24b84c8ec65ecedc5b",
-       "toolbar_position": "left"
-      }
-     },
-     "721f8ce8b65e4f5888a696a336533c27": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "7b95b5427fcf4ea1b5e1287b6efa1038": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "acf3277ecc38448499fbd710ea1ab4d9": {
-      "model_module": "jupyter-matplotlib",
-      "model_module_version": "^0.7.1",
-      "model_name": "ToolbarModel",
-      "state": {
-       "layout": "IPY_MODEL_204dbbb0a02f4761b2ac63effe848d5c",
-       "toolitems": [
-        [
-         "Home",
-         "Reset original view",
-         "home",
-         "home"
-        ],
-        [
-         "Back",
-         "Back to previous view",
-         "arrow-left",
-         "back"
-        ],
-        [
-         "Forward",
-         "Forward to next view",
-         "arrow-right",
-         "forward"
-        ],
-        [
-         "Pan",
-         "Pan axes with left mouse, zoom with right",
-         "arrows",
-         "pan"
-        ],
-        [
-         "Zoom",
-         "Zoom to rectangle",
-         "square-o",
-         "zoom"
-        ],
-        [
-         "Download",
-         "Download plot",
-         "floppy-o",
-         "save_figure"
-        ]
-       ]
-      }
-     },
-     "b2ac2d4e908c4920b9d4895c0c68d30c": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "b36e1d0241314f948d674d2a620b9d00": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "b3b0baf798764165bb8ef5cd6b2e7293": {
-      "model_module": "jupyter-matplotlib",
-      "model_module_version": "^0.7.1",
-      "model_name": "ToolbarModel",
-      "state": {
-       "layout": "IPY_MODEL_071f5fd284394aaabb5a82ac42498a9a",
-       "toolitems": [
-        [
-         "Home",
-         "Reset original view",
-         "home",
-         "home"
-        ],
-        [
-         "Back",
-         "Back to previous view",
-         "arrow-left",
-         "back"
-        ],
-        [
-         "Forward",
-         "Forward to next view",
-         "arrow-right",
-         "forward"
-        ],
-        [
-         "Pan",
-         "Pan axes with left mouse, zoom with right",
-         "arrows",
-         "pan"
-        ],
-        [
-         "Zoom",
-         "Zoom to rectangle",
-         "square-o",
-         "zoom"
-        ],
-        [
-         "Download",
-         "Download plot",
-         "floppy-o",
-         "save_figure"
-        ]
-       ]
-      }
-     },
-     "bdec589a3dd9441786c7a7cef6fa351e": {
-      "model_module": "jupyter-matplotlib",
-      "model_module_version": "^0.7.1",
-      "model_name": "MPLCanvasModel",
-      "state": {
-       "_figure_label": "Figure 1",
-       "_height": 899,
-       "_image_mode": "diff",
-       "_width": 1799,
-       "layout": "IPY_MODEL_13126ef3af3e457db5b8e5e3da6a48ab",
-       "toolbar": "IPY_MODEL_4a38a7c5e9ee47a1a685e0ca3621efe6",
-       "toolbar_position": "left"
-      }
-     },
-     "e8a035657ae44c6d91c8c52daae8ebca": {
-      "model_module": "jupyter-matplotlib",
-      "model_module_version": "^0.7.1",
-      "model_name": "ToolbarModel",
-      "state": {
-       "_current_action": "zoom",
-       "layout": "IPY_MODEL_b36e1d0241314f948d674d2a620b9d00",
-       "toolitems": [
-        [
-         "Home",
-         "Reset original view",
-         "home",
-         "home"
-        ],
-        [
-         "Back",
-         "Back to previous view",
-         "arrow-left",
-         "back"
-        ],
-        [
-         "Forward",
-         "Forward to next view",
-         "arrow-right",
-         "forward"
-        ],
-        [
-         "Pan",
-         "Pan axes with left mouse, zoom with right",
-         "arrows",
-         "pan"
-        ],
-        [
-         "Zoom",
-         "Zoom to rectangle",
-         "square-o",
-         "zoom"
-        ],
-        [
-         "Download",
-         "Download plot",
-         "floppy-o",
-         "save_figure"
-        ]
-       ]
-      }
-     },
-     "ef3945e12d304cf39a9b5245d3f073f7": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     }
-    },
-    "version_major": 2,
-    "version_minor": 0
-   }
-  }
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
diff --git a/examples/tidbits/play-buttons.ipynb b/examples/tidbits/play-buttons.ipynb
deleted file mode 100644
index c11ad7ff..00000000
--- a/examples/tidbits/play-buttons.ipynb
+++ /dev/null
@@ -1,1862 +0,0 @@
-{
- "cells": [
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "# Attaching play buttons to sliders\n",
-    "\n",
-    ":::{note}\n",
-    "\n",
-    "The labels will not update as that requires a Python kernel.\n",
-    "\n",
-    ":::\n",
-    "\n",
-    "If you are working in Jupyter then you can add an [`ipywidgets.Play`](https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20List.html#Play-(Animation)-widget) widget to the sliders for any of the `interactive_*` functions.\n",
-    "\n",
-    "In this tutorial all the functions are {func}`~matplotlib.pyplot.scatter` but this will work for {func}`~matplotlib.pyplot.plot`, {func}`~matplotlib.pyplot.hist`, {func}`~matplotlib.pyplot.imshow`, etc..."
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "## Specifying Which Sliders Get Play buttons"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "%matplotlib ipympl\n",
-    "import matplotlib.pyplot as plt\n",
-    "import numpy as np\n",
-    "\n",
-    "import mpl_interactions.ipyplot as iplt\n",
-    "\n",
-    "# turn off interactive mode so that broken\n",
-    "# plots don't render in the docs\n",
-    "plt.ioff()"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "### {obj}`bool`: All get a button"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "N = 50\n",
-    "x = np.random.rand(N)\n",
-    "\n",
-    "\n",
-    "def f_y(x, tau, beta):\n",
-    "    return np.sin(x * tau) ** 2 + np.random.randn(N) * 0.01 * beta\n",
-    "\n",
-    "\n",
-    "fig, ax = plt.subplots()\n",
-    "controls = iplt.scatter(x, f_y, tau=(1, 2 * np.pi, 100), beta=(0, 2), play_buttons=True)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "### {obj}`str`: Position the button\n",
-    "You make a play button for all sliders on either the left or the right using a string argument."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "fig, ax = plt.subplots()\n",
-    "controls = iplt.scatter(\n",
-    "    x, f_y, tau=(1, 2 * np.pi, 100), beta=(0, 2), play_buttons=\"left\"\n",
-    ")"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "fig, ax = plt.subplots()\n",
-    "controls = iplt.scatter(\n",
-    "    x, f_y, tau=(1, 2 * np.pi, 100), beta=(0, 2), play_buttons=\"right\"\n",
-    ")"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "### {obj}`list`: Choose by name"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "fig, ax = plt.subplots()\n",
-    "controls = iplt.scatter(\n",
-    "    x, f_y, tau=(1, 2 * np.pi, 100), beta=(0, 2), play_buttons=[\"tau\"]\n",
-    ")"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "### {obj}`~collections.defaultdict`: Specify by name and choose default\n",
-    "\n",
-    "If you have many parameters and you want the most, but not all, of them to have a Play button then\n",
-    "you should use a {obj}`~collections.defaultdict`:"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "from collections import defaultdict\n",
-    "\n",
-    "\n",
-    "def f(x, **kwargs):\n",
-    "    return x\n",
-    "\n",
-    "\n",
-    "play_buttons = defaultdict(lambda: True)\n",
-    "play_buttons[\"tau\"] = False\n",
-    "fig, ax = plt.subplots()\n",
-    "controls = iplt.scatter(\n",
-    "    x,\n",
-    "    f,\n",
-    "    tau=(1, 2 * np.pi, 100),\n",
-    "    beta=(0, 2),\n",
-    "    zeta=(0, 1),\n",
-    "    psi=(0, 1),\n",
-    "    play_buttons=play_buttons,\n",
-    ")"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": []
-  }
- ],
- "metadata": {
-  "kernelspec": {
-   "display_name": "Python 3 (ipykernel)",
-   "language": "python",
-   "name": "python3"
-  },
-  "language_info": {
-   "codemirror_mode": {
-    "name": "ipython",
-    "version": 3
-   },
-   "file_extension": ".py",
-   "mimetype": "text/x-python",
-   "name": "python",
-   "nbconvert_exporter": "python",
-   "pygments_lexer": "ipython3",
-   "version": "3.9.5"
-  },
-  "widgets": {
-   "application/vnd.jupyter.widget-state+json": {
-    "state": {
-     "00bc48d724aa438ab87463337e0f7eaf": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "0179bb5130c9469f8f7e1e8799d837c5": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "DescriptionStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "063865096bce4c3eabd7d67f9ae6f602": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "PlayModel",
-      "state": {
-       "layout": "IPY_MODEL_eb601277028748be867ac124794ef32a",
-       "max": 49,
-       "style": "IPY_MODEL_8772edca5d8f4879a7e5bad179dbb465"
-      }
-     },
-     "074b61d9268f4c4393adab67e92ea873": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "09c5da8a71b6456fb3060a3e646436ad": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "0c4dc281a4624366825d33d4d018f06c": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "0db9c1982fec463d8b9dc9c6f586eef0": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "DescriptionStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "0e8b03a19d95457a800e5fd4d2053393": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "DescriptionStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "0f6bdc75f1f143a28bc018529a767e3f": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "SliderStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "12ed86c99bae48f5a92c578d8f7c4516": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "DescriptionStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "13b486847ac9486ebf2a890cd352da66": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "147ed24f6815419581f7bc7beb793bae": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "1488b63d92c647039da0fbc329e7cc0b": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "HBoxModel",
-      "state": {
-       "children": [
-        "IPY_MODEL_f68c476cf3e549c49e8866d85fca539e",
-        "IPY_MODEL_93229abee42d4dbc843c0822fdb8be67",
-        "IPY_MODEL_e5a6bd63bc9e483c8fe0e973e9477990"
-       ],
-       "layout": "IPY_MODEL_00bc48d724aa438ab87463337e0f7eaf"
-      }
-     },
-     "173cf59534d24e76984cc1c97a498d27": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "SliderStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "1af62bb653b44eb4b877430dbcf37369": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "DescriptionStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "1d74805bf6674346b43248b6089ae3a2": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "LinkModel",
-      "state": {
-       "source": [
-        "IPY_MODEL_9f42150ad69e4fcabf9116e6bf63c5f3",
-        "value"
-       ],
-       "target": [
-        "IPY_MODEL_972b7b715a38435ea4f2e643e33370ae",
-        "value"
-       ]
-      }
-     },
-     "1e1aa4ea38ed4f55b86b0a2ff8b629f3": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "1e500a68776e4fc0bcf600a8c5f53830": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "2097e7ad60c34a4692be17b594adf121": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "209e0565eee047a1b2aec9a08130de30": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "20c3bfbec9a043fa82d7331d3f10a9d8": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "2254583091644de4a80cdcccb4c08583": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "SliderStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "22d5dff341384802975183b850a33948": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "23d9f2c257e14ee8bdc8dee4cf34cc56": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "SliderStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "28612cef33384b46a56dacc99c85cab2": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "292627faee79458b8b8681b01af7b58e": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "HBoxModel",
-      "state": {
-       "children": [
-        "IPY_MODEL_b86e5eb431f2444d988c1e0164e0a158",
-        "IPY_MODEL_7094feae581d4ad39372e87e0529d5a0",
-        "IPY_MODEL_bb7a3e7f48de4d06b8d3081be8c87ddd"
-       ],
-       "layout": "IPY_MODEL_a2ce4c83e95642f58879ded368545f27"
-      }
-     },
-     "2a25dcc2cd854baab048c1f66a496d8b": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "2a3d0d9d086d44ab8cbafbba96ff8b3e": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "2b312c52b2454ab0bfbb479141f34a8f": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "IntSliderModel",
-      "state": {
-       "description": "tau",
-       "layout": "IPY_MODEL_28612cef33384b46a56dacc99c85cab2",
-       "max": 99,
-       "readout": false,
-       "style": "IPY_MODEL_23d9f2c257e14ee8bdc8dee4cf34cc56"
-      }
-     },
-     "2e1d18f104f942c88f2e67f8357dd7e0": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "LabelModel",
-      "state": {
-       "layout": "IPY_MODEL_71ae53f85b464f91a8b74e021979331b",
-       "style": "IPY_MODEL_0e8b03a19d95457a800e5fd4d2053393",
-       "value": "0.00"
-      }
-     },
-     "2e3b9925832c4f16aa932f952f9f0e07": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "DescriptionStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "2f2a0039685c4a67863065d23816d6ca": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "3055b3ed151d413d8b9735ab885cc290": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "336cf6ddc05047909cfb90bb6870dc2b": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "34a7eac0c50e4af6983ea87de0950492": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "PlayModel",
-      "state": {
-       "layout": "IPY_MODEL_074b61d9268f4c4393adab67e92ea873",
-       "max": 99,
-       "style": "IPY_MODEL_6d2fd048efe04cbd922eb22353a09690"
-      }
-     },
-     "34c2110420714db5ba37dc6580359aab": {
-      "model_module": "jupyter-matplotlib",
-      "model_module_version": "^0.7.4",
-      "model_name": "ToolbarModel",
-      "state": {
-       "layout": "IPY_MODEL_2a3d0d9d086d44ab8cbafbba96ff8b3e",
-       "toolitems": [
-        [
-         "Home",
-         "Reset original view",
-         "home",
-         "home"
-        ],
-        [
-         "Back",
-         "Back to previous view",
-         "arrow-left",
-         "back"
-        ],
-        [
-         "Forward",
-         "Forward to next view",
-         "arrow-right",
-         "forward"
-        ],
-        [
-         "Pan",
-         "Left button pans, Right button zooms\nx/y fixes axis, CTRL fixes aspect",
-         "arrows",
-         "pan"
-        ],
-        [
-         "Zoom",
-         "Zoom to rectangle\nx/y fixes axis, CTRL fixes aspect",
-         "square-o",
-         "zoom"
-        ],
-        [
-         "Download",
-         "Download plot",
-         "floppy-o",
-         "save_figure"
-        ]
-       ]
-      }
-     },
-     "34f2c427451e4ba8b2246599b48e768b": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "HBoxModel",
-      "state": {
-       "children": [
-        "IPY_MODEL_d442844b13254079b92e172bd06bf992",
-        "IPY_MODEL_ee66bfbfc84f4f678a4c0e004e85d037"
-       ],
-       "layout": "IPY_MODEL_7536b106b60f40c9a432bd91e396c129"
-      }
-     },
-     "367e88248d444ed595826e78c8438423": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "LabelModel",
-      "state": {
-       "layout": "IPY_MODEL_5951f4f41fc7482c9787d4951a82c2e4",
-       "style": "IPY_MODEL_e6d2cef1df63469eb09ab1cd00a2755f",
-       "value": "0.00"
-      }
-     },
-     "385b94b75d074004a90c245122bdb133": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "LabelModel",
-      "state": {
-       "layout": "IPY_MODEL_e62faf79ecab47ccaa1a896a9b09e52d",
-       "style": "IPY_MODEL_ceaf5ab5d836493e9578f0c4477c95a6",
-       "value": "0.00"
-      }
-     },
-     "45495b0d2edc4c23992bc50dcf4601ce": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "VBoxModel",
-      "state": {
-       "children": [
-        "IPY_MODEL_fdab149e5d5f472b9a733a771e6ce9f2",
-        "IPY_MODEL_1488b63d92c647039da0fbc329e7cc0b"
-       ],
-       "layout": "IPY_MODEL_a08069d3e2da40e7b4daa6554a63fa91"
-      }
-     },
-     "46069f8dd83e41f6be1d1bd9e5358fa6": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "HBoxModel",
-      "state": {
-       "children": [
-        "IPY_MODEL_dcf363d524924e78ac5a280145472fb7",
-        "IPY_MODEL_c871a2fcda354f56b75bb4d99a90d6c7",
-        "IPY_MODEL_bbf53d024b3f4f828f29c6c0299243f6"
-       ],
-       "layout": "IPY_MODEL_09c5da8a71b6456fb3060a3e646436ad"
-      }
-     },
-     "46858834f29b4f41ad8070ef912a5f7f": {
-      "model_module": "jupyter-matplotlib",
-      "model_module_version": "^0.7.4",
-      "model_name": "ToolbarModel",
-      "state": {
-       "layout": "IPY_MODEL_6d42f8aaa2f54b199e1355efbfbf9ba4",
-       "toolitems": [
-        [
-         "Home",
-         "Reset original view",
-         "home",
-         "home"
-        ],
-        [
-         "Back",
-         "Back to previous view",
-         "arrow-left",
-         "back"
-        ],
-        [
-         "Forward",
-         "Forward to next view",
-         "arrow-right",
-         "forward"
-        ],
-        [
-         "Pan",
-         "Left button pans, Right button zooms\nx/y fixes axis, CTRL fixes aspect",
-         "arrows",
-         "pan"
-        ],
-        [
-         "Zoom",
-         "Zoom to rectangle\nx/y fixes axis, CTRL fixes aspect",
-         "square-o",
-         "zoom"
-        ],
-        [
-         "Download",
-         "Download plot",
-         "floppy-o",
-         "save_figure"
-        ]
-       ]
-      }
-     },
-     "4aac4f69f7184766a41ae127a87469e8": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "4b0b2c56f1b3459f92d386d93821cf26": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "LabelModel",
-      "state": {
-       "layout": "IPY_MODEL_8ef256ba15fc4af1881aa8d52cbda203",
-       "style": "IPY_MODEL_d4ffdc99ce9843489b5234ada4605c6a",
-       "value": "1.00"
-      }
-     },
-     "4c25880c3434470a8bd5ef15b311a1fd": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "4d6863ed1c4d42f0a3361aa2b3cb66ae": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "IntSliderModel",
-      "state": {
-       "description": "psi",
-       "layout": "IPY_MODEL_0c4dc281a4624366825d33d4d018f06c",
-       "max": 49,
-       "readout": false,
-       "style": "IPY_MODEL_c7c475200a4347dab3744ae01d8cab5c"
-      }
-     },
-     "4dfd079772d5408a90243977c696892d": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "DescriptionStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "506ea9b948d64ee88d3baeea1115289a": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "50a7491fb4794e178029c5b19534969c": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "DescriptionStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "55b3c883757e473097c56be608654e37": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "PlayModel",
-      "state": {
-       "layout": "IPY_MODEL_e0f31fe391f44beb8d5db7ffcfffe74c",
-       "max": 49,
-       "style": "IPY_MODEL_f48b7a250b21444f920fc06fd8611729"
-      }
-     },
-     "55dc31dbd76e44e1b443b80229aebb6d": {
-      "model_module": "jupyter-matplotlib",
-      "model_module_version": "^0.7.4",
-      "model_name": "ToolbarModel",
-      "state": {
-       "layout": "IPY_MODEL_7e0bb89baf694a088495bdf469c67fc8",
-       "toolitems": [
-        [
-         "Home",
-         "Reset original view",
-         "home",
-         "home"
-        ],
-        [
-         "Back",
-         "Back to previous view",
-         "arrow-left",
-         "back"
-        ],
-        [
-         "Forward",
-         "Forward to next view",
-         "arrow-right",
-         "forward"
-        ],
-        [
-         "Pan",
-         "Left button pans, Right button zooms\nx/y fixes axis, CTRL fixes aspect",
-         "arrows",
-         "pan"
-        ],
-        [
-         "Zoom",
-         "Zoom to rectangle\nx/y fixes axis, CTRL fixes aspect",
-         "square-o",
-         "zoom"
-        ],
-        [
-         "Download",
-         "Download plot",
-         "floppy-o",
-         "save_figure"
-        ]
-       ]
-      }
-     },
-     "576038d7322641f3827852d0b63e73c0": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "580da8ff8f0945bea2f67b3a9c1f9a1a": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "593fa6027a4c4a86bf762848f48bd51c": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "LinkModel",
-      "state": {
-       "source": [
-        "IPY_MODEL_bb7a3e7f48de4d06b8d3081be8c87ddd",
-        "value"
-       ],
-       "target": [
-        "IPY_MODEL_b86e5eb431f2444d988c1e0164e0a158",
-        "value"
-       ]
-      }
-     },
-     "5951f4f41fc7482c9787d4951a82c2e4": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "5bb45121d6464c7097363c25efe98cf9": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "5befd79e7c504e37862c87cc7b630bce": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "62b5443b13ea4cdb831ba8b342723277": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "6442aae31ff3455eb26e79975e693a57": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "SliderStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "653fc987e3b645cfa86042507f926d2c": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "VBoxModel",
-      "state": {
-       "children": [
-        "IPY_MODEL_9250b441f643489f844e6038f6fe32b0",
-        "IPY_MODEL_46069f8dd83e41f6be1d1bd9e5358fa6"
-       ],
-       "layout": "IPY_MODEL_580da8ff8f0945bea2f67b3a9c1f9a1a"
-      }
-     },
-     "66896af0a8ba481887962146682a7798": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "IntSliderModel",
-      "state": {
-       "description": "tau",
-       "layout": "IPY_MODEL_13b486847ac9486ebf2a890cd352da66",
-       "max": 99,
-       "readout": false,
-       "style": "IPY_MODEL_d513e060ff3640c080dd3db73f2606e8"
-      }
-     },
-     "6d2fd048efe04cbd922eb22353a09690": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "DescriptionStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "6d42f8aaa2f54b199e1355efbfbf9ba4": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "6d97ab7e653145f4a792eb0019c1b04a": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "LabelModel",
-      "state": {
-       "layout": "IPY_MODEL_147ed24f6815419581f7bc7beb793bae",
-       "style": "IPY_MODEL_0179bb5130c9469f8f7e1e8799d837c5",
-       "value": "1.00"
-      }
-     },
-     "6e6f728142a74b56a31964818638a450": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "LinkModel",
-      "state": {
-       "source": [
-        "IPY_MODEL_fb2486a24a254d7699aa8deab9aab84c",
-        "value"
-       ],
-       "target": [
-        "IPY_MODEL_cef0fa3a198748eeaf798316a99b12e6",
-        "value"
-       ]
-      }
-     },
-     "7094feae581d4ad39372e87e0529d5a0": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "LabelModel",
-      "state": {
-       "layout": "IPY_MODEL_f86b6435058b44149420ba354a8c905f",
-       "style": "IPY_MODEL_1af62bb653b44eb4b877430dbcf37369",
-       "value": "0.00"
-      }
-     },
-     "71ae53f85b464f91a8b74e021979331b": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "721c989b369d426e8611d8919b02e302": {
-      "model_module": "jupyter-matplotlib",
-      "model_module_version": "^0.7.4",
-      "model_name": "ToolbarModel",
-      "state": {
-       "layout": "IPY_MODEL_c957c09849a44aef8c283e84178d05b7",
-       "toolitems": [
-        [
-         "Home",
-         "Reset original view",
-         "home",
-         "home"
-        ],
-        [
-         "Back",
-         "Back to previous view",
-         "arrow-left",
-         "back"
-        ],
-        [
-         "Forward",
-         "Forward to next view",
-         "arrow-right",
-         "forward"
-        ],
-        [
-         "Pan",
-         "Left button pans, Right button zooms\nx/y fixes axis, CTRL fixes aspect",
-         "arrows",
-         "pan"
-        ],
-        [
-         "Zoom",
-         "Zoom to rectangle\nx/y fixes axis, CTRL fixes aspect",
-         "square-o",
-         "zoom"
-        ],
-        [
-         "Download",
-         "Download plot",
-         "floppy-o",
-         "save_figure"
-        ]
-       ]
-      }
-     },
-     "725e98d548db484394aa16bd063ef6f8": {
-      "model_module": "jupyter-matplotlib",
-      "model_module_version": "^0.7.4",
-      "model_name": "MPLCanvasModel",
-      "state": {
-       "_cursor": "default",
-       "_figure_label": "Figure 2",
-       "_height": 480,
-       "_width": 640,
-       "layout": "IPY_MODEL_dcf043c1f4184e689caf11fc9b84427d",
-       "toolbar": "IPY_MODEL_46858834f29b4f41ad8070ef912a5f7f",
-       "toolbar_position": "left"
-      }
-     },
-     "7536b106b60f40c9a432bd91e396c129": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "77c55c42cdce43e58d4027e6995bde2c": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "78bb2ca2bf3f4e4093b64b0d8d25002b": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "SliderStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "7906c14cb4744c3e928973f4db2e0c4f": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "79e7bd8d9c4547ca96bd8f62aa6e352c": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "SliderStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "7a9e7a3dcec249d1a40578d1aebb4508": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "7ab41e4a17144d25942471e52077b5fd": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "SliderStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "7ad2d65aa8744b9e8f9d68524246ad33": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "7b319c13a2b74a79945444134a8cdf05": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "LabelModel",
-      "state": {
-       "layout": "IPY_MODEL_c9ca522bd6664d2e943063c2c1bb778c",
-       "style": "IPY_MODEL_b95a8941e55343d7b71f01ab369ef802",
-       "value": "1.00"
-      }
-     },
-     "7c2c56b5c40c48ad9adaa03926a334ae": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "LinkModel",
-      "state": {
-       "source": [
-        "IPY_MODEL_f68c476cf3e549c49e8866d85fca539e",
-        "value"
-       ],
-       "target": [
-        "IPY_MODEL_93229abee42d4dbc843c0822fdb8be67",
-        "value"
-       ]
-      }
-     },
-     "7e0bb89baf694a088495bdf469c67fc8": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "7f6a223068844722bc137c06282eebbc": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "IntSliderModel",
-      "state": {
-       "description": "beta",
-       "layout": "IPY_MODEL_3055b3ed151d413d8b9735ab885cc290",
-       "max": 49,
-       "readout": false,
-       "style": "IPY_MODEL_173cf59534d24e76984cc1c97a498d27"
-      }
-     },
-     "7fa98aae876442ed90de91dfc5b25d6a": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "VBoxModel",
-      "state": {
-       "children": [
-        "IPY_MODEL_34f2c427451e4ba8b2246599b48e768b",
-        "IPY_MODEL_acb89caef7284ac280bffd46eb7efb1d",
-        "IPY_MODEL_941a1ccb283744b5b45f551fc13ee125",
-        "IPY_MODEL_e87eab4366304228966e81912efe6165"
-       ],
-       "layout": "IPY_MODEL_7ad2d65aa8744b9e8f9d68524246ad33"
-      }
-     },
-     "80c5135322724c618d0d4917c94ea160": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "81b098ba89fb495297a29bcd9fa63552": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "826f79d4c152428598bd068aa6a75458": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "82c1f469e60c4515a86a9ba35d9cbbac": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "LinkModel",
-      "state": {
-       "source": [
-        "IPY_MODEL_34a7eac0c50e4af6983ea87de0950492",
-        "value"
-       ],
-       "target": [
-        "IPY_MODEL_c08c87cf7f1647428136fd31634de388",
-        "value"
-       ]
-      }
-     },
-     "8693a8c89ca84e7fa7ac41be929c1c96": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "HBoxModel",
-      "state": {
-       "children": [
-        "IPY_MODEL_d1bb25426654463294285194c18f977d",
-        "IPY_MODEL_367e88248d444ed595826e78c8438423"
-       ],
-       "layout": "IPY_MODEL_22d5dff341384802975183b850a33948"
-      }
-     },
-     "8772edca5d8f4879a7e5bad179dbb465": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "DescriptionStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "8bc74e78176b4425bf54101365df7399": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "LinkModel",
-      "state": {
-       "source": [
-        "IPY_MODEL_c4b47c5e8f42426fb265ae90be2c9727",
-        "value"
-       ],
-       "target": [
-        "IPY_MODEL_2b312c52b2454ab0bfbb479141f34a8f",
-        "value"
-       ]
-      }
-     },
-     "8dd23a485a314a169b8f202e69b8018b": {
-      "model_module": "jupyter-matplotlib",
-      "model_module_version": "^0.7.4",
-      "model_name": "MPLCanvasModel",
-      "state": {
-       "_cursor": "default",
-       "_figure_label": "Figure 5",
-       "_height": 480,
-       "_width": 640,
-       "layout": "IPY_MODEL_506ea9b948d64ee88d3baeea1115289a",
-       "toolbar": "IPY_MODEL_ba00a98f33c0421eb2eed69306d5ddcd",
-       "toolbar_position": "left"
-      }
-     },
-     "8ef256ba15fc4af1881aa8d52cbda203": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "8f700c21bca44990bc79ebd7c26544ac": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "922918b3a46440caba07dc5d0414478a": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "SliderStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "9250b441f643489f844e6038f6fe32b0": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "HBoxModel",
-      "state": {
-       "children": [
-        "IPY_MODEL_d2fca53ae566481eb92918dbea6736a7",
-        "IPY_MODEL_66896af0a8ba481887962146682a7798",
-        "IPY_MODEL_4b0b2c56f1b3459f92d386d93821cf26"
-       ],
-       "layout": "IPY_MODEL_77c55c42cdce43e58d4027e6995bde2c"
-      }
-     },
-     "93229abee42d4dbc843c0822fdb8be67": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "IntSliderModel",
-      "state": {
-       "description": "beta",
-       "layout": "IPY_MODEL_da486abb36db4e938ab10b860c8cd95c",
-       "max": 49,
-       "readout": false,
-       "style": "IPY_MODEL_7ab41e4a17144d25942471e52077b5fd"
-      }
-     },
-     "940808fbccb540738dd59dc6cc111821": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "LinkModel",
-      "state": {
-       "source": [
-        "IPY_MODEL_55b3c883757e473097c56be608654e37",
-        "value"
-       ],
-       "target": [
-        "IPY_MODEL_7f6a223068844722bc137c06282eebbc",
-        "value"
-       ]
-      }
-     },
-     "941a1ccb283744b5b45f551fc13ee125": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "HBoxModel",
-      "state": {
-       "children": [
-        "IPY_MODEL_9f42150ad69e4fcabf9116e6bf63c5f3",
-        "IPY_MODEL_972b7b715a38435ea4f2e643e33370ae",
-        "IPY_MODEL_385b94b75d074004a90c245122bdb133"
-       ],
-       "layout": "IPY_MODEL_5befd79e7c504e37862c87cc7b630bce"
-      }
-     },
-     "972b7b715a38435ea4f2e643e33370ae": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "IntSliderModel",
-      "state": {
-       "description": "zeta",
-       "layout": "IPY_MODEL_2097e7ad60c34a4692be17b594adf121",
-       "max": 49,
-       "readout": false,
-       "style": "IPY_MODEL_0f6bdc75f1f143a28bc018529a767e3f"
-      }
-     },
-     "9d442f5afced48358f8e6440731e6c9e": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "9d8aabb9f74d4000836fc2b55975dda3": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "9da77a70252e496cbae576ee5f5f2a37": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "9f42150ad69e4fcabf9116e6bf63c5f3": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "PlayModel",
-      "state": {
-       "layout": "IPY_MODEL_4c25880c3434470a8bd5ef15b311a1fd",
-       "max": 49,
-       "style": "IPY_MODEL_2e3b9925832c4f16aa932f952f9f0e07"
-      }
-     },
-     "a08069d3e2da40e7b4daa6554a63fa91": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "a0a62b9b25fb4210b1a9f684a83810ac": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "LinkModel",
-      "state": {
-       "source": [
-        "IPY_MODEL_dcf363d524924e78ac5a280145472fb7",
-        "value"
-       ],
-       "target": [
-        "IPY_MODEL_c871a2fcda354f56b75bb4d99a90d6c7",
-        "value"
-       ]
-      }
-     },
-     "a2ce4c83e95642f58879ded368545f27": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "acb89caef7284ac280bffd46eb7efb1d": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "HBoxModel",
-      "state": {
-       "children": [
-        "IPY_MODEL_55b3c883757e473097c56be608654e37",
-        "IPY_MODEL_7f6a223068844722bc137c06282eebbc",
-        "IPY_MODEL_2e1d18f104f942c88f2e67f8357dd7e0"
-       ],
-       "layout": "IPY_MODEL_fee5501fdac240ebbf99c2fdaeab9636"
-      }
-     },
-     "ad6a6cab03d942468da0e064754e8165": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "HBoxModel",
-      "state": {
-       "children": [
-        "IPY_MODEL_c4b47c5e8f42426fb265ae90be2c9727",
-        "IPY_MODEL_2b312c52b2454ab0bfbb479141f34a8f",
-        "IPY_MODEL_7b319c13a2b74a79945444134a8cdf05"
-       ],
-       "layout": "IPY_MODEL_576038d7322641f3827852d0b63e73c0"
-      }
-     },
-     "add98409108844a9a2044222f40c93da": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "DescriptionStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "aef8b512377a48158b3be378696fee88": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "LinkModel",
-      "state": {
-       "source": [
-        "IPY_MODEL_d2fca53ae566481eb92918dbea6736a7",
-        "value"
-       ],
-       "target": [
-        "IPY_MODEL_66896af0a8ba481887962146682a7798",
-        "value"
-       ]
-      }
-     },
-     "b0843b7ef1df481cb02d43b7b21d8490": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "VBoxModel",
-      "state": {
-       "children": [
-        "IPY_MODEL_e0aed347f4d64594820933534659c93b",
-        "IPY_MODEL_292627faee79458b8b8681b01af7b58e"
-       ],
-       "layout": "IPY_MODEL_4aac4f69f7184766a41ae127a87469e8"
-      }
-     },
-     "b3dcdeeb58424c3098ab27090143f9d4": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "b86e5eb431f2444d988c1e0164e0a158": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "IntSliderModel",
-      "state": {
-       "description": "beta",
-       "layout": "IPY_MODEL_1e1aa4ea38ed4f55b86b0a2ff8b629f3",
-       "max": 49,
-       "readout": false,
-       "style": "IPY_MODEL_6442aae31ff3455eb26e79975e693a57"
-      }
-     },
-     "b95a8941e55343d7b71f01ab369ef802": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "DescriptionStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "ba00a98f33c0421eb2eed69306d5ddcd": {
-      "model_module": "jupyter-matplotlib",
-      "model_module_version": "^0.7.4",
-      "model_name": "ToolbarModel",
-      "state": {
-       "layout": "IPY_MODEL_8f700c21bca44990bc79ebd7c26544ac",
-       "toolitems": [
-        [
-         "Home",
-         "Reset original view",
-         "home",
-         "home"
-        ],
-        [
-         "Back",
-         "Back to previous view",
-         "arrow-left",
-         "back"
-        ],
-        [
-         "Forward",
-         "Forward to next view",
-         "arrow-right",
-         "forward"
-        ],
-        [
-         "Pan",
-         "Left button pans, Right button zooms\nx/y fixes axis, CTRL fixes aspect",
-         "arrows",
-         "pan"
-        ],
-        [
-         "Zoom",
-         "Zoom to rectangle\nx/y fixes axis, CTRL fixes aspect",
-         "square-o",
-         "zoom"
-        ],
-        [
-         "Download",
-         "Download plot",
-         "floppy-o",
-         "save_figure"
-        ]
-       ]
-      }
-     },
-     "bb7a3e7f48de4d06b8d3081be8c87ddd": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "PlayModel",
-      "state": {
-       "layout": "IPY_MODEL_9da77a70252e496cbae576ee5f5f2a37",
-       "max": 49,
-       "style": "IPY_MODEL_0db9c1982fec463d8b9dc9c6f586eef0"
-      }
-     },
-     "bbf53d024b3f4f828f29c6c0299243f6": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "LabelModel",
-      "state": {
-       "layout": "IPY_MODEL_2f2a0039685c4a67863065d23816d6ca",
-       "style": "IPY_MODEL_12ed86c99bae48f5a92c578d8f7c4516",
-       "value": "0.00"
-      }
-     },
-     "bdc96de93ff24577b51e3d42c2bc92e9": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "DescriptionStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "c08c87cf7f1647428136fd31634de388": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "IntSliderModel",
-      "state": {
-       "description": "tau",
-       "layout": "IPY_MODEL_cfa5159e905749af95d4f2bd43976e69",
-       "max": 99,
-       "readout": false,
-       "style": "IPY_MODEL_922918b3a46440caba07dc5d0414478a"
-      }
-     },
-     "c28ac5539ce84262a051493b8af70468": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "DescriptionStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "c4b47c5e8f42426fb265ae90be2c9727": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "PlayModel",
-      "state": {
-       "layout": "IPY_MODEL_826f79d4c152428598bd068aa6a75458",
-       "max": 99,
-       "style": "IPY_MODEL_4dfd079772d5408a90243977c696892d"
-      }
-     },
-     "c4f208dc8ef9474caacdbef360b0eaca": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "c7c475200a4347dab3744ae01d8cab5c": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "SliderStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "c871a2fcda354f56b75bb4d99a90d6c7": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "IntSliderModel",
-      "state": {
-       "description": "beta",
-       "layout": "IPY_MODEL_7906c14cb4744c3e928973f4db2e0c4f",
-       "max": 49,
-       "readout": false,
-       "style": "IPY_MODEL_79e7bd8d9c4547ca96bd8f62aa6e352c"
-      }
-     },
-     "c957c09849a44aef8c283e84178d05b7": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "c9ca522bd6664d2e943063c2c1bb778c": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "cb11dbf2d76b430fb5a88bd0afbab755": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "LabelModel",
-      "state": {
-       "layout": "IPY_MODEL_e7b784dd3cc940fea682474c6033316b",
-       "style": "IPY_MODEL_d52e7a8d606a4ebcada37e4ff93cfa08",
-       "value": "0.00"
-      }
-     },
-     "cda7d2d5e7c341f5a803c0090df592fc": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "DescriptionStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "ceaf5ab5d836493e9578f0c4477c95a6": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "DescriptionStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "cef0fa3a198748eeaf798316a99b12e6": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "IntSliderModel",
-      "state": {
-       "description": "tau",
-       "layout": "IPY_MODEL_81b098ba89fb495297a29bcd9fa63552",
-       "max": 99,
-       "readout": false,
-       "style": "IPY_MODEL_f3f6b0f92cba44e9bc29b218ef1f3fcf"
-      }
-     },
-     "cfa5159e905749af95d4f2bd43976e69": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "d193c01161c947ecabb31eede84added": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "d1bb25426654463294285194c18f977d": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "IntSliderModel",
-      "state": {
-       "description": "beta",
-       "layout": "IPY_MODEL_9d442f5afced48358f8e6440731e6c9e",
-       "max": 49,
-       "readout": false,
-       "style": "IPY_MODEL_78bb2ca2bf3f4e4093b64b0d8d25002b"
-      }
-     },
-     "d2fca53ae566481eb92918dbea6736a7": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "PlayModel",
-      "state": {
-       "layout": "IPY_MODEL_7a9e7a3dcec249d1a40578d1aebb4508",
-       "max": 99,
-       "style": "IPY_MODEL_e6538b271ebb4448a0cd8919878279ea"
-      }
-     },
-     "d442844b13254079b92e172bd06bf992": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "IntSliderModel",
-      "state": {
-       "description": "tau",
-       "layout": "IPY_MODEL_d6a07d0a7bb24d92b937772fc4512823",
-       "max": 99,
-       "readout": false,
-       "style": "IPY_MODEL_2254583091644de4a80cdcccb4c08583"
-      }
-     },
-     "d4ffdc99ce9843489b5234ada4605c6a": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "DescriptionStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "d513e060ff3640c080dd3db73f2606e8": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "SliderStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "d52e7a8d606a4ebcada37e4ff93cfa08": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "DescriptionStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "d5b7667dfb094fee982a96f7cfa5395e": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "d6a07d0a7bb24d92b937772fc4512823": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "d7acd8cdba624082957938500777c124": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "LinkModel",
-      "state": {
-       "source": [
-        "IPY_MODEL_063865096bce4c3eabd7d67f9ae6f602",
-        "value"
-       ],
-       "target": [
-        "IPY_MODEL_4d6863ed1c4d42f0a3361aa2b3cb66ae",
-        "value"
-       ]
-      }
-     },
-     "da486abb36db4e938ab10b860c8cd95c": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "dcf043c1f4184e689caf11fc9b84427d": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "dcf363d524924e78ac5a280145472fb7": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "PlayModel",
-      "state": {
-       "layout": "IPY_MODEL_b3dcdeeb58424c3098ab27090143f9d4",
-       "max": 49,
-       "style": "IPY_MODEL_c28ac5539ce84262a051493b8af70468"
-      }
-     },
-     "e0aed347f4d64594820933534659c93b": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "HBoxModel",
-      "state": {
-       "children": [
-        "IPY_MODEL_c08c87cf7f1647428136fd31634de388",
-        "IPY_MODEL_fa755fc6fb9347fc8cfb14edd5c67e93",
-        "IPY_MODEL_34a7eac0c50e4af6983ea87de0950492"
-       ],
-       "layout": "IPY_MODEL_9d8aabb9f74d4000836fc2b55975dda3"
-      }
-     },
-     "e0f31fe391f44beb8d5db7ffcfffe74c": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "e5a6bd63bc9e483c8fe0e973e9477990": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "LabelModel",
-      "state": {
-       "layout": "IPY_MODEL_d193c01161c947ecabb31eede84added",
-       "style": "IPY_MODEL_e95c0a3bd12544c38923f2bf990b4b58",
-       "value": "0.00"
-      }
-     },
-     "e62faf79ecab47ccaa1a896a9b09e52d": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "e6538b271ebb4448a0cd8919878279ea": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "DescriptionStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "e6d2cef1df63469eb09ab1cd00a2755f": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "DescriptionStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "e7b784dd3cc940fea682474c6033316b": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "e87eab4366304228966e81912efe6165": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "HBoxModel",
-      "state": {
-       "children": [
-        "IPY_MODEL_063865096bce4c3eabd7d67f9ae6f602",
-        "IPY_MODEL_4d6863ed1c4d42f0a3361aa2b3cb66ae",
-        "IPY_MODEL_cb11dbf2d76b430fb5a88bd0afbab755"
-       ],
-       "layout": "IPY_MODEL_62b5443b13ea4cdb831ba8b342723277"
-      }
-     },
-     "e8d30c286f134439a490ef6ac63464d9": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "VBoxModel",
-      "state": {
-       "children": [
-        "IPY_MODEL_ad6a6cab03d942468da0e064754e8165",
-        "IPY_MODEL_8693a8c89ca84e7fa7ac41be929c1c96"
-       ],
-       "layout": "IPY_MODEL_80c5135322724c618d0d4917c94ea160"
-      }
-     },
-     "e95c0a3bd12544c38923f2bf990b4b58": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "DescriptionStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "eb601277028748be867ac124794ef32a": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "ecc7dad4ca024181809d0564e0782045": {
-      "model_module": "jupyter-matplotlib",
-      "model_module_version": "^0.7.4",
-      "model_name": "MPLCanvasModel",
-      "state": {
-       "_cursor": "default",
-       "_figure_label": "Figure 3",
-       "_height": 480,
-       "_width": 640,
-       "layout": "IPY_MODEL_20c3bfbec9a043fa82d7331d3f10a9d8",
-       "toolbar": "IPY_MODEL_55dc31dbd76e44e1b443b80229aebb6d",
-       "toolbar_position": "left"
-      }
-     },
-     "ee66bfbfc84f4f678a4c0e004e85d037": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "LabelModel",
-      "state": {
-       "layout": "IPY_MODEL_d5b7667dfb094fee982a96f7cfa5395e",
-       "style": "IPY_MODEL_50a7491fb4794e178029c5b19534969c",
-       "value": "1.00"
-      }
-     },
-     "f1b7c0904d774daea8df0d6c3db7296e": {
-      "model_module": "jupyter-matplotlib",
-      "model_module_version": "^0.7.4",
-      "model_name": "MPLCanvasModel",
-      "state": {
-       "_cursor": "default",
-       "_figure_label": "Figure 1",
-       "_height": 480,
-       "_width": 640,
-       "layout": "IPY_MODEL_5bb45121d6464c7097363c25efe98cf9",
-       "toolbar": "IPY_MODEL_34c2110420714db5ba37dc6580359aab",
-       "toolbar_position": "left"
-      }
-     },
-     "f3f6b0f92cba44e9bc29b218ef1f3fcf": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "SliderStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "f48b7a250b21444f920fc06fd8611729": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "DescriptionStyleModel",
-      "state": {
-       "description_width": ""
-      }
-     },
-     "f68c476cf3e549c49e8866d85fca539e": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "PlayModel",
-      "state": {
-       "layout": "IPY_MODEL_1e500a68776e4fc0bcf600a8c5f53830",
-       "max": 49,
-       "style": "IPY_MODEL_bdc96de93ff24577b51e3d42c2bc92e9"
-      }
-     },
-     "f86b6435058b44149420ba354a8c905f": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     },
-     "fa755fc6fb9347fc8cfb14edd5c67e93": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "LabelModel",
-      "state": {
-       "layout": "IPY_MODEL_2a25dcc2cd854baab048c1f66a496d8b",
-       "style": "IPY_MODEL_cda7d2d5e7c341f5a803c0090df592fc",
-       "value": "1.00"
-      }
-     },
-     "fb2486a24a254d7699aa8deab9aab84c": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "PlayModel",
-      "state": {
-       "layout": "IPY_MODEL_c4f208dc8ef9474caacdbef360b0eaca",
-       "max": 99,
-       "style": "IPY_MODEL_add98409108844a9a2044222f40c93da"
-      }
-     },
-     "fc3fdfc0d42d4df3aae4d1ba0ec6f627": {
-      "model_module": "jupyter-matplotlib",
-      "model_module_version": "^0.7.4",
-      "model_name": "MPLCanvasModel",
-      "state": {
-       "_cursor": "default",
-       "_figure_label": "Figure 4",
-       "_height": 480,
-       "_width": 640,
-       "layout": "IPY_MODEL_209e0565eee047a1b2aec9a08130de30",
-       "toolbar": "IPY_MODEL_721c989b369d426e8611d8919b02e302",
-       "toolbar_position": "left"
-      }
-     },
-     "fdab149e5d5f472b9a733a771e6ce9f2": {
-      "model_module": "@jupyter-widgets/controls",
-      "model_module_version": "1.5.0",
-      "model_name": "HBoxModel",
-      "state": {
-       "children": [
-        "IPY_MODEL_fb2486a24a254d7699aa8deab9aab84c",
-        "IPY_MODEL_cef0fa3a198748eeaf798316a99b12e6",
-        "IPY_MODEL_6d97ab7e653145f4a792eb0019c1b04a"
-       ],
-       "layout": "IPY_MODEL_336cf6ddc05047909cfb90bb6870dc2b"
-      }
-     },
-     "fee5501fdac240ebbf99c2fdaeab9636": {
-      "model_module": "@jupyter-widgets/base",
-      "model_module_version": "1.2.0",
-      "model_name": "LayoutModel",
-      "state": {}
-     }
-    },
-    "version_major": 2,
-    "version_minor": 0
-   }
-  }
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
diff --git a/setup.cfg b/setup.cfg
index d9cd7573..941ba28b 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -50,9 +50,12 @@ doc =
     myst-nb
     numpydoc
     Sphinx >= 1.5
+    sphinx-book-theme
     sphinx-copybutton
     sphinx-gallery >= 0.8.2
-    sphinx-rtd-theme
+    sphinx-panels
+    sphinx-thebe
+    sphinx-togglebutton
     xarray
 sty =
     black
diff --git a/tox.ini b/tox.ini
index be29ab7e..f59a5d38 100644
--- a/tox.ini
+++ b/tox.ini
@@ -19,7 +19,7 @@ commands =
 
 [testenv:doc]
 description =
-    Build documentation
+    Build documentation and API through Sphinx
 changedir = docs
 allowlist_externals =
     make
@@ -32,6 +32,7 @@ description =
 allowlist_externals =
     sphinx-autobuild
 passenv =
+    EXECUTE_NB
     TERM
 commands =
     sphinx-autobuild \
@@ -40,13 +41,23 @@ commands =
         --re-ignore .*/.ipynb_checkpoints/.* \
         --re-ignore .*/__pycache__/.* \
         --re-ignore docs/_build/.* \
-        --re-ignore docs/autoapi/.* \
+        --re-ignore docs/api/.* \
         --re-ignore docs/examples/.*.gif \
-        --re-ignore docs/examples/.*.ipynb \
         --re-ignore docs/gallery/.* \
         --open-browser \
         docs/ docs/_build/html
 
+[testenv:docnb]
+description =
+    Build documentation through Sphinx WITH output of Jupyter notebooks
+setenv =
+    EXECUTE_NB = "yes"
+changedir = docs
+allowlist_externals =
+    make
+commands =
+    make html
+
 [testenv:linkcheck]
 description =
     Check external links in the documentation (requires internet connection)


Test patch:
diff --git a/examples/test.dat b/docs/examples/test.dat
similarity index 100%
rename from examples/test.dat
rename to docs/examples/test.dat
diff --git a/mpl_interactions/tests/test_generic.py b/mpl_interactions/tests/test_generic.py
index 2bbeb9d2..92904142 100644
--- a/mpl_interactions/tests/test_generic.py
+++ b/mpl_interactions/tests/test_generic.py
@@ -1,3 +1,5 @@
+from os.path import dirname, realpath
+
 import matplotlib.cbook as cbook
 import matplotlib.pyplot as plt
 import numpy as np
@@ -38,10 +40,9 @@ def test_image_segmentation():
     image = plt.imread(
         "https://github.com/matplotlib/matplotlib/raw/v3.3.0/lib/matplotlib/mpl-data/sample_data/ada.png"
     )
-    try:
-        mask = np.load("../../examples/ada-mask.npy")
-    except FileNotFoundError:
-        mask = np.load("examples/ada-mask.npy")
+    script_dir = realpath(dirname(__file__))
+    mask_file = "examples/ada-mask.npy"
+    mask = np.load(f"{script_dir}/../../docs/{mask_file}")
     preloaded = image_segmenter(image, nclasses=3, mask=mask)
 
     return preloaded.fig
diff --git a/pytest.ini b/pytest.ini
index d90df217..31d1b926 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -1,4 +1,6 @@
 [pytest]
-testpaths = mpl_interactions/tests examples
+testpaths =
+    docs/examples
+    mpl_interactions/tests
 norecursedirs = node_modules .ipynb_checkpoints devlop
 addopts = --nbval --current-env --mpl