{"id":30602228,"url":"https://github.com/plaidfinch/myxine","last_synced_at":"2025-12-11T23:18:35.685Z","repository":{"id":37183614,"uuid":"230528340","full_name":"plaidfinch/myxine","owner":"plaidfinch","description":"Get a GUI fast in any language under the sea!","archived":false,"fork":false,"pushed_at":"2023-01-31T23:37:10.000Z","size":1111,"stargazers_count":47,"open_issues_count":24,"forks_count":1,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-08-10T23:29:22.427Z","etag":null,"topics":["command-line","gui","html","reactive","rust","scripting"],"latest_commit_sha":null,"homepage":"","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/plaidfinch.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-12-27T22:43:57.000Z","updated_at":"2023-03-24T10:58:51.000Z","dependencies_parsed_at":"2023-02-17T00:01:43.245Z","dependency_job_id":null,"html_url":"https://github.com/plaidfinch/myxine","commit_stats":null,"previous_names":["kwf/myxine"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/plaidfinch/myxine","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plaidfinch%2Fmyxine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plaidfinch%2Fmyxine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plaidfinch%2Fmyxine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plaidfinch%2Fmyxine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/plaidfinch","download_url":"https://codeload.github.com/plaidfinch/myxine/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plaidfinch%2Fmyxine/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272771239,"owners_count":24990325,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-29T02:00:10.610Z","response_time":87,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["command-line","gui","html","reactive","rust","scripting"],"created_at":"2025-08-30T00:19:20.720Z","updated_at":"2025-12-11T23:18:35.651Z","avatar_url":"https://github.com/plaidfinch.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Myxine: a slithery sea-friend to help you _get GUI fast_\n\n\u003ctable style=\"border: 0\"\u003e\n\u003ctr style=\"border: 0\"\u003e\n  \u003ctd width=\"40%\" style=\"border: 0\"\u003e\n    \u003cimg src=\"https://github.com/kwf/myxine/raw/master/images/myxine_glutinosa.png\" target=\"_blank\" width=\"425px\" alt=\"woodcut sketch of myxine glutinosa, the hagfish\"\u003e\n  \u003c/td\u003e\n  \u003ctd style=\"border: 0\"\u003e\n    \u003cp\u003eHagfish, a.k.a. \u003ca href=\"https://en.wikipedia.org/wiki/Hagfish\"\u003e\u003ci\u003emyxine glutinosa\u003c/i\u003e\u003c/a\u003e, are eel-like sea creatures best known for their ability to make a lot of slime.\u003c/p/\u003e\n    \u003cp\u003eBy analogy, \u003ccode\u003emyxine\u003c/code\u003e quickly reduces the friction in creating a dynamic graphical interface, helping you to \u003cb\u003e\u003ci\u003eget GUI fast\u003c/i\u003e\u003c/b\u003e in any language under the sea.\u003c/p\u003e\n  \u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003cp\u003e\n    \u003ca href=\"https://crates.io/crates/myxine\"\u003e\u003cimg src=\"https://img.shields.io/crates/d/myxine\" /\u003e\u003c/a\u003e\n    \u003ca href=\"https://crates.io/crates/myxine\"\u003e\u003cimg src=\"https://img.shields.io/crates/v/myxine\" /\u003e\u003c/a\u003e\n    \u003ca href=\"https://pypi.org/project/myxine-client/\"\u003e\u003cimg src=\"https://img.shields.io/pypi/v/myxine-client\" /\u003e\u003c/a\u003e\n    \u003ca href=\"https://hackage.haskell.org/package/myxine-client\"\u003e\u003cimg src=\"https://img.shields.io/hackage/v/myxine-client\" /\u003e\u003c/a\u003e\n    \u003ca href=\"https://crates.io/crates/myxine\"\u003e\u003cimg src=\"https://img.shields.io/crates/l/myxine\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nMyxine is a local web server that enables you to create interactive applications\nin your web browser from the comfort of your favorite programming language. It's\ndesigned to satisfy three explicit goals:\n\n1. To enable programmers who don't necessarily specialize in UI design to build\n   appealing interactive applications without learning a complex UI framework.\n2. To make it easy to write correct, efficient, and idiomatic bindings to Myxine\n   from almost any programming language.\n3. To be as fast as possible while consuming as few resources as possible.\n\n**Here's how it works:**\n\n1. You start Myxine and open your browser to a page it is serving.\n2. From your programming language of choice, you send some HTML to Myxine, and\n   it instantly appears, replacing the contents of that page's `\u003cbody\u003e`.\n3. You then request a subscription to whichever browser events in which you're\n   interested, and Myxine notifies you when each one occurs, eliding those you\n   don't care about.\n4. You can then react to those events, updating the page again to reflect what\n   you'd like it to look like now. Rinse and repeat!\n\nMyxine handles the hard work of optimizing this process to minimize latency and\ncomputational load: it can handle thousands of requests per second and\ntranslate them into smooth, flicker-free animations at up to 60 frames per\nsecond.\n\n## Installing\n\nTo install the Myxine server, you will need a recent version of the Rust\nprogramming langauge and its build tool, `cargo`. If you don't have it, [here's\nthe quick-start for installing\nRust](https://www.rust-lang.org/learn/get-started). After you have that set up,\ninstall the Myxine server:\n\n```bash\n$ cargo install myxine\n```\n\nInstalling a client library for Myxine will require steps specific to that\nlibrary: consult the appropriate library's documentation to find out how to\ninstall it.\n\n## Building interactive applications\n\nYou can interact directly with the server via HTTP requests (see the [API\ndocumentation](API.md) for details), but most likely you will want to use a\nlibrary of lightweight bindings in your language of choice. Currently, the two\nclient libraries officially maintained by the Myxine project are those for\n[Python](https://pypi.org/project/myxine-client/) and\n[Haskell](https://hackage.haskell.org/package/myxine-client).\n\nIf you're interested in writing Myxine bindings for a new language, you'll want\nto read the [API documentation](API.md), and perhaps reference one or more of\nthe [existing client libraries](clients/). Don't be afraid to ask for help by\n[opening an issue](https://github.com/kwf/myxine/issues/new), and please\ndo contribute back your work by submitting a pull request!\n\n### An example in Python\n\nIf a picture is worth a thousand words, how many pictures is a visual\ninteraction worth?\n\nBelow is a simple, complete Myxine application in Python. For this example to\nwork, you will need to install the Python client library:\n\n``` bash\n$ pip3 install myxine-client\n```\n\nTo run the example, first make sure `myxine` is running on your computer:\n\n``` bash\n$ myxine\nRunning at: http://127.0.0.1:1123\n```\n\nThen, in another terminal window, run the script:\n\n``` bash\n$ ./examples/python/follow.py\n```\n\nFinally, navigate in your web browser to\n[http://localhost:1123](http://localhost:1123), and play around! You can press\nCtrl+C in your terminal to stop the application.\n\nYou can find this example and others in the [examples](examples/) directory,\ncategorized in subdirectories by language of implementation.\n\n**Without further ado, `follow.py`:**\n\n``` python\n#!/usr/bin/env python3\n\nimport random\nimport myxine\n\nclass Page:\n    # The model of the page\n    def __init__(self):\n        self.x, self.y = 150, 150\n        self.hue = random.uniform(0, 360)\n        self.radius = 75\n\n    # Draw the page's model as a fragment of HTML\n    def draw(self):\n        circle_style = f'''\n        position: absolute;\n        height: {round(self.radius*2)}px;\n        width: {round(self.radius*2)}px;\n        top: {self.y}px;\n        left: {self.x}px;\n        transform: translate(-50%, -50%);\n        border-radius: 50%;\n        border: {round(self.radius/2)}px solid hsl({round(self.hue)}, 80%, 80%);\n        background: hsl({round(self.hue+120)}, 80%, 75%)\n        '''\n        background_style = f'''\n        position: absolute;\n        overflow: hidden;\n        width: 100vw;\n        height: 100vh;\n        background: hsl({round(self.hue-120)}, 80%, 90%);\n        '''\n        instructions_style = f'''\n        position: absolute;\n        bottom: 30px;\n        left: 30px;\n        font-family: sans-serif;\n        font-size: 22pt;\n        user-select: none;\n        '''\n        return f'''\n        \u003cdiv style=\"{background_style}\"\u003e\n            \u003cdiv style=\"{instructions_style}\"\u003e\n                \u003cb\u003eMove the mouse\u003c/b\u003e to move the circle\u003cbr/\u003e\n                \u003cb\u003eScroll\u003c/b\u003e to change the circle's size\u003cbr/\u003e\n                \u003cb\u003eCtrl + Scroll\u003c/b\u003e to change the color scheme\u003cbr/\u003e\n                \u003cb\u003eClick\u003c/b\u003e to randomize the color scheme\u003cbr/\u003e\n            \u003c/div\u003e\n            \u003cdiv style=\"{circle_style}\"\u003e\u003c/div\u003e\n        \u003c/div\u003e\n        '''\n\n    # Change the page's model in response to a browser event\n    def react(self, event):\n        if event.event() == 'mousemove':\n            self.x = event.clientX\n            self.y = event.clientY\n        elif event.event() == 'mousedown':\n            self.hue = (self.hue + random.uniform(30, 330)) % 360\n        elif event.event() == 'wheel':\n            if event.ctrlKey:\n                self.hue = (self.hue + event.deltaY * -0.1) % 360\n            else:\n                self.radius += event.deltaY * -0.2\n                self.radius = min(max(self.radius, 12), 1000)\n\n    # The page's event loop\n    def run(self, path):\n        myxine.update(path, self.draw())          # Draw the page in the browser.\n        try:\n            for event in myxine.events(path):     # For each browser event,\n                self.react(event)                 # update our model of the page,\n                myxine.update(path, self.draw())  # then re-draw it in the browser.\n        except KeyboardInterrupt:\n            pass                                  # Press Ctrl-C to quit.\n\nif __name__ == '__main__':\n    Page().run('/')  # Run the page on the root path.\n```\n\nIn the above, you can see each of the steps of Myxine's interaction model\nrepresented as separate Python constructs:\n\n- The `Page` class has fields that track the state of the application.\n- The `draw` function is a pure function from the current application state to\n  its representation as HTML.\n- The `react` function updates the `Page`'s state in response to some event that\n  occurred in the browser.\n- The `run` function ties it all together by looping over all events in the\n  browser, updating the application state in reaction to each, and sending a new\n  HTML body to the browser, to be immediately displayed to you!\n  \nWhile different client libraries may represent this pattern using different\nlanguage-specific idioms, the basic structure is the same. And despite its\nsimplicity, it's fast!\n  \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplaidfinch%2Fmyxine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fplaidfinch%2Fmyxine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplaidfinch%2Fmyxine/lists"}