Skip to content

What is Munos?

Munos is the language you write MultiNostalgia mods in. A mod is a small program that rides along with a retro game — it can read the game's memory, draw on top of the screen, and talk to other players over the network, all while the original game keeps running underneath.

If you've ever wanted to add multiplayer ghosts to a single-player NES game, build a pause menu the cartridge never had, or paint a live map over the action, that's what Munos is for.

The mental model

Two sentences capture the whole language:

Munos looks like JavaScript and behaves like BASIC.

The surface is familiar: curly braces, if/while/for, // comments, and TypeScript-style type annotations (x: i32). If you've written a little JavaScript or TypeScript, you can already read it.

The semantics are deliberately tiny. There are no closures, no classes, no this, no first-class functions, no dynamic objects. A Munos program is variables, conditionals, loops, and function calls — and that's genuinely all. The language is built to be learnable in an afternoon and to compile straight to fast native code.

munos
var score = 0

function add_points(n: i32) {
    score = score + n
}

add_points(100)
print("score: ", score)   // score: 100

What a script can do

A Munos mod has exactly four powers:

  1. Read and write game memory. The console's RAM is an address space you reach through builtins like read_u8(0x0086). This is how you find out where the player is, how many lives are left, which level is loaded.
  2. Draw overlays. You can paint indexed-color images onto the framebuffer on top of whatever the game is rendering.
  3. Send and receive messages. send() and receive are built into the language. Your mod talks to its server with no socket code.
  4. React to events. The runtime calls your code when a frame is drawn, a message arrives, or a button is pressed.

Everything else the language omits on purpose — there's no file system, no arbitrary networking, no way to escape the sandbox. A mod is powerful inside the game and harmless outside it.

One file, two roles

This is the part that surprises people: a single .munos file contains both the client and the server. The same source is compiled and run in two places — in each player's browser, and on a shared server that relays messages between them.

You separate the two with on client and on server blocks. Anything outside those blocks is shared by both.

munos
// Shared by both sides — a memory address constant.
const PLAYER_X: u32 = 0x0086

on client {
    event frame(frame_num: i32) {
        var x = read_u8(PLAYER_X)   // read where the player is
        // ...draw, send updates...
    }
}

on server {
    event tick(tick_num: i32) {
        // ...relay player positions to everyone...
    }
}

Because both halves live in one file, the wire format between them is just shared knowledge — you decide how to pack a message on the client and unpack it on the server, and the two can never drift apart.

We'll build up to a real client/server mod over the next few chapters. For now, the takeaway is: client code and server code are two sections of the same program.

Why the language is so small

A few of Munos's limits are worth understanding up front, because they shape how you write everything else:

  • No floating-point numbers. Munos has only integers (i8/i16/i32 and their unsigned u* counterparts). Retro games are integer machines; dropping floats keeps every client computing bit-identical results, which matters when players must agree on what happened.
  • No first-class functions. A function name isn't a value you can pass around. This keeps the compiler simple and your programs flat and readable.
  • Static types, checked ahead of time. Every variable has a type the compiler knows. There are no surprise type coercions at runtime.

None of this makes mods harder to write — it makes them easier to reason about. You'll rarely miss what isn't there.

How a script runs

You don't compile Munos by hand. When the MultiNostalgia player loads your mod, it:

  1. compiles your .munos source to WebAssembly, in the browser, and
  2. runs it as native code alongside the emulator.

That compile happens once at load time, so your per-frame code runs at full speed. The same compiler runs on the server for the on server half.

Next

You've got the shape of the language. Time to run something.

Your first script → — draw a block on the screen and see it appear over a real game.

Part of the MultiNostalgia project.