Introduction to Interact.jl


In [1]:
using React, Interact


Interact.jl provides interactive widgets for IJulia. Interaction relies on React.jl reactive programming package. React provides the type Signal which represent time-varying values. For example, a Slider widget can be turned into a "signal of numbers". Execute the following two cells, and then move the slider. You will see that the value of signal(slider) changes accordingly.


In [2]:
s = slider(0:0.01:1, label="Slider X:")


Out[2]:

In [3]:
signal(s)


Out[3]:
0.5

Let us now inspect the types of these entities.


In [4]:
display(typeof(s));
isa(s, Widget)


Slider{Float64} (constructor with 1 method)
Out[4]:
true

In [5]:
display(typeof(signal(s)));
isa(signal(s), Signal{Float64})


Input{Float64} (constructor with 1 method)
Out[5]:
true

You can have many instances of the same widget in a notebook, and they stay in sync:


In [6]:
s


Out[6]:

Using Widget Signals

A slider is useless if you cannot do more with it than just watch its value. Thankfully we can transform one signal into another, which means we can transform the signal of values that the slider takes into, say a signal of it's squares:


In [7]:
xsquared = lift(x -> x*x, signal(s))


Out[7]:
0.25

Go ahead and vary the slider to see this in action.

You can transform a signal into pretty much anything else. Let's use the Color package to produce different saturations of red:


In [8]:
using Color
lift(x -> RGB(x, 0.5, 0.5), signal(s))


Out[8]:

You can of course use several inputs as arguments to lift enabling you to combine many signals. Let's create a full color-picker.


In [9]:
r = slider(0:0.01:1, label="R")
g = slider(0:0.01:1, label="G")
b = slider(0:0.01:1, label="B")
map(display, [r,g,b]);



In [10]:
color = lift((x, y, z) -> RGB(x, y, z), r, g, b)


Out[10]:

the @lift macro provides useful syntactic sugar to do this:


In [11]:
color = @lift RGB(r, g, b)


Out[11]:

We can use the HTML widget to write some text you can change the color of.


In [12]:
@lift html(string("<div style='color:#", hex(color), "'>Hello, World!</div>"))


Out[12]:

The @manipulate Macro

The @manipulate macro lets you play with any expression using widgets. We could have, for example, used @manipulate to make a color picker along with our HTML output in one line of code:


In [13]:
@manipulate for r = 0:.05:1, g = 0:.05:1, b = 0:.05:1
    html(string("<div style='color:#", hex(RGB(r,g,b)), "'>Color me concise</div>"))
end


Out[13]:

Signal of Widgets

You can in fact create signal of other widgets to update them reactively. We have seen one case with HTML above. Let us now create a signal of Slider:


In [14]:
x = slider(0:.1:2pi, label="x")
s = @lift slider(-1:.05:1, value=sin(2x), label="sin(2x)")
c = @lift slider(-1:.05:1, value=cos(2x), label="cos(2x)")
map(display, [x,s,c]);


Now vary the x slider to see sin(2x) and cos(2x) get set to their appropriate values.

But in the above case, you cannot also use sin(2x) and cos(2x) sliders as input values. To do this, we will have to create a separate Input signal and pass it as argument to lift. Unfortunaltely, we cannot use the @lift macro here because of ambiguity in parsing. Example:


In [15]:
fx = Input(0.0) # A float input


Out[15]:
0.0

In [16]:
x = slider(0:.1:2pi, label="x")
y = lift(v -> slider(-1:.05:1, value=sin(v), input=fx, label="f(x)"), x)
map(display, (x,y));


f(x) will update as x changes. But if the user slides f(x) then the fx signal takes the value chosen by the user.