Skip to main content

Odin (Wasm)

Odin is a general-purpose programming language with distinct typing, built for high performance, modern systems and built-in data-oriented data types.

In LiveCodes, Odin is compiled and run entirely in the browser by Odin's compiler, compiled to WebAssembly. Nothing runs on a server.

Usage​

Demo:

show code
import { createPlayground } from 'livecodes';

const options = {
"config": {
"mode": "simple",
"activeEditor": "script",
"script": {
"language": "odin-wasm",
"content": "package main\n\nimport \"core:fmt\"\n\nmain :: proc() {\n\tprogram := \"+ + * - /\"\n\taccumulator := 0\n\n\tfor token in program {\n\t\tswitch token {\n\t\tcase '+': accumulator += 1\n\t\tcase '-': accumulator -= 1\n\t\tcase '*': accumulator *= 2\n\t\tcase '/': accumulator /= 2\n\t\tcase: // ignore everything else\n\t\t}\n\t}\n\n\tfmt.printfln(\"The program \\\"%s\\\" calculates the value %d\", program, accumulator)\n}\n"
},
"tools": {
"status": "full"
}
}
};
createPlayground('#container', options);

Communication with JavaScript​

The code is compiled in a web worker. Where it then runs depends on the target it is built for, which is set in custom settings:

TargetRuns onWhat it reaches
wasi_wasm32 (default)a WASI host, in the workercore:fmt, standard input, arguments, exit codes
js_wasm32this page, on Odin's own JS runtimethe DOM and a <canvas>, through core:sys/wasm/js
Custom Settings
{
"odin-wasm": { "target": "js_wasm32" }
}

Either way the following helpers are available in the browser global livecodes.odinWasm object:

  • livecodes.odinWasm.input: The standard input passed to the program.
  • livecodes.odinWasm.loaded: A promise that resolves when the compiler is loaded and the program has run once.
  • livecodes.odinWasm.output: The program's output.
  • livecodes.odinWasm.error: The compiler diagnostics, if the code failed to compile.
  • livecodes.odinWasm.exitCode: The program's exit code.
  • livecodes.odinWasm.run: A function that compiles and runs the code with new input. It takes a string as input and returns a promise that resolves with an object containing output, error and exitCode.

Example:

show code
import { createPlayground } from 'livecodes';

const options = {
"template": "odin-wasm",
"params": {
"activeEditor": "markup"
}
};
createPlayground('#container', options);

JavaScript interop, the DOM and canvas​

With target: "js_wasm32" the program runs on the page, on Odin's own JavaScript runtime, so core:sys/wasm/js reaches what the page has: the document, elements, events, WebGL and a canvas.

  • js.evaluate(str) evaluates JavaScript in the page — the shortest path to anything the bindings do not wrap.
  • The DOM bindings: js.get_element_value_string, js.set_element_value_string, js.set_element_style, js.set_document_title, js.get_bounding_client_rect.
  • The event listeners in core:sys/wasm/js/events, and the webgl / webgl2 bindings for a canvas.
  • A program that exports step :: proc(dt: f64, ctx: rawptr) -> bool is driven once per animation frame, as Odin's runtime does it, and the canvas is yours to draw on. Such a program still needs a main: Odin requires an entry point.

A canvas, drawn from Odin through js.evaluate:

show code
import { createPlayground } from 'livecodes';

const options = {
"config": {
"mode": "simple",
"activeEditor": "script",
"markup": {
"language": "html",
"content": "<canvas id=\"canvas\" width=\"480\" height=\"240\"></canvas>\n<script>\n window.drawDot = () => {\n const c = document.getElementById('canvas');\n const ctx = c.getContext('2d');\n const t = performance.now() / 1000;\n ctx.fillStyle = '#0f1116';\n ctx.fillRect(0, 0, c.width, c.height);\n ctx.fillStyle = '#3b82f6';\n ctx.beginPath();\n ctx.arc(c.width / 2 + Math.cos(t) * 90, c.height / 2 + Math.sin(t) * 55, 16, 0, Math.PI * 2);\n ctx.fill();\n };\n</script>"
},
"script": {
"language": "odin-wasm",
"content": "package main\n\nimport \"core:fmt\"\nimport \"core:sys/wasm/js\"\n\nmain :: proc() {\n\tfmt.println(\"drawing on the canvas\")\n}\n\n@(export)\nstep :: proc(dt: f64, _: rawptr) -> bool {\n\tjs.evaluate(\"drawDot()\")\n\treturn true\n}\n"
},
"customSettings": {
"odin-wasm": {
"target": "js_wasm32"
}
},
"tools": {
"status": "full"
}
}
};
createPlayground('#container', options);

package main

import "core:fmt"
import "core:sys/wasm/js"

main :: proc() {
fmt.println("drawing on the canvas")
}

@(export)
step :: proc(dt: f64, _: rawptr) -> bool {
js.evaluate("drawDot()")
return true
}

The page's markup provides the canvas and the drawDot function; the JavaScript runtime, not the program, owns the DOM, so the two meet through js.evaluate.

Reading standard input​

The input (livecodes.odinWasm.input, or whatever is passed to run) reaches the program byte for byte — nothing appends a newline to it. That matters for a line-based program, because Odin's bufio.reader_read_string returns the data together with .EOF when the last line has no trailing newline (the same as Go's ReadString). Read the data first, then stop on the error:

for {
line, err := bufio.reader_read_string(&reader, '\n')
if len(line) > 0 {
// use the line
}
if err != nil {
break
}
}

Language Info​

Name​

odin-wasm

Aliases and Extensions​

odin

Editor​

script

Compiler​

Odin's compiler, compiled to WebAssembly by wodin.

Version​

Odin Compiler Version: dev-2026-09

Custom Settings​

Custom settings added to the property odin-wasm are passed as a JSON object to the compiler during compile. Please check the documentation for full reference.

Please note that custom settings should be valid JSON (i.e. functions are not allowed).

Example:

Custom Settings
{
"odin-wasm": {}
}

args is passed to the program as its command-line arguments, compileArgs is added to the odin build command line, and target selects the target the program is built for (wasi_wasm32, the default, which runs on a WASI host, or js_wasm32, which runs on the browser's JavaScript runtime).

Example:

Custom Settings
{
"odin-wasm": {
"args": ["--flag", "value"],
"compileArgs": ["-o:speed"]
}
}

Live Reload​

By default, new code changes are sent to the result page for re-evaluation without a full page reload, avoiding the need to reinitialize the Odin environment. This behavior can be disabled by adding the code comment // __livecodes_reload__ to the Odin code, which forces a full page reload.

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

Limitations​

  • The vendor collection is not shipped: a program that imports a vendor: package will not find it.
  • No threads, files, sockets or subprocesses: WASI preview 1 has none, and a program is given an empty filesystem.
  • A program that never returns blocks the tab; both targets run synchronously.

Example Usage​

package main

import "core:fmt"

main :: proc() {
fmt.println("hello from Odin, compiled in your browser")

for n in 1..=5 {
fmt.printfln("%d squared is %d", n, n * n)
}
}

Starter Template​

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