# Interactions with Kino

## Setup

In this notebook we will explore the possibilities that
[`kino`](https://github.com/elixir-nx/kino) brings
into your notebooks. Kino can be thought of as Livebook's
friend that instructs it how to render certain output
and interact with it.

```elixir
Mix.install([
  {:kino, "~> 0.3.1"},
  {:vega_lite, "~> 0.1.2"}
])
```

```elixir
alias VegaLite, as: Vl
```

<!-- livebook:{"branch_parent_index":0} -->

## Kino.VegaLite

In the [Plotting with VegaLite](/explore/notebooks/intro-to-vega-lite) notebook we show
numerous ways in which you can visualize your data. However, all of the plots
there are static.

Using Kino, we can dynamically stream data to the plot, so that it keeps updating!
To do that, all you need is a regular VegaLite specification that you then pass
to `Kino.VegaLite.new/1`. You don't have to specify any data up-front.

```elixir
widget =
  Vl.new(width: 400, height: 400)
  |> Vl.mark(:line)
  |> Vl.encode_field(:x, "x", type: :quantitative)
  |> Vl.encode_field(:y, "y", type: :quantitative)
  |> Kino.VegaLite.new()
```

Then you can push data to the plot widget at any point and see it update dynamically:

```elixir
for i <- 1..300 do
  point = %{x: i / 10, y: :math.sin(i / 10)}

  # The :window option ensures we only show the latest
  # 100 data points on the plot
  Kino.VegaLite.push(widget, point, window: 100)

  Process.sleep(25)
end
```

You can also explicitly clear the data:

```elixir
Kino.VegaLite.clear(widget)
```

### Periodical updates

You may want to have a plot running forever and updating in the background.
There is a dedicated `Kino.VegaLite.periodically/4` function that allows you do do just that!
You just need to specify the interval and the reducer callback like this,
then you interact with the widget as usually.

```elixir
widget =
  Vl.new(width: 400, height: 400)
  |> Vl.mark(:line)
  |> Vl.encode_field(:x, "x", type: :quantitative)
  |> Vl.encode_field(:y, "y", type: :quantitative)
  |> Kino.VegaLite.new()
  |> tap(&Kino.render/1)

# Add a callback to run every 25ms
Kino.VegaLite.periodically(widget, 25, 0, fn i ->
  point = %{x: i / 10, y: :math.sin(i / 10)}
  # Interacting with the widget is as usual
  Kino.VegaLite.push(widget, point, window: 100)
  # Continue with the new accumulator value
  {:cont, i + 1}
end)
```

<!-- livebook:{"branch_parent_index":0} -->

## Kino.ETS

You can use `Kino.ETS.new/1` to render ETS tables and easily
browse their contents. Let's first create our own table:

```elixir
tid = :ets.new(:users, [:set, :public])
Kino.ETS.new(tid)
```

In fact, Livebook automatically recognises an ETS table and
renders it as such:

```elixir
tid
```

Currently the table is empty, so it's time to insert some rows.

```elixir
for id <- 1..24 do
  :ets.insert(tid, {id, "User #{id}", :rand.uniform(100), "Description #{id}"})
end
```

Having the rows inserted, click on the "Refetch" icon in the table output
above to see them.

<!-- livebook:{"branch_parent_index":0} -->

## Kino.DataTable

When it comes to tables, we are not limited to ETS! You can render
arbitrary tabular data using `Kino.DataTable.new/1`, let's have
a look:

```elixir
data = [
  %{id: 1, name: "Elixir", website: "https://elixir-lang.org"},
  %{id: 2, name: "Erlang", website: "https://www.erlang.org"}
]

Kino.DataTable.new(data)
```

The data must be an enumerable, with records being maps,
keyword lists or tuples.

Now, let's get some more realistic data:

```elixir
processes = Process.list() |> Enum.map(&Process.info/1)
```

We can easily pick only the data keys that are relevant
for us:

```elixir
Kino.DataTable.new(
  processes,
  keys: [:registered_name, :initial_call, :reductions, :stack_size]
)
```

We can sort by the number of reductions to identify the
most busy processes!

<!-- livebook:{"branch_parent_index":0} -->

## Kino.Markdown

Sometimes you may want to render arbitrary content as rich-text,
that's when `Kino.Markdown.new/1` comes into play:

````elixir
"""
# Example

A regular Markdown file.

## Code

```elixir
"Elixir" |> String.graphemes() |> Enum.frequencies()
```

## Table

| ID | Name   | Website                 |
| -- | ------ | ----------------------- |
| 1  | Elixir | https://elixir-lang.org |
| 2  | Erlang | https://www.erlang.org  |
"""
|> Kino.Markdown.new()
````

<!-- livebook:{"branch_parent_index":0} -->

## Kino.render/1

As we saw, Livebook automatically recognises widgets returned
from each cell and renders them accordingly. However, sometimes
it's useful to explicitly render a widget in the middle of the cell,
similarly to `IO.puts/1` and that's exactly what `Kino.render/1`
does! It works with any type and tells Livebook to render the value
in its special manner.

```elixir
# Arbitrary data structures
Kino.render([%{name: "Ada Lovelace"}, %{name: "Alan Turing"}])

# Static plots
vl =
  Vl.new(width: 400, height: 400)
  |> Vl.data_from_series(x: 1..100, y: 1..100)
  |> Vl.mark(:line)
  |> Vl.encode_field(:x, "x", type: :quantitative)
  |> Vl.encode_field(:y, "y", type: :quantitative)

Kino.render(vl)
Kino.render(vl)

Kino.render("Plain text")

"Cell result 🚀"
```

Before we saw how you can render and stream data to the plot
from a separate cell, the same could be rewritten in one go
like this:

```elixir
widget =
  Vl.new(width: 400, height: 400)
  |> Vl.mark(:line)
  |> Vl.encode_field(:x, "x", type: :quantitative)
  |> Vl.encode_field(:y, "y", type: :quantitative)
  |> Kino.VegaLite.new()
  |> tap(&Kino.render/1)

for i <- 1..300 do
  point = %{x: i / 10, y: :math.sin(i / 10)}
  Kino.VegaLite.push(widget, point, window: 100)
  Process.sleep(25)
end
```

<!-- livebook:{"branch_parent_index":0} -->

## Kino.animate/3

If you want to continuously update the output as time passes,
you can use `Kino.animate/3`:

```elixir
Kino.animate(100, 0, fn i ->
  md = Kino.Markdown.new("**Iteration: `#{i}`**")
  {:cont, md, i + 1}
end)
```

The above example renders new Markdown output every 100ms.
You can use the same approach to render regular output
or images too! Also note some elements may have specific
functions for periodic updates, such as `Kino.VegaLite.periodically/4`
seen in previous sections.
