Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ Enabled via `processors` config:
| Zig (Wasm) | `zig-wasm`, `zig` | Zig WASM |
| V (Wasm) | `v-wasm`, `v`, `vlang`, `wasm.v`, `vsh` | V compiler in WASM |
| Janet (Wasm) | `janet-wasm`, `janet`, `wasm.janet` | Janet interpreter (WASM) |
| Elixir (Wasm) | `elixir-wasm`, `ex`, `exs`, `elixir`, `wasm.ex` | Elixir compiler in WASM |
| Erlang (Wasm) | `erlang-wasm`, `erl`, `erlang`, `wasm.erl` | Erlang runtime in WASM (AtomVM) |
| Tcl | `tcl` | Tcl interpreter |
| WebAssembly | `wat`, `wast`, `wasm`, `webassembly` | WAT format |
| Assembly (Wasm) | `assembly-wasm`, `assembly`, `asm`, `asm-wasm`, `x86`, `x86-64`, `nasm`, `wasm.asm` | Keystone + Unicorn (WASM), x86-64 assembly |
Expand Down
136 changes: 136 additions & 0 deletions docs/docs/languages/elixir-wasm.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Elixir (Wasm)

Elixir is a dynamic, functional language for building scalable and maintainable applications, powered by the Erlang VM.

