6 releases
0.2.2 | Aug 17, 2020 |
---|---|
0.2.1 | Aug 5, 2020 |
0.2.0 | Jul 23, 2020 |
0.1.0 | Jun 19, 2020 |
0.0.2 | Feb 20, 2020 |
#423 in GUI
115KB
1.5K
SLoC
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 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:
- To enable programmers who don't necessarily specialize in UI design to build appealing interactive applications without learning a complex UI framework.
- To make it easy to write correct, efficient, and idiomatic bindings to Myxine from almost any programming language.
- To be as fast as possible while consuming as few resources as possible.
Here's how it works:
- You start Myxine and open your browser to a page it is serving.
- From your programming language of choice, you send some HTML to Myxine, and
it instantly appears, replacing the contents of that page's
<body>
. - 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.
- 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. After you have that set up,
install the Myxine server:
$ 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 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 and Haskell.
If you're interested in writing Myxine bindings for a new language, you'll want to read the API documentation, and perhaps reference one or more of the existing client libraries. Don't be afraid to ask for help by opening an issue, 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:
$ pip3 install myxine-client
To run the example, first make sure myxine
is running on your computer:
$ myxine
Running at: http://127.0.0.1:1123
Then, in another terminal window, run the script:
$ ./examples/python/follow.py
Finally, navigate in your web browser to 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 directory, categorized in subdirectories by language of implementation.
Without further ado, follow.py
:
#!/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'''
<div style="{background_style}">
<div style="{instructions_style}">
<b>Move the mouse</b> to move the circle<br/>
<b>Scroll</b> to change the circle's size<br/>
<b>Ctrl + Scroll</b> to change the color scheme<br/>
<b>Click</b> to randomize the color scheme<br/>
</div>
<div style="{circle_style}"></div>
</div>
'''
# 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 thePage
'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!
Dependencies
~17–28MB
~424K SLoC