# Myxine: a slithery sea-friend to help you _get GUI fast_
Hagfish, a.k.a. myxine glutinosa, are eel-like sea creatures best known for their ability to make a lot of slime.
By analogy, myxine quickly reduces the friction in creating a dynamic graphical interface, helping you to get GUI fast in any language under the sea.
Myxine is a local web server that enables you to create interactive applications
in your web browser from the comfort of your favorite programming language. It's
designed to satisfy three explicit goals:
1. To enable programmers who don't necessarily specialize in UI design to build
appealing interactive applications without learning a complex UI framework.
2. To make it easy to write correct, efficient, and idiomatic bindings to Myxine
from almost any programming language.
3. To be as fast as possible while consuming as few resources as possible.
**Here's how it works:**
1. You start Myxine and open your browser to a page it is serving.
2. From your programming language of choice, you send some HTML to Myxine, and
it instantly appears, replacing the contents of that page's ``.
3. You then request a subscription to whichever browser events in which you're
interested, and Myxine notifies you when each one occurs, eliding those you
don't care about.
4. You can then react to those events, updating the page again to reflect what
you'd like it to look like now. Rinse and repeat!
Myxine handles the hard work of optimizing this process to minimize latency and
computational load: it can handle thousands of requests per second and
translate them into smooth, flicker-free animations at up to 60 frames per
second.
## Installing
To install the Myxine server, you will need a recent version of the Rust
programming langauge and its build tool, `cargo`. If you don't have it, [here's
the quick-start for installing
Rust](https://www.rust-lang.org/learn/get-started). After you have that set up,
install the Myxine server:
```bash
$ cargo install myxine
```
Installing a client library for Myxine will require steps specific to that
library: consult the appropriate library's documentation to find out how to
install it.
## Building interactive applications
You can interact directly with the server via HTTP requests (see the [API
documentation](API.md) for details), but most likely you will want to use a
library of lightweight bindings in your language of choice. Currently, the two
client libraries officially maintained by the Myxine project are those for
[Python](https://pypi.org/project/myxine-client/) and
[Haskell](https://hackage.haskell.org/package/myxine-client).
If you're interested in writing Myxine bindings for a new language, you'll want
to read the [API documentation](API.md), and perhaps reference one or more of
the [existing client libraries](clients/). Don't be afraid to ask for help by
[opening an issue](https://github.com/kwf/myxine/issues/new), and please
do contribute back your work by submitting a pull request!
### An example in Python
If a picture is worth a thousand words, how many pictures is a visual
interaction worth?
Below is a simple, complete Myxine application in Python. For this example to
work, you will need to install the Python client library:
``` bash
$ pip3 install myxine-client
```
To run the example, first make sure `myxine` is running on your computer:
``` bash
$ myxine
Running at: http://127.0.0.1:1123
```
Then, in another terminal window, run the script:
``` bash
$ ./examples/python/follow.py
```
Finally, navigate in your web browser to
[http://localhost:1123](http://localhost:1123), and play around! You can press
Ctrl+C in your terminal to stop the application.
You can find this example and others in the [examples](examples/) directory,
categorized in subdirectories by language of implementation.
**Without further ado, `follow.py`:**
``` python
#!/usr/bin/env python3
import random
import myxine
class Page:
# The model of the page
def __init__(self):
self.x, self.y = 150, 150
self.hue = random.uniform(0, 360)
self.radius = 75
# Draw the page's model as a fragment of HTML
def draw(self):
circle_style = f'''
position: absolute;
height: {round(self.radius*2)}px;
width: {round(self.radius*2)}px;
top: {self.y}px;
left: {self.x}px;
transform: translate(-50%, -50%);
border-radius: 50%;
border: {round(self.radius/2)}px solid hsl({round(self.hue)}, 80%, 80%);
background: hsl({round(self.hue+120)}, 80%, 75%)
'''
background_style = f'''
position: absolute;
overflow: hidden;
width: 100vw;
height: 100vh;
background: hsl({round(self.hue-120)}, 80%, 90%);
'''
instructions_style = f'''
position: absolute;
bottom: 30px;
left: 30px;
font-family: sans-serif;
font-size: 22pt;
user-select: none;
'''
return f'''
Move the mouse to move the circle Scroll to change the circle's size Ctrl + Scroll to change the color scheme Click to randomize the color scheme
'''
# Change the page's model in response to a browser event
def react(self, event):
if event.event() == 'mousemove':
self.x = event.clientX
self.y = event.clientY
elif event.event() == 'mousedown':
self.hue = (self.hue + random.uniform(30, 330)) % 360
elif event.event() == 'wheel':
if event.ctrlKey:
self.hue = (self.hue + event.deltaY * -0.1) % 360
else:
self.radius += event.deltaY * -0.2
self.radius = min(max(self.radius, 12), 1000)
# The page's event loop
def run(self, path):
myxine.update(path, self.draw()) # Draw the page in the browser.
try:
for event in myxine.events(path): # For each browser event,
self.react(event) # update our model of the page,
myxine.update(path, self.draw()) # then re-draw it in the browser.
except KeyboardInterrupt:
pass # Press Ctrl-C to quit.
if __name__ == '__main__':
Page().run('/') # Run the page on the root path.
```
In the above, you can see each of the steps of Myxine's interaction model
represented as separate Python constructs:
- The `Page` class has fields that track the state of the application.
- The `draw` function is a pure function from the current application state to
its representation as HTML.
- The `react` function updates the `Page`'s state in response to some event that
occurred in the browser.
- The `run` function ties it all together by looping over all events in the
browser, updating the application state in reaction to each, and sending a new
HTML body to the browser, to be immediately displayed to you!
While different client libraries may represent this pattern using different
language-specific idioms, the basic structure is the same. And despite its
simplicity, it's fast!