In LiveCodes, Elixir runs in the browser using WebAssembly: the Elixir compiler and standard library execute on [AtomVM](https://atomvm.org/), a lightweight Erlang VM with a WebAssembly target. No server is involved.

## Usage

Demo:

import LiveCodes from '../../src/components/LiveCodes.tsx';

export const elixirConfig = {
activeEditor: 'script',
script: {
language: 'elixir-wasm',
content: `IO.puts("Hello, Elixir!")`,
},
mode: 'simple',
editor: 'auto',
tools: {
status: 'full',
},
};

<LiveCodes config={elixirConfig}></LiveCodes>

### Communication with JavaScript

The program runs in a persistent worker owned by the result page, not in the result page's JavaScript
context. The browser global `livecodes.elixir` object is the result page's interface to that worker:

- `livecodes.elixir.input`: the standard input passed to the program.
- `livecodes.elixir.loaded`: a promise that resolves after the initial program run has completed.
For a non-empty program, that includes downloading and booting the runtime; an empty initial
program resolves before the runtime is downloaded, and initialization starts on the first
non-empty run. It rejects if initialization fails. Other helpers should be used after it resolves.
- `livecodes.elixir.output`: the program's standard output.
- `livecodes.elixir.value`: the inspected return value of the program.
- `livecodes.elixir.warnings`: warnings emitted while compiling or running the program.
- `livecodes.elixir.error`: the syntax or runtime error, if the program failed.
- `livecodes.elixir.exitCode`: the exit code of the program.
- `livecodes.elixir.run`: runs the current code with new input, reusing the booted VM, and returns a
promise that resolves with `output`, `value`, `warnings`, `error` and `exitCode`.
- `livecodes.elixir.retry`: restarts the runtime after a failed initialization. A VM crash in a
normal run is recovered automatically on the next run.

Example:

<LiveCodes template="elixir-wasm" params={{ activeEditor: 'markup' }} height="80vh"></LiveCodes>

```elixir
IO.puts("Hello, Elixir!")

squares = Enum.map(1..5, &(&1 * &1))
IO.inspect(squares, label: "squares")
```

### Runtime lifecycle

The VM runs in a persistent worker owned by the result window. Updating the code
rewrites the result document but does not destroy the worker, so normal edits
reuse the booted VM instead of downloading and initializing it again.

Each worker boot compiles a small Elixir prelude. That takes a few seconds on
the first run; subsequent runs only evaluate the user code. Evaluations time out
after 60 seconds. If a newer run is waiting and the active evaluation does not
finish within 10 seconds, the worker is terminated and recreated so the newer
run is not blocked. Pending edits are coalesced to the latest one. A VM crash is
reported in `error`; the next run starts a fresh worker automatically. Use
`retry()` when initialization itself failed.

## Language Info

### Name

`elixir-wasm`

### Aliases / Extensions

`ex`, `exs`, `elixir`, `elixir-wasm`, `wasm.ex`

### Editor

`script`

## Compiler

The Elixir toolchain compiled to WebAssembly, running on
[AtomVM](https://github.com/atomvm/AtomVM) with the
[Popcorn](https://popcorn.swmansion.com/) browser runtime. The single-threaded runtime is published
as [`@hmh-3080/atomvm-wasm`](https://www.npmjs.com/package/@hmh-3080/atomvm-wasm); the Elixir
and Erlang bytecode bundle is pinned to
`live-codes/elixir-browser-eval@6b354dab35fb7454ba2812f935b52b398ca1bec8`.

### Version

Elixir 1.17.3 on OTP 27, with AtomVM 0.7.0-dev (32-bit).

### Limitations

AtomVM implements a subset of the Erlang VM and its standard library. Features that depend on a full
operating system (for example file I/O and OS processes) may be missing or behave differently.
Examples include `File.write!/2` (`not_implemented`), `Date.utc_today/0`, `Task.async/await`,
`Regex.match?/2`, and the `JSON` module. `System.halt/1` terminates the VM instead of returning an
exit status; the runtime reports the crash and the next run boots a fresh worker. Frameworks that
require a server, such as Phoenix and LiveView, are out of scope for the playground.

The runtime is a single-threaded AtomVM build. It does not use WebAssembly threads,
`SharedArrayBuffer`, or `Cross-Origin-Opener-Policy` / `Cross-Origin-Embedder-Policy`
headers, so it runs in normal (non-isolated) result pages and keeps popup-based OAuth working.

## Code Formatting

Not currently supported.

## Live Reload

By default, new code changes are sent to the result page for re-evaluation without a full page reload
or a VM restart. This behavior can be disabled by adding the code comment `# __livecodes_reload__` to
the Elixir code, which forces a full page reload.

This comment can be added in the `hiddenContent` property of the editor for embedded playgrounds.

## Starter Template

https://livecodes.io/?template=elixir-wasm

## Links

- [Elixir official website](https://elixir-lang.org/)
- [Elixir documentation](https://hexdocs.pm/elixir/)
- [Learn X in Y minutes, where X=Elixir](https://learnxinyminutes.com/elixir/)
- [AtomVM](https://atomvm.org/)
- [@hmh-3080/atomvm-wasm](https://www.npmjs.com/package/@hmh-3080/atomvm-wasm)
- [Popcorn: Elixir and Wasm](https://popcorn.swmansion.com/)
150 changes: 150 additions & 0 deletions docs/docs/languages/erlang-wasm.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Erlang (Wasm)

Erlang is a functional, concurrent language for building scalable and fault-tolerant systems.

In LiveCodes, Erlang runs in the browser using WebAssembly on [AtomVM](https://atomvm.org/), the same lightweight Erlang VM used by [Elixir (Wasm)](./elixir-wasm.mdx). No server is involved.

## Usage

Demo:

import LiveCodes from '../../src/components/LiveCodes.tsx';

export const erlangConfig = {
activeEditor: 'script',
script: {
language: 'erlang-wasm',
content: `io:format("Hello, Erlang!~n").`,
},
mode: 'simple',
editor: 'auto',
tools: {
status: 'full',
},
};

<LiveCodes config={erlangConfig}></LiveCodes>

### Communication with JavaScript

The program runs in a persistent worker owned by the result page, not in the result page's JavaScript
context. The browser global `livecodes.erlang` object is the result page's interface to that worker:

- `livecodes.erlang.input`: the standard input passed to the program.
- `livecodes.erlang.loaded`: a promise that resolves after the runtime has downloaded and the first
program run has completed. It rejects if the environment fails to load. Other helpers should be
used after it resolves.
Comment on lines +34 to +36

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- `livecodes.erlang.loaded`: a promise that resolves after the runtime has downloaded and the first
program run has completed. It rejects if the environment fails to load. Other helpers should be
used after it resolves.
- `livecodes.erlang.loaded`: a promise that resolves after the initial program run has completed.
For a non-empty program, that includes downloading and booting the runtime; an empty initial
program resolves before the runtime is downloaded, and initialization starts on the first
non-empty run. It rejects if initialization fails. Other helpers should be used after it resolves.

- `livecodes.erlang.output`: the program's standard output.
- `livecodes.erlang.value`: the inspected return value of the program.
- `livecodes.erlang.warnings`: warnings emitted while compiling or running the program.
- `livecodes.erlang.error`: the syntax or runtime error, if the program failed.
- `livecodes.erlang.exitCode`: the exit code of the program.
- `livecodes.erlang.run`: runs the current code with new input, reusing the booted VM, and returns a
promise that resolves with `output`, `value`, `warnings`, `error` and `exitCode`.
- `livecodes.erlang.retry`: restarts the runtime after a failed initialization. A VM crash in a
normal run is recovered automatically on the next run.

Example:

<LiveCodes template="erlang-wasm" params={{ activeEditor: 'markup' }} height="80vh"></LiveCodes>

```erlang
io:format("Hello, Erlang!~n"),
[X * X || X <- lists:seq(1, 5)].
```

### Modules and stdin

Erlang expressions are comma-separated and must end with a dot. Code that starts with `-module(`
is compiled and loaded into the running VM, so it stays callable in later runs:

```erlang
-module(m).
-export([double/1]).

double(X) -> X * 2.
```

Then run `m:double(21).` in a later run. `io:get_line("")` reads the standard input, and
`erlang:halt(0)` (or a bare `halt(0)`) sets the exit code.

The shared stdin device returns binaries. Use `binary_to_list/1` when an API needs a charlist,
and remove the trailing newline with `binary:replace(Value, <<"\n">>, <<>>, [global])`
(`string:trim/1` is not reliable on AtomVM).

### Runtime lifecycle

The VM runs in a persistent worker owned by the result window. Updating the code
rewrites the result document but does not destroy the worker, so normal edits
reuse the booted VM instead of downloading and initializing it again.

Each worker boot compiles a small Elixir prelude that provides the shared I/O
device for Erlang and Elixir. That takes a few seconds on the first run;
subsequent runs only evaluate the user code. Evaluations time out after 60
seconds. If a newer run is waiting and the active evaluation does not finish
within 10 seconds, the worker is terminated and recreated so the newer run is
not blocked. Pending edits are coalesced to the latest one. A VM crash is
reported in `error`; the next run starts a fresh worker automatically. Use
`retry()` when initialization itself failed.

## Language Info

### Name

`erlang-wasm`

### Aliases / Extensions

`erl`, `erlang`, `erlang-wasm`, `wasm.erl`

### Editor

`script`

## Compiler

The Erlang runtime compiled to WebAssembly, running on
[AtomVM](https://github.com/atomvm/AtomVM) with the
[Popcorn](https://popcorn.swmansion.com/) browser runtime. The single-threaded runtime is published
as [`@hmh-3080/atomvm-wasm`](https://www.npmjs.com/package/@hmh-3080/atomvm-wasm); it shares the
pinned `live-codes/elixir-browser-eval@6b354dab35fb7454ba2812f935b52b398ca1bec8` bytecode bundle
with Elixir (Wasm).

### Version

AtomVM 0.7.0-dev (32-bit), with the Erlang runtime bundled in the shared Elixir/Erlang Wasm bundle.

### Limitations

AtomVM implements a subset of the Erlang VM and its standard library. Features that depend on a full
operating system (for example file I/O and OS processes) may be missing or behave differently.
Frameworks and applications that require a server are out of scope for the playground.

The runtime is a single-threaded AtomVM build. It does not use WebAssembly threads,
`SharedArrayBuffer`, or `Cross-Origin-Opener-Policy` / `Cross-Origin-Embedder-Policy`
headers, so it runs in normal (non-isolated) result pages and keeps popup-based OAuth working.

## Code Formatting

Not currently supported.

## Live Reload

By default, new code changes are sent to the result page for re-evaluation without a full page reload
or a VM restart. This behavior can be disabled by adding the code comment `% __livecodes_reload__` to
the Erlang code, which forces a full page reload.

This comment can be added in the `hiddenContent` property of the editor for embedded playgrounds.

## Starter Template

https://livecodes.io/?template=erlang-wasm

## Links

- [Erlang official website](https://www.erlang.org/)
- [Erlang documentation](https://www.erlang.org/docs)
- [Learn X in Y minutes, where X=Erlang](https://learnxinyminutes.com/erlang/)
- [AtomVM](https://atomvm.org/)
- [@hmh-3080/atomvm-wasm](https://www.npmjs.com/package/@hmh-3080/atomvm-wasm)
- [Popcorn: Elixir and Wasm](https://popcorn.swmansion.com/)
2 changes: 2 additions & 0 deletions docs/src/components/LanguageSliders.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ export default function Sliders() {
{ name: 'dart', title: 'Dart' },
{ name: 'flutter', title: 'Flutter' },
{ name: 'v-wasm', title: 'V (Wasm)' },
{ name: 'elixir-wasm', title: 'Elixir (Wasm)' },
{ name: 'erlang-wasm', title: 'Erlang (Wasm)' },
{ name: 'rust-wasm', title: 'Rust (Wasm)' },
{ name: 'scala-wasm', title: 'Scala (Wasm)' },
{ name: 'swift-wasm', title: 'Swift (Wasm)' },
Expand Down
2 changes: 2 additions & 0 deletions docs/src/components/TemplateList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ const templates = [
{ name: 'crystal-wasm', title: 'Crystal Starter', thumbnail: 'crystal.svg' },
{ name: 'odin-wasm', title: 'Odin Starter', thumbnail: 'odin.svg' },
{ name: 'v-wasm', title: 'V Starter', thumbnail: 'v.svg' },
{ name: 'elixir-wasm', title: 'Elixir Starter', thumbnail: 'elixir.png' },
{ name: 'erlang-wasm', title: 'Erlang Starter', thumbnail: 'erlang.svg' },
{ name: 'cpp-wasm', title: 'C++ (Wasm) Starter', thumbnail: 'cpp.svg' },
{ name: 'objc-wasm', title: 'Objective-C (Wasm) Starter', thumbnail: 'objective-c.svg' },
{ name: 'rust-wasm', title: 'Rust (Wasm) Starter', thumbnail: 'rust.svg' },
Expand Down
Loading
Loading