The iptscrae vm supports classic set commands from the original Palace as well as an extended set first used by Pa//n for Palacechat.
Enabling the extended set — extended opcodes only resolve when Preferences → Room Experience → "Enable extended iptscrae (by pawn)" is checked. With it off, a script that calls an extended-only word (e.g. SETPICOPACITY) treats it as an undefined symbol, exactly like classic iptscrae did — no crash, just silently not a function. This keeps old rooms authored against strict classic iptscrae behaving identically by default, while giving room authors who want the bigger PalaceChat vocabulary an opt-in switch. The toggle is on by default for new installs; flip it off in Preferences to pin a room to classic-only behavior.
Data types & reading the syntax
iptscrae is postfix (Reverse Polish): operands first, operator last — 2 3 +, not 2 + 3. Every command pops what it needs off a shared stack and pushes its result back on, so 2 3 + 4 * reads as "push 2, push 3, add (pops both, pushes 5), push 4, multiply (pops both, pushes 20)". A script body is just a flat run of these postfix commands, grouped into { … } blocks where a command needs a chunk of code as data (a condition body, a loop body, a callback) rather than a value.
Type
Written as
Notes
Number
42 -7
Signed 32-bit integer only — no floats. A bare - right before a digit is a negative literal; anywhere else it's the subtraction operator, so write 3 -2 + (not 3 - 2 +) if you mean "add negative two".
String
"hello" "she said \"hi\""
Double-quoted. \" and \\ escape themselves; \xHH inserts one byte from two hex digits (e.g. \x3b → ;) — handy for embedding a literal {/}/; in text without confusing the tokenizer. & concatenates two strings.
Symbol
myVar tempCount
A variable name — any run of characters that isn't whitespace, an operator, or a bracket/quote. Unlike the original 1990s client, this implementation doesn't require starting with a letter or cap the length at 31 characters. Set with value sym = or value sym DEF; promote to cross-handler scope with sym GLOBAL.
Array
[ 1 2 3 ] [ "a" "b" ]
Ordered, mixed-type, 0-indexed. Also buildable at runtime with n ARRAY (zero-filled). Capped at 256 elements — see Limits. Read/write with arr idx GET / value arr idx PUT; iterate with { block } arr FOREACH.
Atomlist
{ "hi" SAY }
A block of code as data — a "subroutine". Doesn't run where it's written; a command that expects one (IF, IFELSE, WHILE, FOREACH, EXEC, ALARMEXEC, DEF) runs it later, on its own terms. Can nest.
; lexical notes
# starts a line comment (to end of line) in this implementation. The original client used ; for that; here ; is just an inert statement separator (same as whitespace), kept only so old scripts that sprinkled semicolons between statements still parse.
= assigns, == compares — see fidelity notes for this and other easy-to-misread operators.
CHATSTR is the best-known special symbol: it's pre-seeded by OUTCHAT/INCHAT rather than declared — see the event table below and the fidelity notes.
Events (ON <event> { … })
Handlers are matched by name at the top level of a hotspot's or the cyborg's script. Extended events fire unconditionally (they're new hook points, not opcodes) — a script with no handler for one simply doesn't react, on classic or extended mode alike.
SIGNON classic
Fires once, before the first ENTER, only on initial connect.
ENTER classic
Fires on every room load, cyborg first then each hotspot.
LEAVE classic
Fires on the room being left, before teardown.
SELECT classic
Fires on click, on the clicked hotspot only.
ALARM classic
Fires when a SETALARM timer for that spot elapses.
OUTCHAT / INCHAT classic
Fire on outgoing (typed) / incoming (incl. own echoed) chat. CHATSTR is pre-seeded and re-read after, so a handler can rewrite the text.
MACRO0–MACRO12 classic
Fire from F1–F12 or the n MACRO opcode.
ROOMLOAD extended
Fires once per room load, just before ENTER (PalaceChat PE_Startup).
SIGNOFF extended
Fires on the room being left when the local user disconnects.
PROPCHANGE extended
Fires whenever the local user's own worn-prop set changes (DONPROP/DOFFPROP/REMOVEPROP/CLEARPROPS/NAKED/SETPROPS/DROPPROP).
LOCK / UNLOCK extended
Fire on the hotspot whose lock state just changed, via the LOCK/UNLOCK opcodes or the default bolt-click handler.
USERENTER / USERLEAVE extended
Fire when another user (not you) enters/leaves the room -- live arrivals/departures only, not on reconnect scrollback catch-up. WHOENTER/WHOLEAVE hold that user's id.
USERMOVE extended
Fires when another user moves within the room. WHOMOVE holds that user's id -- read their new position with WHOMOVE WHOPOS.
; fidelity notes — things that read as "obvious" but aren't
Single = is assignment, double == is comparison — the reverse of a C/JS-influenced reading.
SUBSTR is a case-insensitive literal contains test, not a regex search — use GREPSTR for classic regex or REGEXSTR for modern JS RegExp.
WHOPOS / MOUSEPOS push two separate stack values (x, then y) — not one point/array.
CHATSTR is pre-seeded before OUTCHAT/INCHAT fire and re-read after, so a handler can rewrite outgoing/incoming chat text before it's shown.
Binary operators pop v2 (top) first, then v1 — result is v1 OP v2, so 3 2 - pushes 1, not -1.
String comparisons (== != < > <= >=) are case-insensitive when both operands are strings.
Limits
The original client enforced hard, fairly tight caps (it ran in a few hundred KB of 1990s RAM). This browser VM removes most of them — bounded instead by ordinary browser memory and the JS call stack — but a couple of behaviors changed shape rather than just growing a number, which matters if you're porting an old script.
Limit
Original client
This implementation
Array size
256 elements
Still 256 (IPT_MAX_ARRAY) — n ARRAY with n out of range pushes 0 instead of allocating; a [ … ] literal over 256 items aborts the script.
Stack depth
256 items
Unbounded — limited only by available memory, not a fixed count.
Nested atomlists
64 deep
Unbounded — limited only by the JS call stack (in practice, thousands of frames).
Variables per handler
64
Unbounded.
Global variables
Unbounded (client storage)
Capped at 256 declared globals, each name ≤64 characters, each string value ≤4096 characters / array value ≤256 elements, ~64KB total across all of them (PalaceGlobalStore — guest: localStorage only; signed-in: synced to the account). Check NBRGLOBALS if a room's state is growing unbounded; sym CLEARGLOBAL un-declares one.
Queued alarms
64, queued
A spot has at most one pending SETALARM timer — re-arming a spot that already has one replaces it rather than queuing a second. ALARMEXEC timers aren't spot-keyed and aren't capped.
GREPSTR pattern / GREPSUB result length
1024 / 16384 bytes
Unbounded (ordinary JS strings).
Spot state range
-32768…32767
Unbounded — any integer SETSPOTSTATE accepts is stored as-is (whether a picture exists for it is a separate, authoring-time concern).
Symbol name
Letters/digits/underscore, must start with a letter, ≤31 chars
Any run of non-whitespace/non-operator characters, any length — see Data types.
Tutorial: an adding machine
A short worked example that ties several pieces together: pattern-matching chat with GREPSTR, pulling the captured groups back out with GREPSUB, converting string↔number with ATOI/ITOA, and rewriting CHATSTR so the reply replaces what was actually said. Drop this in an OUTCHAT handler (Cyborg.ipt or a room/spot script) and typing 673 plus 897 in chat says 673 plus 897 equals 1570.
ON OUTCHAT {
{
"$1" GREPSUB firstNumber =
"$2" GREPSUB secondNumber =
firstNumber ATOI firstNumber =
secondNumber ATOI secondNumber =
firstNumber secondNumber + total =
total ITOA total =
"$1 plus $2" GREPSUB " equals " & total & CHATSTR =
} CHATSTR "^(.*) plus (.*)$" GREPSTR IF
}
Reading it top to bottom:
CHATSTR "^(.*) plus (.*)$" GREPSTR IF — the condition runs last (postfix), but reads first: match the typed text against the pattern, and only run the block if it matched. The two (.*) groups are captured for GREPSUB to pull out afterward.
"$1" GREPSUB firstNumber = — GREPSUB substitutes captured groups into its template; a template of just "$1" yields group 1 as a string, which gets stored into firstNumber.
firstNumber ATOI firstNumber = — converts that string in place to a number (re-=-ing the same symbol is fine; iptscrae has no separate declaration step).
firstNumber secondNumber + total = — ordinary postfix addition, stored into a new symbol.
total ITOA total = — back to a string, since CHATSTR and & need strings, not numbers.
"$1 plus $2" GREPSUB " equals " & total & CHATSTR = — rebuilds "673 plus 897", concatenates " equals 1570" onto it, and assigns the result back to CHATSTR — which is what actually gets sent as the chat text (see the CHATSTR fidelity note above).
The same shape — GREPSTR to test-and-capture, GREPSUB to extract or rebuild — is the standard way to write any chat-triggered command in iptscrae.
classic Original iptscrae
The command set understood by the original 1990s Palace client, always available regardless of the extended-opcode preference.
Control flow
Opcode
Stack
Description
IF
block cond →
Runs block if cond is truthy.
IFELSE
trueBlock falseBlock cond →
Runs one block or the other.
WHILE
bodyBlock condBlock →
Runs condBlock, pops its result; loops while truthy, running bodyBlock each pass.
EXEC
block →
Runs a block (or atom-list) as a subroutine. A literal 0 is treated as an explicit no-op.
FOREACH
block arr →
Runs block once per array element, with the element pushed before each run.
EXIT
→
Aborts the whole running script (all enclosing blocks).
RETURN
→
Returns from the current subroutine (block called via EXEC); does not abort the outer script.
BREAK
→
Breaks out of the innermost WHILE/FOREACH loop.
DELAY
ticks →
Classic client busy-waits; here the operand is consumed and execution continues immediately (browser-safe).
Variables & symbols
Opcode
Stack
Description
DEF
value sym →
Defines/sets a local variable to value.
GLOBAL
sym →
Promotes a variable to GLOBAL: flat client-side state, persisted to localStorage, surviving room changes and server transfers.
SGLOBAL (alias of GLOBAL)
sym →
Classic alias.
CLEARGLOBAL
sym →
PalaceSpace extension: un-declares one global (back to an ordinary local). Doesn't touch any other global — there's no "clear everything" opcode.
NBRGLOBALS
→ n
PalaceSpace extension: count of currently-declared globals. See Limits for the 256-global cap.
Identity & spot state
Opcode
Stack
Description
ME
→ spotId
Pushes the id of the hotspot the running script belongs to.
ID (alias of ME)
→ spotId
Classic alias.
DEST
→ destRoom
Pushes this hotspot's authored destination (door target).
GETSPOTSTATE
spotId → n
Reads a spot's current picture-state index.
SETSPOTSTATE
n spotId →
Sets a spot's picture-state index (switches its displayed picture).
SETSPOTSTATELOCAL
n spotId →
Local-only variant — no distinct behavior in this single-user client.
LOCK
spotId →
Marks a spot locked, plays the door-lock sound, fires ON LOCK.
UNLOCK
spotId →
Marks a spot unlocked, plays the door-unlock sound, fires ON UNLOCK.
ISLOCKED
spotId → 0|1
Reads a spot's lock state.
Alarms & timing
Opcode
Stack
Description
SETALARM
ticks spotId →
Schedules ON ALARM to fire on spotId after ticks/60 seconds. Re-arming the same spot replaces its pending timer.
ALARMEXEC
block ticks →
Runs block after a delay, independent of any spot's alarm slot.
STOPALARM
spotId →
Cancels a pending SETALARM for one spot.
STOPALARMS
→
Cancels every pending alarm/timer.
TICKS
→ n
~1/60s ticks since page load (classic Mac TickCount analogue).
DATETIME
→ n
Current Unix time as a signed 32-bit long. The Debug "Mac epoch" toggle emulates the classic Mac client's 1904-epoch overflow behavior.
Navigation & messaging
Opcode
Stack
Description
GOTOROOM
roomId →
Navigates to a room by id and aborts the rest of the script.
NETGOTO
url →
Opens a URL, or joins another palace for a palace:// link.
GOTOURL (alias of NETGOTO)
url →
Classic alias.
SOUND
name →
Plays a room sound once, fire-and-forget.
MIDIPLAY
name →
Plays a MIDI tune once.
MIDILOOP
loopCount name →
Plays a MIDI tune looped.
MIDISTOP
→
Stops the currently playing MIDI tune.
LOCALMSG
text →
Shows a chat balloon locally only (never sent over the wire).
ROOMMSG
text →
Broadcasts a balloon message to the whole room.
SAY
text →
Sends text as the user's own chat, exactly as if typed (incl. backtick-command handling).
CHAT (alias of SAY)
text →
Classic alias.
GLOBALMSG
text →
Server-wide announcement balloon (wizard rank only when live).
SUSRMSG
text →
Shows a balloon message (no distinct target-user argument).
PRIVATEMSG
text target →
Sends a private whisper to one user.
LOGMSG
text →
Writes a line to the Log tab only (no balloon).
STATUSMSG
text →
Flashes the status bar (and records to the Log).
Boolean & operators
Opcode
Stack
Description
AND
a b → 0|1
Logical AND.
OR
a b → 0|1
Logical OR.
NOT
a → 0|1
Logical NOT.
+ - * / %
v1 v2 → n
Arithmetic (signed 32-bit); + concatenates when either side is a string.
== != <>
v1 v2 → 0|1
Equality / inequality, case-insensitive for strings. Note: single = is assignment, not comparison.
< > <= >=
v1 v2 → 0|1
Ordering, lexicographic (case-insensitive) for strings.
&
v1 v2 → str
String concatenation.
!
v → 0|1
Unary logical NOT (operator form).
++ --
sym →
Increment/decrement a variable in place.
+= -= *= /= %= &=
value sym →
Compound assignment.
=
value sym →
Assignment (not comparison — that's ==).
Strings & patterns
Opcode
Stack
Description
GREPSTR
subject pattern → 0|1
Classic-dialect regex match (case-sensitive; ? and | are literal). Sets the match state GREPSUB reads.
GREPSUB
template → str
Substitutes $0–$9 / & from the last GREPSTR match into template.
SUBSTR
whole frag → 0|1
Case-insensitive literal "contains" test — not a regex, despite the name.
REGEXSTR
subject pattern → 0|1
Modern JS RegExp boolean match. Keeps its own match state, separate from GREPSTR.
STRTOATOM
str → block
Tokenizes a string into an executable block for EXEC.
Extracts a substring; negative length means "rest of string".
REPLACE
subject pattern replacement → str
Replaces the first literal match.
REPLACEALL
subject pattern replacement → str
Replaces every literal match.
ENCODEURL / DECODEURL
str → str
URI component encode/decode.
CHARTONUM
str → n
First character's code point (0–255).
NUMTOCHAR
n → str
Code point (0–255) to single-character string.
RANDOM
n → r
Random integer in [0, n).
SPLIT
str delimiter → arr
PalaceSpace extension: literal (non-regex) split. An empty delimiter splits into individual characters. Capped at 256 pieces like every array (see Limits) — extra pieces are silently dropped, not aborted.
Math & bitwise
Opcode
Stack
Description
ABSVALUE
n → n
Absolute value.
ROUNDNUM
n → n
Rounds to nearest integer.
SINE / COSINE / TANGENT
degrees → n
Trig functions; degrees in, result ×1000 fixed-point out.
SQUAREROOT
n → n
Integer square root; aborts the script if n is negative.
SQRT (alias of SQUAREROOT)
n → n
Talx alias.
BITAND / BITOR / BITXOR
a b → n
Bitwise AND / OR / XOR.
BITNOT
n → n
Bitwise complement.
Array & stack
Opcode
Stack
Description
ARRAY
n → arr
Allocates a zero-filled array of length n (max 256). Arrays can also be written as literals: [ a b c ].
GET
arr idx → value
Reads an array element; out-of-range aborts the script.
PUT
value arr idx →
Writes an array element in place; out-of-range aborts the script.
JOIN
arr separator → str
PalaceSpace extension: joins array elements (stringified) with separator. Same pop order as PUT.
SORT
arr → arr
PalaceSpace extension: pushes a new sorted array (doesn't mutate the input). Numeric ascending if every element is a number, otherwise case-insensitive lexicographic (same rule ==/</> use for strings).
POP
v →
Discards the top of the stack.
DUP
v → v v
Duplicates the top of the stack.
SWAP
a b → b a
Swaps the top two stack items.
OVER
a b → a b a
Duplicates the second-from-top item.
PICK
… n → … v
Copies the item n deep (0 = top).
STACKDEPTH
→ n
Pushes the current stack depth.
TOPTYPE
→ n
Type tag of the top stack value, without popping it.
VARTYPE
→ n
Type tag of the top stack value, resolving a symbol to its bound value first.
Room & user info
Opcode
Stack
Description
USERNAME
→ str
The local user's display name.
SERVERNAME
→ str
The currently connected server's name.
ICAMEFROM
→ str
Talx extension: the previous palace's name after a server hop, else "null".
ROOMNAME
→ str
Current room's name.
ROOMID
→ n
Current room's id.
ROOMWIDTH / ROOMHEIGHT
→ n
Current room's real pixel dimensions (512×384 unless the server defines a larger background).
NBRROOMUSERS
→ n
Count of users in the room (live roster + self, or 1 offline).
ROOMUSER
idx → userId
User id at roster index idx (self at 0).
WHOCHAT
→ userId
User id that generated the current/last INCHAT event.
WHOENTER / WHOLEAVE / WHOMOVE
→ userId
User id from the current/last USERENTER/USERLEAVE/USERMOVE event — see Events.
WHONAME
userId → str
Display name for a user id.
WHOME
→ userId
The local user's own id.
WHOTARGET
→ userId
Currently selected whisper target's id, or 0.
WHOPOS
who → x y
A user's room position. Pushes two values (x, then y) — not a point array.
MOUSEPOS
→ x y
Current mouse position over the room. Also two separate values.
SAYAT
text x y →
Says text as the user's own chat, positioned as @x,y text.
POSX / POSY
→ n
Local avatar's current room position.
SETPOS
x y →
Moves the avatar to an absolute position (clamped by nav-area / forbidden-spot rules, same as a click).
MOVE
dx dy →
Moves the avatar by a relative offset.
INSPOT
spotId → 0|1
Whether the avatar's current position is inside a spot's outline.
Avatar appearance
Opcode
Stack
Description
SETCOLOR
n →
Sets the avatar body color by classic palette index (0–15); out-of-range resets to 0.
SETCOLOUR
n →
Talx extension: sets the avatar's exact tint from the 256-entry custom color picker palette (0–255) instead of the classic 16-color set. Out-of-range or non-integer n is a no-op — unlike SETCOLOR, there's no sane "nearest" fallback for a raw palette index.
SETFACE
n →
Sets the avatar's face expression by classic face number.
SETFACESET
n|str →
Talx extension: selects the smiley art pack.
SETSMILEYSET (alias of SETFACESET)
n|str →
Same, alternate name.
USERPROP
n → propId
Prop id worn at don-index n.
NBRUSERPROPS
→ n
Count of props currently worn.
Access level
Rank on the ladder Guest < Member ≤ Wizard ≤ God ≤ Architect ≤ Owner ≤ Host. While connected, this is your real server-confirmed rank (same signal the Moderator button / ~susr/~op elevation and Hidden-rooms visibility already use) — not the offline Debug-tab rank simulator, which only applies when you aren't connected. Elevation is still required first (~susr/~op, or the Moderator button): a real Wizard/God/etc. who hasn't elevated this session reads as Member here, same as everywhere else operator status is checked. One live gap: the server can't always tell a plain Wizard from a God apart without an extra round trip, so immediately after connecting ISGOD may briefly read the same as ISWIZARD until that resolves.
True at or above that rank -- e.g. an Owner also reads ISWIZARD/ISGOD/ISARCHITECT as true.
ISHOST
→ 0|1
Current rank is exactly Host (the top of the ladder).
Worn props
Opcode
Stack
Description
DONPROP
propIdOrName →
Wears a prop by legacy id, bag name, or Modern UUID.
DOFFPROP
→
Removes the most recently worn prop.
REMOVEPROP
propIdOrName →
Removes a specific worn prop.
CLEARPROPS
→
Removes every worn prop.
NAKED (alias of CLEARPROPS)
→
Same effect, classic name.
SETPROPS
idArray →
Replaces the whole worn set at once.
TOPPROP
→ propId
Id of the topmost worn prop, or 0 when naked.
DROPPROP
x y →
Takes the top worn prop off and drops it in the room as a loose prop at (x,y).
Loose props
Opcode
Stack
Description
ADDLOOSEPROP
propIdOrName x y →
Drops a prop into the room at (x,y).
CLEARLOOSEPROPS
→
Removes every loose prop in the room.
SHOWLOOSEPROPS
→
Forces a redraw of the loose-prop layer.
Painting & pen
The scriptable pen is separate state from the palette pen (position/color/size/layer aren't shared) but draws into the same room paint layer, so PAINTUNDO/PAINTCLEAR affect both. Every opcode here is gated exactly like the server's AddDrawCommand and silently no-ops for a guest, a member in a NOPAINTING room, or a parentally-blocked session — no error, no stack effect beyond popping its own args. See also the drawing extensions (fill/opacity/text/shapes) for the same pen under the extended opcode set.
Opcode
Stack
Description
PAINTUNDO
→
Removes the last drawn paint command in the room.
PAINTCLEAR
→
Clears every paint command in the room.
PENFRONT / PENBACK
→
Selects which layer the scriptable pen draws on.
PENCOLOR
r g b →
Sets the pen's stroke color (0–255 each channel).
PENSIZE
n →
Sets pen stroke width, clamped 1–8.
PENPOS
x y →
Moves the pen to an absolute position without drawing.
PENTO
dx dy →
Moves the pen by a relative offset without drawing.
LINETO
dx dy →
Draws a relative line from the pen's current position and advances it.
LINE
x1 y1 x2 y2 →
Draws an absolute line and moves the pen to the endpoint.
OS & misc
Opcode
Stack
Description
SHELLCMD / LAUNCHAPP / LAUNCHPPA / TALKPPA
arg →
No-op stubs — a browser cannot shell out to the OS. Arguments are consumed for stack fidelity.
MACRO
n →
Fires ON MACROn on the cyborg and room hotspots (n = 0–12).
DIMROOM
n →
Darkens the room background; 0 or ≥100 is full brightness.
BEEP
→
No-op (no system beep in a browser).
KILLUSER
targetUser →
Whispers a `kill command at the target; the server applies the actual kick.
IPTVERSION
→ n
Interpreter version number.
CLIENTTYPE
→ "WEB"
Identifies this client as a web client.
extended "pawn" reference set
Ported from PalaceChat, the most actively developed clone of the original engine. Only resolve when the extended-iptscrae preference is enabled — see the note above.
Spot introspection & rewiring
Inspect and rewire doors/spots at runtime instead of hardcoding ids.
Opcode
Stack
Description
SPOTIDX
idx → spotId
Spot id at room-authoring index idx.
SPOTNAME
spotId → str
A spot's authored name.
SPOTDEST
spotId → n
A spot's destination (door target).
GETSPOTLOC
spotId → x y
A spot's authored location.
SETSPOTLOC
x y spotId →
Moves a spot at runtime.
GETSPOTTYPE
spotId → n
Numeric sub-kind (door-like types are > 0).
SETSPOTDEST
dest spotId →
Rewires a door's destination at runtime.
DOORIDX
idx → spotId
Spot id of the idx-th door-like spot in the room.
NBRDOORS
→ n
Count of door-like spots in the room.
NBRSPOTS
→ n
Count of every spot in the room.
LOCINSPOT
x y spotId → 0|1
Exact polygon containment test against a spot's authored outline (distinct from INSPOT's avatar-proximity check).
GETSPOTPOINTS
spotId → flatArr
A spot's outline as a flat [x0,y0,x1,y1,…] array.
SETSPOTPOINTS
flatArr x y spotId →
Replaces a spot's outline and location at runtime.
SETSPOTOPTIONS
options layer type spotId →
Sets a spot's type override. layer/options are consumed but have no effect (no layer/bitflag system in this renderer).
GETSPOTOPTIONS
spotId → type layer options
Reads back the type override; layer/options always 0.
SETSPOTNAMELOCAL
name spotId →
Renames a spot at runtime, local-only.
GETSPOTTEXTSIZE
maxW maxH spotId → x y w h
Measures a spot's authored name text, clamped to maxW/maxH.
SETSPOTSCRIPT
block eventName spotId →
Overrides what ON eventName runs for one spot at runtime, without touching its other events.
CACHESCRIPT
block eventName →
Stashes a reusable script block under a name for later reuse.
Spot visual styling
Font, border, clip, curve, and gradient styling for a spot. Data is recorded for script fidelity but has no visible effect — this client's renderer has no per-spot border/fill/gradient compositing pipeline (spots are outline polygons plus per-state pictures only).
Opcode
Stack
Description
SETSPOTFONT
bold strikeout underline italic center size pad color name spotId →
Sets font styling for a spot's label.
SETSPOTSTYLE
bgColor borderColor borderSize spotId →
Sets fill/border styling.
SETSPOTCLIP
clipMode spotId →
Sets clip mode.
SETSPOTCURVE
tension spotId →
Sets corner curve tension.
SETSPOTGRADIENT
angle color2 color1 spotId →
Sets a linear gradient fill.
SETSPOTPATHGRADIENT
useSpotPoint centerColor surround spotId →
Sets a radial/path gradient fill.
SETSPOTPICMODE
picMode spotId →
Sets picture display mode.
Loose-prop query & removal
Opcode
Stack
Description
NBRLOOSEPROPS
→ n
Count of loose props in the room.
LOOSEPROP
idx → propId
Prop id of the loose prop at index idx.
LOOSEPROPIDX
propId → idx
Index of a loose prop by its prop id, or -1.
LOOSEPROPPOS
idx → x y
Room position of a loose prop by index.
REMOVELOOSEPROP
idx →
Removes one loose prop by index.
MOVELOOSEPROP
x y idx →
Repositions one loose prop by index.
WHEREPROP
→ x y
Only meaningful inside a loose-prop-added/moved/deleted event, which this client never dispatches — always pushes 0 0.
UI & dialogs
Opcode
Stack
Description
ALERTBOX
msg →
Native blocking alert dialog.
CONFIRMBOX
msg → 0|1
Native blocking OK/Cancel dialog.
PROMPT
msg defaultValue → str
Native blocking text-input dialog.
SETCURSOR
n →
Sets the room cursor to a classic cursor number, mapped to a CSS cursor.
SETCURSORPIC
fileName x y →
Sets a custom image cursor with a hotspot offset.
SETTOOLTIP
msg →
Sets a tooltip on the room canvas (native browser title tooltip).
SETHELPTAG (alias of SETTOOLTIP)
msg →
Same effect.
CLEARTOOLTIP / CLEARHELPTAG
→
Clears the room tooltip.
UPDATELATER
→
Increments a redraw-defer counter. This renderer redraws immediately on state changes regardless, so this doesn't suppress interim redraws.
UPDATENOW
→
Clears the defer counter and forces one redraw.
Extended sound control
A "sound handle" is a stack value returned by SOUNDOPEN, addressing one <audio> element — unlike the fire-and-forget classic SOUND opcode.
Opcode
Stack
Description
SOUNDOPEN
name → handle
Opens a sound file, returning a handle.
SOUNDPLAY
handle →
Plays from the current position.
SOUNDLOOP
handle →
Plays looped from the start.
SOUNDPAUSE
handle →
Pauses playback.
SOUNDSEEK
position handle →
Seeks to position ms without changing play state.
SOUNDPLAYFROM
position handle →
Seeks then plays.
SOUNDSTOP
→
Stops and rewinds every open sound handle.
SOUNDISPLAYING
handle → 0|1
Whether one handle is currently playing.
ISSOUNDPLAYING
→ 0|1
Whether anything is currently playing, across every open handle.
SOUNDGETPOSITION
handle → ms
Current playback position.
SOUNDLENGTH
handle → ms
Total duration.
Remaining user & avatar
Opcode
Stack
Description
HASPROP
propId → 0|1
Whether the local user is wearing a specific prop.
USERID
→ userId
The local user's own id (equivalent to WHOME).
SETUSERNAME
name →
Renames the local user (persists and pushes to the server nickname when connected).
SETLOC / SETLOCLOCAL
x y →
Absolute avatar move — same as SETPOS.
HIDEAVATARS / SHOWAVATARS
→
Hides/shows every avatar in the room.
AUTOUSERLAYER
on →
Records a flag; this renderer already draws avatars in deterministic order, so it has no further effect.
WHOCOLOR
userId → n
A user's avatar body color.
WHOFACE
userId → n
A user's current face-expression number.
PROPDIMENSIONS
propId → w h
A prop's pixel size, from the loaded manifest.
PROPOFFSETS
propId → x y
A prop's anchor offset, from the loaded manifest.
LOADPROPS
idArray →
No-op beyond popping — this client loads its whole prop bag up front, nothing to fetch on demand.
Misc / math / hash
Opcode
Stack
Description
POWER
base exp → n
Integer exponentiation.
BITSHIFTLEFT / BITSHIFTRIGHT
value amount → n
Bit shifts.
NEWHASH
→ hash
Creates an empty key/value hash handle.
HASHTOJSON
hash → str
Serializes a hash to a JSON string.
JSONTOHASH
str → hash
Parses JSON into a hash handle.
ISFUNCTION
name → 0|1
Whether name is a callable opcode right now (checks classic always, extended only if the preference is on).
GETTIMEZONE
→ str
The browser's IANA timezone name.
CLIENTID
→ userId
No separate client-vs-user id concept here — same as the local user id.
NBRSERVERUSERS
→ n
Total users connected to the server (not just this room).
GETROOMOPTIONS
→ n
Count of authored room flags.
ROOMPICNAME
→ str
The room background picture's file name.
MEDIAADDRESS
→ str
Base URL media is served from for the active server.
ISKEYDOWN
keyCode → 0|1
Live physical-key state.
ISRIGHTCLICK
→ 0
Always false — right-click context isn't threaded through this client's click dispatch.
TEXTSPEECH
text →
Speaks text aloud via the browser's speech synthesis, when available.
REGEXP (alias of REGEXSTR)
subject pattern → 0|1
Same as REGEXSTR.
REGEXPREPLACE
template → str
Substitutes $1–$9 from the last REGEXSTR/REGEXP match.
OPENPALACE
→ 0
Legacy multi-window opcode — no such window model in a browser client.
PALACECHAT
→ n
Fixed "modern" Palace Chat protocol version number.
Debug
Opcode
Stack
Description
_TRACE
msg →
Writes a trace line to the Log tab.
TRACESTACK
… →
Destructive: pops and logs every remaining stack entry.
_BREAKPOINT
→
Logs a breakpoint marker (no real pause/step debugger exists here).
Drawing extensions
Extends the classic scriptable pen (PENCOLOR/PENSIZE/LINE/…) with fill, opacity, text styling, and new shape opcodes, rendered through the same paint layer.
Opcode
Stack
Description
PENFILLCOLOR
r g b →
Sets the pen's fill color for shapes.
PENFILLOPACITY / PENOPACITY
n →
Sets fill / stroke opacity (0–255).
PENFONT
fontName →
Sets the font used by DRAWTEXT.
PENBOLD / PENUNDERLINE / PENITALIC
on →
Text style toggles for DRAWTEXT.
PENSHADOW
n|[r g b a dx dy blur] →
Toggles a drop shadow, or sets one with explicit color/offset/blur. Omitted trailing slots fall back to documented defaults.
OVAL
x y w h →
Draws an oval centered at (x,y).
POLYGON
flatArr →
Draws a polygon from a flat [x0,y0,x1,y1,…] array.
DRAWTEXT
text x y →
Draws text at a position using the current pen font/style.
Dynamic pic manipulation
Swap and transform a spot's picture at runtime — animated signs, reveal-on-click secrets, image galleries. Pics are keyed per hotspot state (same model classic PICTS already uses), loaded through the same picture pipeline authored pictures use.
Opcode
Stack
Description
NBRROOMPICS
→ n
Count of distinct pictures loaded in the room.
GETPICPIXEL
x y state spotId → argb
Reads one pixel's color from a spot's picture.
GETPICDIMENSIONS
state spotId → w h
A spot's picture's pixel size.
GETPICLOC / SETPICLOC / SETPICLOCLOCAL
(get) state spotId → x y · (set) x y state spotId →
Reads/sets a picture's offset from its spot's anchor.
GETPICNAME
state spotId → str
A spot's picture's file name.
SETPICANGLE / GETPICANGLE
(set) angle state spotId → · (get) state spotId → angle
Rotation.
SETPICBRIGHTNESS / GETPICBRIGHTNESS
value state spotId
Brightness, -100…100.
SETPICOPACITY / GETPICOPACITY
value state spotId
Opacity, 0…100.
SETPICSATURATION / GETPICSATURATION
value state spotId
Saturation, -10000…10000.
SETPICHUE
degrees state spotId →
Hue rotation, -180…180.
SETPICCONTRAST
value state spotId →
Contrast, -100…1000.
SETPICBLUR
px state spotId →
Gaussian blur radius, 0…2500.
ADDPIC
fileName spotId →
Sets the picture for a spot's current state.
ADDPICNAME
fileName saveName spotId →
Same as ADDPIC; saveName is consumed but unused (no named resource registry here).
INSERTPIC
fileName index spotId →
Sets the picture for an explicit state index.
REMOVEPIC
index spotId →
Clears the picture for a state index.
SETPICFRAME / PAUSEPIC / RESUMEPIC / NBRPICFRAMES
args → (see note)
Argument-consuming stubs — animated per-state frame control was never finished in the PalaceChat reference either, so this mirrors that exactly.
EMBEDLAYER
args →
Stub — pops its arguments.
ADDSPOT
flatArr xLoc yLoc → spotId
Creates a new runtime-only spot from an outline array, returns its id. Removable only via REMOVESPOT.
REMOVESPOT
spotId →
Removes a spot previously created by ADDSPOT (authored spots can't be removed this way).
IMAGETOPROP
fileName →
Not implemented — this client has no script-triggered prop-authoring pipeline. Logs a note instead of silently doing nothing.
Example scripts
Small, complete scripts — each one drops straight into a room/spot's SCRIPT … ENDSCRIPT block (via the room/spot editor) or Cyborg.ipt as-is. Pop order and opcode behavior are verified against this client's actual VM, not assumed from the classic spec — see the tutorial above for a line-by-line walkthrough of the same style of code.
Greet the room on entry
The simplest possible script — one event, one line.
ON ENTER {
"Hi, I'm " USERNAME & "!" & SAY
}
Persistent visit counter
GLOBAL promotes a variable to flat client-side state that survives room changes and server transfers (persisted to localStorage) — so this keeps counting across your whole session, not just this room. See Limits: there's no cap on globals.
ON ENTER {
visitCount GLOBAL
visitCount ++
"You've entered rooms " visitCount ITOA & " time(s) this session." & LOCALMSG
}
A clickable light switch
A two-state spot (state 0 = off picture, state 1 = on picture, set up via PICTS in the spot editor) that flips itself on click. ME pushes the id of the hotspot the running script belongs to, so the same script works unmodified on any spot.
ON SELECT {
{
0 ME SETSPOTSTATE
} {
1 ME SETSPOTSTATE
} ME GETSPOTSTATE 0 == IFELSE
}
A self-rearming blinking spot
SETALARM schedules one ON ALARM fire — for a repeating animation, the handler re-arms itself as its last step. Remember a spot has only one pending alarm at a time (re-arming replaces it, see Limits), so this is safe to call every tick without timers piling up. 30 ticks ≈ half a second.
ON ENTER {
30 ME SETALARM
}
ON ALARM {
{
0 ME SETSPOTSTATE
} {
1 ME SETSPOTSTATE
} ME GETSPOTSTATE 0 == IFELSE
30 ME SETALARM
}
"Zap" — a drawn beam + sound effect
The scriptable pen (Painting & pen) draws a line from the avatar to wherever the pointer last was, then PAINTUNDO removes it a moment later (the built-in "draw then briefly show before removing" flash) — a one-shot beam effect. POSX POSY MOUSEPOS LINE lines up the stack exactly as LINE expects: x1 y1 x2 y2.
ON OUTCHAT {
{
255 0 0 PENCOLOR
3 PENSIZE
POSX POSY MOUSEPOS LINE
"Zap" SOUND
} CHATSTR LOWERCASE "zap" == IF
}
A random compliment on request
arr idx GET reads one array element; n RANDOM gives an integer in [0, n), so 4 RANDOM is always a valid index into a 4-item array.
ON OUTCHAT {
{
[ "sharp" "clever" "brilliant" "delightful" ] 4 RANDOM GET word =
"You seem quite " word & " today." & CHATSTR =
} CHATSTR LOWERCASE "compliment me" == IF
}
A wizard-only command
Gates a chat-triggered effect on the rank check opcodes (Access level) — your real elevated rank when connected, or the Debug tab's Rank control when testing offline. ISWIZARD is true for Wizard and everything above it on the ladder (God, Architect, Owner, Host), matching the classic spec's "operator or owner-level access" wording.
ON OUTCHAT {
{
{
"The lights flicker ominously..." ROOMMSG
1 DIMROOM
} {
"Sorry, wizards only." LOCALMSG
} ISWIZARD IFELSE
} CHATSTR LOWERCASE "flicker" == IF
}