Talx gives you three distinct scripting layers: IptScrae - the original 1995 Forth-like stack language, Lua (WebAssembly in the browser, or gopher-lua on the server), and Python via Pyodide. Each has a distinct performance profile and sweet spot. Use what fits.
All three client languages run in your browser - no server involvement. The server-side Lua plugin layer is separate and lives inside the Go binary. Here's the full picture.
| Language | Runtime | Startup Cost | Handler Speed | Timeout | Numbers | Async? | Best For |
|---|---|---|---|---|---|---|---|
| IptScrae | JavaScript VM (in-browser) | Instant | Fastest | 500 ms | 32-bit int only | No | Classic room/hotspot scripts, legacy compatibility |
| Lua (client) | Wasmoon WASM (in-browser) | ~80 ms first load, cached | Fast | 1500 ms | Lua 5.4 floats | No (coroutines ok) | Modern room scripts, cyborg logic, complex hotspot flows |
| Python (client) | Pyodide WASM (in-browser) | ~4–8 s first load, cached | Medium | 2000 ms | Full Python numerics | Yes (asyncio) | Complex data processing, numpy/pandas, rapid prototyping |
| Lua (server plugin) | gopher-lua sandboxed VM | Per-plugin, <5 ms | Fast | 5000 ms (configurable) | Lua 5.1 doubles | Timers, HTTP fetch | Server-wide automation, bots, chat moderation, HTTP integrations |
IptScrae executes in the same JS thread that renders the Palace canvas. It was designed for
constrained 1990s hardware - opcode dispatch is essentially a switch statement.
If you need a script that fires on every keystroke or every frame, IptScrae is the right choice.
No WASM startup, no JIT warm-up, no GC pauses.
Both Lua (Wasmoon) and Python (Pyodide) run inside WebAssembly sandboxes. The first load in a fresh browser session pays a compilation cost - Pyodide is much heavier (~6 MB WASM + Python stdlib). After the first load, both are cached by the browser's service worker and subsequent loads are near-instant. If your users often clear cache or visit only occasionally, IptScrae's zero-download profile wins.
A classic IptScrae gotcha: all numbers are 32-bit signed integers. There is no floating point. Division truncates. Colors, room coordinates, and timer values all work fine, but if you need sub-pixel math or trig, switch to Lua (native floats) or Python (full IEEE 754 + numpy). This limitation doesn't affect 99% of room scripts.
IptScrae and client Lua are synchronous. Handlers must complete before the next
event fires. Server-side Lua plugins have access to palace.timer.after and
palace.timer.every, plus palace.http.fetch for external API calls.
Python (Pyodide) supports asyncio for deferred work - the most powerful option
for client-side coroutines and delayed effects.
Server Lua plugins support live hot-reload without restarting the server. The admin panel
lets you disable, enable, and reload individual plugin bundles. The VM is discarded and
re-created cleanly - all phantom bots owned by the plugin are automatically destroyed and
the plugin's main.lua runs fresh. No reconnect required for connected users.
IptScrae is 100% backward-compatible with scripts written for the original Palace
(1996–2005). If you have an old .ipt file, paste it in and it works. Lua
client scripts can call all the same host effects via the t.* API, but there
is no mechanical translator - new scripts only. Server Lua maps 1:1 to the classic
s-pluginapi.h C ABI.
IptScrae (pig Latin for "script") is a Forth-like stack-based language built into every Palace client since 1996. It's deterministic, fast, and proven - over 297 opcodes, all tested, all supported.
IptScrae uses Reverse Polish Notation (RPN): operands come before operators. Values are pushed onto a stack; operators pop and push results.
Classic IptScrae matches the 1996–2005 Palace spec. Extended IptScrae (enabled in
Preferences → "Enable extended Iptscrae") adds 256-color palettes,
GETROOMOPTIONS, and modern Palace additions.
The complete searchable command reference (all 297+ opcodes, classic and extended) lives in the client help system and at:
Client Lua runs Lua 5.4 via Wasmoon WebAssembly - the same VM as the desktop
engine, fully sandboxed in your browser tab. It mirrors every IptScrae host effect through a
modern t.* API, plus adds closures, proper error messages, coroutines, and
floating-point math.
| Event | Triggered When |
|---|---|
| ENTER | You enter a room |
| LEAVE | You leave a room |
| OUTCHAT | You send a chat message (mutable) |
| INCHAT | Someone else sends chat |
| HOTSPOT_CLICK | A hotspot is clicked |
| ALARM | A timer fires |
| PROPCHANGE | Avatar/prop changes |
| USERMOVE | A user moves in the room |
Write Palace scripts in pure Python - running entirely in your browser via
Pyodide WebAssembly, zero server involvement. The full CPython 3.12 stdlib
is available. Import random, json, math, even
numpy or pandas (loaded on demand).
json, re, datetime, mathasyncio for timed / multi-step sequencesThe first time a user visits a room with a Python script, Pyodide downloads ~6 MB of WebAssembly and Python stdlib. On a fast connection this takes 2–4 seconds; on slow mobile it can take 10+ seconds. Subsequent visits use the browser cache - effectively free. If instant load is required, use IptScrae or client Lua instead.
A cyborg script is personal - it runs on your client, intercepting your outgoing chat before it reaches the server. Use it to create chat macros, auto-formatters, personal bots, and shortcut commands. Your cyborg script can be written in IptScrae, Lua, or Python.
The original Palace called personal automation scripts "cyborgs" - a human and a bot at the same time. Your cyborg script sees every message you type before it leaves your machine. It can block the message, rewrite it, or fire side effects (change your avatar face, navigate to a room, etc.). Other users see the result, not the raw input.
The Cyborgs Marketplace is a community repository of shareable cyborg scripts. Find scripts for prop trading, auto-greeting, moderation helpers, games, and more.
Server plugins run inside the Go binary in a sandboxed
gopher-lua VM - one per plugin, hot-reloadable without a server restart. They
have full access to the server event stream, all connected users and rooms, the chat pipeline,
and an outbound HTTP allowlist.
Client scripts run in your browser and affect only you. Server plugins run on the server and affect every connected user. A server plugin can listen to every chat message from every user, send god-messages, move users between rooms, create NPC phantom bots, and call external HTTP APIs. With great power comes the usual caveats - only server admins can install plugins.
A plugin is a zip file (or directory) containing a plugin.json manifest
and a main.lua entry point:
The server plugin API maps 1:1 to the classic Palace 4.5 s-pluginapi.h C ABI.
If you wrote a C plugin in the 2000s, all the same capabilities are here.
| Namespace | Key Functions |
|---|---|
| palace.user.* | get, list, rank, moveToRoom, gag, logoff |
| palace.room.* | get, list, users, message |
| palace.chat.* | god, user, room, global, whisper, talk |
| palace.command.* | register, unregister |
| palace.timer.* | after, every, cancel |
| palace.store.* | global.get/set/del, user.*, room.* |
| palace.http.* | fetch (allowlist), route (serve endpoints) |
| palace.user (bots) | createPhantom, walkTo, destroyPhantom |
| palace.loose.* | add, move, delete loose props |
| palace.spot.* | setState, move, lockDoor |
| palace.util.* | sha256, jsonEncode, randomPoint, throttle |
Phantom users are server-created bot accounts with no underlying client connection. They appear in rooms, can walk, talk, change avatars, and react to events - entirely server-driven. A wandering NPC, a shopkeeper, an auto-moderator - all implemented as phantom users.
palace.user.walkTo
You have an existing .ipt script from an old Palace world, you need the
absolute lowest latency (fires on every frame), you want zero-download compatibility
for users on slow connections, or you're doing simple room greetings and navigation.
The 297-opcode spec is stable and won't change.
You want to write new scripts with a modern language. Closures, pattern matching,
t.wrapSpot, proper error messages, 1500 ms timeout, and real floating-point
numbers. About 80 ms first-load overhead, then cached. Ideal for most new room and
cyborg scripts.
You're more comfortable with Python than Lua, you need asyncio for timed
sequences, or you need the full stdlib or third-party packages. The 4–8 second
first-load cost is fine for rooms where users will stay a while.
You need something that affects all users - moderation, server-wide announcements, NPC bots, external webhook integrations, or command extensions. Requires server admin access. Hot-reloadable, zero user impact on reload.
There's no restriction on using multiple languages in the same world. A room might have an IptScrae hotspot script for instant legacy feel, a Lua cyborg for personal chat macros, and a server plugin running an NPC bot. Each layer is fully independent - they don't interfere with each other.