# Build a chat app with Kino
```elixir
Mix.install([
{:kino, "~> 0.18.0"}
])
```
## Introduction
In this notebook, we will build and deploy a chat application.
To do so, we will use Livebook's companion library called
[`kino`](https://github.com/livebook-dev/kino).
In a nutshell, Kino is a library that you install as part
of your notebooks to make your notebooks interactive.
Kino comes from the Greek prefix "kino-" and it stands for
"motion". As you learn the library, it will become clear that
this is precisely what it brings to our notebooks.
Kino can render Markdown, animate frames, display tables,
manage inputs, and more. It also provides the building blocks
for extending Livebook with charts, smart cells, and much more.
For building notebook applications, we rely on two main
building blocks: `Kino.Control` and `Kino.Frame`.
You can see `kino` listed as a dependency above, so let's run
the setup cell and get started.
## Kino.Control
The simplest control is `Kino.Control.button/1`. Let's give it a try:
```elixir
click_me = Kino.Control.button("Click me!")
```
Execute the cell above and the button will be rendered. You can click
it, but nothing will happen. Luckily, we can subscribe to the button
events:
```elixir
Kino.Control.subscribe(click_me, :click_me)
```
Now that we have subscribed, every time the button is clicked, we will
receive a message tagged with `:click_me`. Let's print all messages
in our inbox:
```elixir
Process.info(self(), :messages)
```
Now execute the cell above, click the button a couple times, and
re-execute the cell above. For each click, there is a new message in
our inbox. There are several ways we can consume this message.
Let's see a different one in the next example.
## Enumerating controls
All Kino controls are enumerable. This means we can treat them
as a collection, an infinite stream of events in this case.
Let's define another button:
```elixir
click_me_again = Kino.Control.button("Click me again!")
```
And now let's consume those events. Because the stream is
infinite, we will consume them inside a separate process,
in order to not block our notebook:
```elixir
spawn(fn ->
for event <- click_me_again do
IO.inspect(event)
end
end)
```
Now, as you submit the button, you should see a new event
printed. It happens this pattern of consuming events without
blocking the notebook is so common that `Kino` even has a
convenience functions for it, such as `Kino.animate/2` and
`Kino.listen/2`. Let's keep on learning.
## Kino.Frame and animations
`Kino.Frame` allows us to render an empty frame and update it
as we progress. Let's render an empty frame:
```elixir
frame = Kino.Frame.new()
```
Now, let's render a random number between 1 and 100 directly
in the frame:
```elixir
Kino.Frame.render(frame, "Got: #{Enum.random(1..100)}")
```
Notice how every time you reevaluate the cell above it updates
the frame. You can also use `Kino.Frame.append/2` to append to
the frame:
```elixir
Kino.Frame.append(frame, "Got: #{Enum.random(1..100)}")
```
Appending multiple times will always add new contents. The content
can be reset by calling `Kino.Frame.render/2` or `Kino.Frame.clear/1`.
One important thing about frames is that they are shared across
all users. If you open up this same notebook in another tab and
execute the cell above, it will append the new result on all tabs.
This means we can use frames for building collaborative applications
within Livebook itself!
You can combine this with loops to dynamically add contents
or animate your notebooks. In fact, there is a convenience function
called `Kino.animate/2` to be used exactly for this purpose:
```elixir
Kino.animate(100, fn i ->
Kino.Markdown.new("**Iteration: `#{i}`**")
end)
```
The above example creates a new frame behind the scenes and renders
new Markdown output every 100ms. You can use the same approach to
render regular output or images too!
There's also `Kino.animate/3`, in case you need to accumulate state or
halt the animation at certain point. Both `animate` functions allow
an enumerable to be given, which means we can animate a frame based
on the events of a control:
```elixir
button = Kino.Control.button("Click") |> Kino.render()
Kino.animate(button, 0, fn _event, counter ->
new_counter = counter + 1
md = Kino.Markdown.new("**Clicks: `#{new_counter}`**")
{:cont, md, new_counter}
end)
```
One of the benefits of using `animate` to consume events is
that it does not block the notebook execution and we can
proceed as usual.
## Putting it all together
We have learned about controls and frames, which means now we
are ready to build our chat application.
The first step is to define the frame we want to render our
chat messages:
```elixir
frame = Kino.Frame.new()
```
Now we will use a new control, called forms, to render and submit
multiple inputs at once:
```elixir
inputs = [
name: Kino.Input.text("Name"),
message: Kino.Input.text("Message")
]
form = Kino.Control.form(inputs, submit: "Send", reset_on_submit: [:message])
```
Now we want to append the message to a frame every time the
form is submitted. We have learned about `Kino.animate/3`,
that receives control events, but unfortunately it only updates
frames in place while we want to always append content.
We could accumulate the content ourselves and always re-render
it all on the frame, but that sounds a bit wasteful.
Luckily, Kino also provides a function called `listen`. `listen`
also consumes events from controls and enumerables, but it does
not assume we want to render a frame, ultimately giving us more
control. Let's give it a try:
```elixir
Kino.listen(form, fn %{data: %{name: name, message: message}, origin: origin} ->
if name != "" and message != "" do
content = Kino.Markdown.new("**#{name}**: #{message}")
Kino.Frame.append(frame, content)
else
content = Kino.Markdown.new("_ERROR! You need a name and message to submit..._")
Kino.Frame.append(frame, content, to: origin)
end
end)
```
Execute the cell above and your chat app should be
fully operational. Scroll up, submit messages via the
form, and see them appear in the frame.
Implementation-wise, the call to `listen` receives the
form events, which includes the value of each input.
If a name and message have been given, we append it
to the frame.
The `append` function also accepts two options worth
discussing. The first one, used in the example above,
is the `to: origin` option. This means the particular
message will be sent only to the user who submitted
the form, instead of everyone.
Another option frequently used is `:temporary`. All
messages are stored in the frame by default. This means
that, if you reload the page, or join late, you can see
all history. If you set `:temporary` to true, that will
no longer be the case. Note all messages sent with the
`:to` option are temporary.
You can also open up this notebook on different tabs
and emulate how different users can chat with each other.
Give it a try!
## Running as a Livebook app
Our chat application is ready, therefore it means we are
ready to run it!
Click on the
icon on the sidebar and then on "Configure". Now, define a slug for your deployment,
such as "chat-app", set a password (or disable password protection), and click
"Save".
Click on the "Launch preview" button to run your notebook as an app.
Now you can click the URL and interact with the chat app, as you did inside the notebook.
When you run a notebook as an app, Livebook will execute all of
the code in the notebook from beginning to end. This sets
up our whole application, including frames and forms, for
users to interact with. In case something goes wrong, you
can always click the Livebook icon on the running app and
choose to debug the running notebook session.
From here onwards, feel free to adjust the application,
by removing unused outputs from earlier sections or by adding
new features.
Congratulations on shipping!
## Docker deployment
Now that you have run your notebook as an application
locally, you may be wondering: can I actually ship this production?
The answer is yes!
Click on the icon on the sidebar
and you will find a "Manual Docker deployment" button. Clicking on it
will open up a modal with instructions on deploying a single notebook
or a folder with several entries through Docker.
We also offer a service called [Livebook Teams](https://livebook.dev/teams),
which comes with the following features for Livebook apps:
* one-click deployment
* authentication
* authorization
## Where to go next
There are many types of applications you can build with
notebooks. For example, we can use the foundation we learned
here to develop any type of form-driven application.
The structure is always the same:
1. Define a inputs and forms for the user to interact with
2. Hook into the form events to receive and validate data
3. Render updates directly into `Kino.Frame`
The frame plays an essential role here. If you render to the
frame without the `to: origin` option, the updates are sent
to all users. With the `to: origin` option, the changes are
visible only to a given user. This means you get full control
if the application is collaborative or not.
[Livebook also supports multi-session applications](https://www.youtube.com/watch?v=dSjryA1iFng),
where each user starts their own Livebook session on demand.
By using `Kino.Input` and `Kino.interrupt/2`, it is common to
build multi-session applications that execute step-by-step,
similar to regular notebooks, without a need to setup form
controls and events handlers as done in this guide.
Furthermore, each session in a multi-session app has their
own Elixir runtime, which provides isolation but also leads
to higher memory usage per session.
To learn more about apps, here are some resources to dig deeper:
* [The announcement of Livebook apps with livecoding of
the application built in this guide](https://www.youtube.com/watch?v=q7T6ue7cw1Q)
* [Livecoding of an audio-based chat application where
a Neural Network is used to convert speech to text](https://www.youtube.com/watch?v=uyVRPEXOqzw)
* [The announcement of multi-session apps with livecoding
of a sample application](https://www.youtube.com/watch?v=dSjryA1iFng)