@delwing/mudix
v0.1.1
Published
Web-based MUD client with Mudlet-compatible Lua scripting — usable standalone or as a library for branded builds
Maintainers
Readme
🗺️ mudix
A modern, web-based MUD client with Mudlet-compatible Lua scripting — running entirely in your browser.
No installs. No native dependencies. Open a tab and play.
What is mudix?
mudix is a MUD (Multi-User Dungeon) client that lives in the browser. It speaks the full telnet protocol stack that modern MUDs use, renders rich ANSI output, and runs real Lua scripting compiled to WebAssembly — with an API designed for drop-in compatibility with Mudlet packages, maps, and profiles.
If you have Mudlet scripts, triggers, aliases, or maps, mudix aims to run them unchanged — anywhere you have a web browser.
✨ Features
🔌 Connectivity
- Direct WebSocket connections to MUDs that expose a
ws(s)://endpoint. - Telnet via proxy — connect to any classic
host:portMUD through the bundled telnet→WebSocket proxy. - Full protocol support: GMCP, MSDP, MSSP, MCCP (compression), MSP (sound), telnet CHARSET, TTYPE/MTTS, and GA/EOR prompt detection.
📜 Mudlet-compatible Lua scripting
- A complete Lua 5.1 runtime (WASM) with Mudlet-native globals —
send,echo/cecho/decho/hecho,tempTimer,tempTrigger,tempAlias, and hundreds more. - Triggers, aliases, timers, keybindings, and buttons organized in Mudlet-style folder trees.
- PCRE regex powered by
pcre2-wasm— the same engine flavor as Mudlet. - Bundled Mudlet standard library: Geyser GUI toolkit, the generic mapper, string/table utilities, and the
db:*database API. - A built-in CodeMirror script editor with autocompletion for the entire API surface.
🗺️ Maps & GUI
- Renders Mudlet binary maps with a built-in viewer and editor.
- Geyser mini-consoles, labels, gauges, and command lines as pixel-positioned overlays.
- A custom dock/float window system — split, tab, dock to any edge, or tear off into free-floating windows.
💾 Storage & persistence
- A per-profile virtual filesystem (ZenFS) backed by IndexedDB — or linked to a real folder on your disk via the File System Access API, with two-way sync.
- An in-browser SQLite database for the Mudlet
db:*API. - Session logging to IndexedDB, browsable and exportable.
- Install Mudlet packages and modules (
.mpackage/.zip/ XML) — including browsing the public Mudlet package repository.
🎨 Polish
- Multiple themes (dark, light, amber, sky), custom fonts, configurable colors and backgrounds.
- Text-to-speech, sound, and video playback wired to Mudlet's media APIs.
🚀 Getting Started
Prerequisites
Run the client
git clone <repo-url> mudix
cd mudix
yarn
yarn devOpen the printed local URL (usually http://localhost:5173) in your browser, then create a connection on the start screen.
Run the telnet proxy (for host:port MUDs)
The browser cannot open raw TCP sockets, so connecting to a classic telnet MUD needs the small bundled proxy:
cd proxy
yarn
yarn start # listens on ws://localhost:3001 by defaultThen choose MUD (host:port) mode when creating a connection. Servers that expose a native WebSocket endpoint don't need the proxy — use WebSocket mode and point it straight at the ws(s):// URL.
🛠️ Development
yarn dev # Start the Vite dev server
yarn build # Type-check + production build
yarn preview # Preview the production build
yarn typecheck # Type-check only (src + tests)
yarn test # Run the Vitest suite
yarn test:watch # Vitest in watch modeProduction builds deploy to GitHub Pages automatically on push to master.
🧱 Architecture at a glance
MudClient (WebSocket — direct or via telnet proxy)
→ telnet/protocol parsing (GMCP · MSDP · MSSP · MCCP · MSP · CHARSET · TTYPE)
→ MudSession.events (a typed EventBus — the spine of the app)
├─ ScriptingEngine → LuaRuntime (WASM) + alias/trigger/timer/key engines
├─ React UI (output area, dock/float panels, toolbar)
└─ SessionLogger (persists output to IndexedDB)| Layer | Lives in | Notes |
|---|---|---|
| Connection | src/mud/connection, src/mud/protocol | Telnet over binary WebSocket frames |
| Scripting | src/scripting | wasmoon Lua 5.1, Mudlet-native API, bundled Mudlet Lua |
| UI / Windows | src/ui | Custom dock/float layout, CodeMirror editor, overlays |
| Maps | src/map, MapPanel | Mudlet binary map reader/renderer/editor |
| Storage | src/storage, src/scripting/vfs, src/db | Zustand + per-profile VFS + SQLite + IndexedDB logs |
| Import | src/import | Mudlet XML & package install/export |
A deeper tour of the internals lives in CLAUDE.md; the full Mudlet API implementation status is tracked in MUDLET_API.md.
🏷️ Branded builds
mudix is also published as an npm library (@delwing/mudix) for shipping a white-label client for one specific MUD — your own name, login screen, theme, toolbar, and bundled Lua packages, without forking the repo. See docs/BRANDED_BUILDS.md for the setup guide, or embervale-web for a live example.
⚠️ Limitations & known constraints
mudix runs inside a browser sandbox, which trades some of Mudlet's native reach for zero-install portability. Worth knowing before you switch:
- Telnet MUDs need the proxy. Browsers can't open raw TCP sockets, so classic
host:portMUDs are reachable only through the bundled telnet→WebSocket proxy. Servers with a nativews(s)://endpoint connect directly. - One profile per tab. There's no Mudlet-style multi-profile tabbing within a tab — you open one connection per browser tab (
loadProfile()and in-tab profile switching are no-op stubs). You can still run different profiles in separate tabs: each profile is locked to a single tab (opening the same one elsewhere shows a "waiting" screen until the first tab releases it), every profile's data is isolated in its own filesystem, and profiles can signal each other across tabs via Mudlet'sraiseGlobalEvent. - Storage is browser-scoped. Profiles, scripts, maps, and logs live in the browser's IndexedDB/localStorage for the app's origin. Clearing site data wipes them — unless you link a real disk folder for that profile (see below). Different browsers/machines don't share state automatically.
- Disk-folder linking is Chromium-only. The "link a folder on disk" feature uses the File System Access API, which Firefox and Safari don't implement. Those browsers fall back to IndexedDB-only storage.
- Secure context required. The VFS service worker (which serves profile images/fonts/CSS) needs HTTPS or
localhost. - Clipboard access is best-effort. The browser's OS clipboard is asynchronous and gated on a user gesture (and a secure context), whereas Mudlet's
getClipboardText/setClipboardTextare synchronous. mudix keeps a session-local text-clipboard mirror as the authoritative value and syncs it to the real OS clipboard opportunistically — sosetClipboardTextmay not reach the system clipboard without a user gesture, andgetClipboardTextreturns the last value mudix knows about (an external copy made elsewhere shows up on the next call, once the async read completes). This mirror is separate from the rich-text buffer used bycopy()/paste(). - Some Mudlet APIs are stubbed or partial. Anything fundamentally native is bound as a warning-emitting no-op so imported packages still load, but does nothing: Discord Rich Presence, IRC client, OS
spawn/subprocess, and the system dictionary. A few synchronous Mudlet calls (invokeFileDialog,getImageSize) don't map cleanly onto the browser's async pickers/loaders and are still in progress. SeeMUDLET_API.mdfor the per-function status (✅ / ⚠️ / 🚧 / ❌). - The main window is the viewport. Calls like
setMainWindowSizeare no-ops — the browser window is the main window. - The Lua↔JS boundary has a per-call cost. Each crossing between the Lua VM and JS is cheap individually but adds up — a script that makes thousands of tiny boundary calls in a tight loop (e.g. iterating every room in a large area one
getRoom*call at a time) will feel noticeably slower than in native Mudlet. The hot paths you actually hit constantly (line/trigger processing, GMCP) are batched and stay fast; prefer bulk/batched APIs over per-item calls when you can.
🤝 Contributing
When adding a Mudlet API function, implement the JS-side method in ScriptingAPI, bind the Lua global in LuaRuntime, and add the autocomplete entry in luaCompletions.ts. Run yarn typecheck and yarn test before opening a PR.
License
mudix is licensed under the GNU General Public License, version 2 or (at your option) any later version — see LICENSE. This matches Mudlet's license (GPL-2.0-or-later): mudix bundles Mudlet's Lua runtime files (src/scripting/lua/mudlet-lua/ — LuaGlobal, Geyser, the generic mapper, DB utilities, and more), copyright the Mudlet contributors, so every mudix distribution is a combined work with that GPL code.
Lua scripts and packages that mudix merely runs (your profile scripts, installed .mpackages, branded-build packages) are separate works and are not required to be GPL — the same way the Mudlet ecosystem treats its packages.
mudix also bundles Bitstream Vera Sans Mono (src/assets/fonts/bitstream-vera-sans-mono/) as the default console output and command-line font, matching Mudlet's own default console typeface. It's under the separate, permissive Bitstream Vera license (free redistribution and embedding; just don't sell the font standalone or rename a modified copy while keeping "Bitstream"/"Vera" in the name).
