Interactive code in your book

Sometimes you'd rather let people interact with code directly on the page instead of sending them off to a Binder or a JupyterHub. There are currently a few ways to make this happen in Jupyter Book (both of which are experimental).

This page describes how to bring interactivity to your book. Both of these tools use MyBinder to provide a remote kernel.

Making your page inputs interactive

experimental

If you'd like to provide interactivity for your content without making your readers leave the Jupyter Book site, you can use a project called Thebelab.

This provides you a button that, when clicked, will convert each code cell into an interactive cell that can be edited. It also adds a "run" button to each cell, and connects to a Binder kernel running in the cloud. As an alternative to pressing the Thebelab button at the top of the page, you can press the symbol in the top right corner of each code cell to start the interactive mode.

To add a Thebelab button to your Jupyter Book pages, use the following configuration:

use_thebelab_button              : true  # If 'true', display a button to allow in-page running code cells with Thebelab

In addition, you can configure the Binder settings that are used to provide a kernel for Thebelab to run the code. These use the same configuration fields as the BinderHub interact buttons described above.

For an example, click the Thebelab button above on this page, and run the code below.

In [1]:
import numpy as np
import matplotlib.pyplot as plt
plt.ion()

x = np.arange(500)
y = np.random.randn(500)

fig, ax = plt.subplots()
ax.scatter(x, y, c=y, s=x)
Out[1]:
<matplotlib.collections.PathCollection at 0x7ff693dfc128>

Running cells in Thebelab when it is initialized

Sometimes you'd like to initialize the kernel that Thebelab uses by running some code ahead of time. This might be code that you then hide from the user in order to narrow the focus of what they interact with. This is possible by using Jupyter Notebook tags.

Adding the tag thebelab-init to any code cell will cause Thebelab to run this cell after it has received a kernel. Any subsequent Thebelab cells will have access to the same environment (e.g. any module imports made in the initialization cell).

You can then pair this with something like hide_input in order to run initialization code that your user doesn't immediately see. For example, below we'll initialize a variable in a hidden cell, and then tell another cell to print the output of that variable.

In [ ]:
my_hidden_variable = 'wow, it worked!'
In [ ]:
# The variable for this is defined in the cell above!
print(my_hidden_variable)

Using interactive widgets on your page

experimental

nbinteract is a tool for displaying interactive widgets in your static HTML page. It uses a Binder kernel to power the widgets, and displays output that your readers can interact with. For example, below we will show a simple matplotlib plot that can be made interactive with ipywidgets

To add a Show Widgets button to your Jupyter Book pages, use the following configuration:

use_show_widgets_button         : true  # If 'true', display a button to show widgets backed by a Binder kernel

Then, tell Jupyter Book that you want a cell to display a widget by adding a tag to the cell's metadata called interactive. When a reader clicks on the "show widgets" button, any cells with this tag will be run on Binder, and have their output widgets displayed underneath the cell.

Here's an example of cell metadata that would trigger this behavior:

{
    "tags": [
        "interactive",
    ]
}

You can configure the Binder settings that are used to provide a kernel to run the code. These use the same configuration fields as the BinderHub interact buttons described above.

Clicking on "show widgets" should display a widget below. We've hidden the code cell that generates the widget by default (though you can always show it by clicking the button to the right!

In [2]:
from ipywidgets import interact, FloatSlider
import numpy as np
import matplotlib.pyplot as plt
from IPython.display import display, HTML
plt.ion()

x = np.arange(500)
y = np.random.randn(500)

def update_plot_size(s, cmap):
    if cmap == "jet":
        display(HTML("<h2 style='color: red; margin: 0px auto;'>Nope</h2>"))
        return
    fig, ax = plt.subplots()
    ax.scatter(x, y, c=y, s=x*s, cmap=cmap)

interact(update_plot_size, s=FloatSlider(value=1, min=.1, max=2, step=.1), cmap=['viridis', 'magma', 'jet']);