asoma
An anarchic society of machines.
Connecting machines is easy; getting them to behave as one is the work. Asoma is a small virtual machine and a language for it, built so that the boundary between machines matters less.
Anything with the machine in it — a laptop, a browser, a board, a Python process — offers what it can do as addresses, and what it does is a program: written anywhere, sent to it, composed from several machines at once, replaced without a compiler. A program is a bundle of OpenSoundControl (OSC) messages, and so is the running machine itself — its values, its names, the rest of its program — so a running program can be sent too, and carry on where it lands.
The language
Each box below holds a live machine, compiled to WebAssembly. Every line you write is a message to a local OSC server: the machine inside the box, on this page. Edit the program and press Run.
A message is an address and a value. Parentheses gather messages into a
bundle, a colon binds a value to a name, and /o/get reads a message
back out of a bundle by its address:
# a bundle of messages, bound to a name /voice : (/freq : 440, /amp : 0.5), /voice, (/voice, "/freq") /o/get
Each line answers with a message of its own; the leading colon marks one
with no address. Commas separate the lines of a program, and # starts a
comment.
A call puts the arguments first and the function after them, as if the bundle of arguments were sent to the machine and then the function to apply to it:
/freq : 440, # arguments first, then the function /octave : (/freq, 2) /o/mul, (/freq, /octave)
A function is a body in curly braces — a bundle kept for later rather
than run now — and the names of its parameters. /o/defn puts them
together, and the result is called like anything built in:
# a body kept for later, and its parameter
/octave/up : ({ (/hz, 2) /o/mul }, "/hz") /o/defn,
(/freq : 220, /octave : (/freq) /octave/up)
That function is itself a bundle of messages: the body’s instructions
and the parameter list. /o/inspect shows them:
/octave/up : ({ (/hz, 2) /o/mul }, "/hz") /o/defn,
/octave/up,
# the function, as the messages it is made of
(/octave/up) /o/inspect
And so is the machine’s own memory. />/_e puts the environment on the
table: every name this program has bound, as one bundle. This is what
would travel if the program moved to another machine. (The two empty
bundles are scope markers; the tour explains them.)
/freq : 440,
/octave/up : ({ (/hz, 2) /o/mul }, "/hz") /o/defn,
# the machine's memory: everything bound so far
/>/_e
The guided tour of osen goes on from here — choosing, lists, folding, and how names are found.
Where it runs
The same machine runs in each of these, a program written for one runs on the others, and any of them can send a program to any other, and most can send a running one.
- Command line — a prompt, files and UDP; the easiest to start with.
- Max — two externals, and Max objects written in the language itself.
- Browser — WebAssembly, WebAudio and a WebSocket; the boxes above, and a playground.
- Microcontroller — Arduino-core firmware for ESP32 and Photon 2 boards; GPIO and WiFi.
- Python —
import ose, and a module that lets the machine call Python.