; iptscrae reference

iptscrae command reference

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.

TypeWritten asNotes
Number42 -7Signed 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.
SymbolmyVar tempCountA 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.

LimitOriginal clientThis implementation
Array size256 elementsStill 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 depth256 itemsUnbounded — limited only by available memory, not a fixed count.
Nested atomlists64 deepUnbounded — limited only by the JS call stack (in practice, thousands of frames).
Variables per handler64Unbounded.
Global variablesUnbounded (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 alarms64, queuedA 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 length1024 / 16384 bytesUnbounded (ordinary JS strings).
Spot state range-32768…32767Unbounded — any integer SETSPOTSTATE accepts is stored as-is (whether a picture exists for it is a separate, authoring-time concern).
Symbol nameLetters/digits/underscore, must start with a letter, ≤31 charsAny 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

OpcodeStackDescription
IFblock cond →Runs block if cond is truthy.
IFELSEtrueBlock falseBlock cond →Runs one block or the other.
WHILEbodyBlock condBlock →Runs condBlock, pops its result; loops while truthy, running bodyBlock each pass.
EXECblock →Runs a block (or atom-list) as a subroutine. A literal 0 is treated as an explicit no-op.
FOREACHblock arr →Runs block once per array element, with the element pushed before each run.
EXITAborts the whole running script (all enclosing blocks).
RETURNReturns from the current subroutine (block called via EXEC); does not abort the outer script.
BREAKBreaks out of the innermost WHILE/FOREACH loop.
DELAYticks →Classic client busy-waits; here the operand is consumed and execution continues immediately (browser-safe).

Variables & symbols

OpcodeStackDescription
DEFvalue sym →Defines/sets a local variable to value.
GLOBALsym →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.
CLEARGLOBALsym →PalaceSpace extension: un-declares one global (back to an ordinary local). Doesn't touch any other global — there's no "clear everything" opcode.
NBRGLOBALS→ nPalaceSpace extension: count of currently-declared globals. See Limits for the 256-global cap.

Identity & spot state

OpcodeStackDescription
ME→ spotIdPushes the id of the hotspot the running script belongs to.
ID (alias of ME)→ spotIdClassic alias.
DEST→ destRoomPushes this hotspot's authored destination (door target).
GETSPOTSTATEspotId → nReads a spot's current picture-state index.
SETSPOTSTATEn spotId →Sets a spot's picture-state index (switches its displayed picture).
SETSPOTSTATELOCALn spotId →Local-only variant — no distinct behavior in this single-user client.
LOCKspotId →Marks a spot locked, plays the door-lock sound, fires ON LOCK.
UNLOCKspotId →Marks a spot unlocked, plays the door-unlock sound, fires ON UNLOCK.
ISLOCKEDspotId → 0|1Reads a spot's lock state.

Alarms & timing

OpcodeStackDescription
SETALARMticks spotId →Schedules ON ALARM to fire on spotId after ticks/60 seconds. Re-arming the same spot replaces its pending timer.
ALARMEXECblock ticks →Runs block after a delay, independent of any spot's alarm slot.
STOPALARMspotId →Cancels a pending SETALARM for one spot.
STOPALARMSCancels every pending alarm/timer.
TICKS→ n~1/60s ticks since page load (classic Mac TickCount analogue).
DATETIME→ nCurrent 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

OpcodeStackDescription
GOTOROOMroomId →Navigates to a room by id and aborts the rest of the script.
NETGOTOurl →Opens a URL, or joins another palace for a palace:// link.
GOTOURL (alias of NETGOTO)url →Classic alias.
SOUNDname →Plays a room sound once, fire-and-forget.
MIDIPLAYname →Plays a MIDI tune once.
MIDILOOPloopCount name →Plays a MIDI tune looped.
MIDISTOPStops the currently playing MIDI tune.
LOCALMSGtext →Shows a chat balloon locally only (never sent over the wire).
ROOMMSGtext →Broadcasts a balloon message to the whole room.
SAYtext →Sends text as the user's own chat, exactly as if typed (incl. backtick-command handling).
CHAT (alias of SAY)text →Classic alias.
GLOBALMSGtext →Server-wide announcement balloon (wizard rank only when live).
SUSRMSGtext →Shows a balloon message (no distinct target-user argument).
PRIVATEMSGtext target →Sends a private whisper to one user.
LOGMSGtext →Writes a line to the Log tab only (no balloon).
STATUSMSGtext →Flashes the status bar (and records to the Log).

Boolean & operators

OpcodeStackDescription
ANDa b → 0|1Logical AND.
ORa b → 0|1Logical OR.
NOTa → 0|1Logical NOT.
+ - * / %v1 v2 → nArithmetic (signed 32-bit); + concatenates when either side is a string.
== != <>v1 v2 → 0|1Equality / inequality, case-insensitive for strings. Note: single = is assignment, not comparison.
< > <= >=v1 v2 → 0|1Ordering, lexicographic (case-insensitive) for strings.
&v1 v2 → strString concatenation.
! v → 0|1Unary logical NOT (operator form).
++ --sym →Increment/decrement a variable in place.
+= -= *= /= %= &=value sym →Compound assignment.
=value sym →Assignment (not comparison — that's ==).

Strings & patterns

OpcodeStackDescription
GREPSTRsubject pattern → 0|1Classic-dialect regex match (case-sensitive; ? and | are literal). Sets the match state GREPSUB reads.
GREPSUBtemplate → strSubstitutes $0$9 / & from the last GREPSTR match into template.
SUBSTRwhole frag → 0|1Case-insensitive literal "contains" test — not a regex, despite the name.
REGEXSTRsubject pattern → 0|1Modern JS RegExp boolean match. Keeps its own match state, separate from GREPSTR.
STRTOATOMstr → blockTokenizes a string into an executable block for EXEC.
ITOAn → strNumber to string.
ATOIstr → nString to number (0 if unparsable).
LOWERCASE / UPPERCASEstr → strCase conversion.
TRIMstr → strPalaceSpace extension: strips leading/trailing whitespace.
LENGTHarr → nArray length (arrays only — wrong type pushes 0).
STRLENstr → nString length.
STRINDEXwhole frag → nCase-insensitive substring offset, or -1.
SUBSTRINGstr offset length → strExtracts a substring; negative length means "rest of string".
REPLACEsubject pattern replacement → strReplaces the first literal match.
REPLACEALLsubject pattern replacement → strReplaces every literal match.
ENCODEURL / DECODEURLstr → strURI component encode/decode.
CHARTONUMstr → nFirst character's code point (0–255).
NUMTOCHARn → strCode point (0–255) to single-character string.
RANDOMn → rRandom integer in [0, n).
SPLITstr delimiter → arrPalaceSpace 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

OpcodeStackDescription
ABSVALUEn → nAbsolute value.
ROUNDNUMn → nRounds to nearest integer.
SINE / COSINE / TANGENTdegrees → nTrig functions; degrees in, result ×1000 fixed-point out.
SQUAREROOTn → nInteger square root; aborts the script if n is negative.
SQRT (alias of SQUAREROOT)n → nTalx alias.
BITAND / BITOR / BITXORa b → nBitwise AND / OR / XOR.
BITNOTn → nBitwise complement.

Array & stack

OpcodeStackDescription
ARRAYn → arrAllocates a zero-filled array of length n (max 256). Arrays can also be written as literals: [ a b c ].
GETarr idx → valueReads an array element; out-of-range aborts the script.
PUTvalue arr idx →Writes an array element in place; out-of-range aborts the script.
JOINarr separator → strPalaceSpace extension: joins array elements (stringified) with separator. Same pop order as PUT.
SORTarr → arrPalaceSpace 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).
POPv →Discards the top of the stack.
DUPv → v vDuplicates the top of the stack.
SWAPa b → b aSwaps the top two stack items.
OVERa b → a b aDuplicates the second-from-top item.
PICK… n → … vCopies the item n deep (0 = top).
STACKDEPTH→ nPushes the current stack depth.
TOPTYPE→ nType tag of the top stack value, without popping it.
VARTYPE→ nType tag of the top stack value, resolving a symbol to its bound value first.

Room & user info

OpcodeStackDescription
USERNAME→ strThe local user's display name.
SERVERNAME→ strThe currently connected server's name.
ICAMEFROM→ strTalx extension: the previous palace's name after a server hop, else "null".
ROOMNAME→ strCurrent room's name.
ROOMID→ nCurrent room's id.
ROOMWIDTH / ROOMHEIGHT→ nCurrent room's real pixel dimensions (512×384 unless the server defines a larger background).
NBRROOMUSERS→ nCount of users in the room (live roster + self, or 1 offline).
ROOMUSERidx → userIdUser id at roster index idx (self at 0).
WHOCHAT→ userIdUser id that generated the current/last INCHAT event.
WHOENTER / WHOLEAVE / WHOMOVE→ userIdUser id from the current/last USERENTER/USERLEAVE/USERMOVE event — see Events.
WHONAMEuserId → strDisplay name for a user id.
WHOME→ userIdThe local user's own id.
WHOTARGET→ userIdCurrently selected whisper target's id, or 0.
WHOPOSwho → x yA user's room position. Pushes two values (x, then y) — not a point array.
MOUSEPOS→ x yCurrent mouse position over the room. Also two separate values.
SAYATtext x y →Says text as the user's own chat, positioned as @x,y text.
POSX / POSY→ nLocal avatar's current room position.
SETPOSx y →Moves the avatar to an absolute position (clamped by nav-area / forbidden-spot rules, same as a click).
MOVEdx dy →Moves the avatar by a relative offset.
INSPOTspotId → 0|1Whether the avatar's current position is inside a spot's outline.

Avatar appearance

OpcodeStackDescription
SETCOLORn →Sets the avatar body color by classic palette index (0–15); out-of-range resets to 0.
SETCOLOURn →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.
SETFACEn →Sets the avatar's face expression by classic face number.
SETFACESETn|str →Talx extension: selects the smiley art pack.
SETSMILEYSET (alias of SETFACESET)n|str →Same, alternate name.
USERPROPn → propIdProp id worn at don-index n.
NBRUSERPROPS→ nCount 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.

OpcodeStackDescription
ISGUEST→ 0|1Current rank is exactly Guest.
ISMEMBER / ISWIZARD / ISGOD / ISARCHITECT / ISOWNER→ 0|1True at or above that rank -- e.g. an Owner also reads ISWIZARD/ISGOD/ISARCHITECT as true.
ISHOST→ 0|1Current rank is exactly Host (the top of the ladder).

Worn props

OpcodeStackDescription
DONPROPpropIdOrName →Wears a prop by legacy id, bag name, or Modern UUID.
DOFFPROPRemoves the most recently worn prop.
REMOVEPROPpropIdOrName →Removes a specific worn prop.
CLEARPROPSRemoves every worn prop.
NAKED (alias of CLEARPROPS)Same effect, classic name.
SETPROPSidArray →Replaces the whole worn set at once.
TOPPROP→ propIdId of the topmost worn prop, or 0 when naked.
DROPPROPx y →Takes the top worn prop off and drops it in the room as a loose prop at (x,y).

Loose props

OpcodeStackDescription
ADDLOOSEPROPpropIdOrName x y →Drops a prop into the room at (x,y).
CLEARLOOSEPROPSRemoves every loose prop in the room.
SHOWLOOSEPROPSForces 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.

OpcodeStackDescription
PAINTUNDORemoves the last drawn paint command in the room.
PAINTCLEARClears every paint command in the room.
PENFRONT / PENBACKSelects which layer the scriptable pen draws on.
PENCOLORr g b →Sets the pen's stroke color (0–255 each channel).
PENSIZEn →Sets pen stroke width, clamped 1–8.
PENPOSx y →Moves the pen to an absolute position without drawing.
PENTOdx dy →Moves the pen by a relative offset without drawing.
LINETOdx dy →Draws a relative line from the pen's current position and advances it.
LINEx1 y1 x2 y2 →Draws an absolute line and moves the pen to the endpoint.

OS & misc

OpcodeStackDescription
SHELLCMD / LAUNCHAPP / LAUNCHPPA / TALKPPAarg →No-op stubs — a browser cannot shell out to the OS. Arguments are consumed for stack fidelity.
MACROn →Fires ON MACROn on the cyborg and room hotspots (n = 0–12).
DIMROOMn →Darkens the room background; 0 or ≥100 is full brightness.
BEEPNo-op (no system beep in a browser).
KILLUSERtargetUser →Whispers a `kill command at the target; the server applies the actual kick.
IPTVERSION→ nInterpreter 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.

OpcodeStackDescription
SPOTIDXidx → spotIdSpot id at room-authoring index idx.
SPOTNAMEspotId → strA spot's authored name.
SPOTDESTspotId → nA spot's destination (door target).
GETSPOTLOCspotId → x yA spot's authored location.
SETSPOTLOCx y spotId →Moves a spot at runtime.
GETSPOTTYPEspotId → nNumeric sub-kind (door-like types are > 0).
SETSPOTDESTdest spotId →Rewires a door's destination at runtime.
DOORIDXidx → spotIdSpot id of the idx-th door-like spot in the room.
NBRDOORS→ nCount of door-like spots in the room.
NBRSPOTS→ nCount of every spot in the room.
LOCINSPOTx y spotId → 0|1Exact polygon containment test against a spot's authored outline (distinct from INSPOT's avatar-proximity check).
GETSPOTPOINTSspotId → flatArrA spot's outline as a flat [x0,y0,x1,y1,…] array.
SETSPOTPOINTSflatArr x y spotId →Replaces a spot's outline and location at runtime.
SETSPOTOPTIONSoptions layer type spotId →Sets a spot's type override. layer/options are consumed but have no effect (no layer/bitflag system in this renderer).
GETSPOTOPTIONSspotId → type layer optionsReads back the type override; layer/options always 0.
SETSPOTNAMELOCALname spotId →Renames a spot at runtime, local-only.
GETSPOTTEXTSIZEmaxW maxH spotId → x y w hMeasures a spot's authored name text, clamped to maxW/maxH.
SETSPOTSCRIPTblock eventName spotId →Overrides what ON eventName runs for one spot at runtime, without touching its other events.
CACHESCRIPTblock 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).

OpcodeStackDescription
SETSPOTFONTbold strikeout underline italic center size pad color name spotId →Sets font styling for a spot's label.
SETSPOTSTYLEbgColor borderColor borderSize spotId →Sets fill/border styling.
SETSPOTCLIPclipMode spotId →Sets clip mode.
SETSPOTCURVEtension spotId →Sets corner curve tension.
SETSPOTGRADIENTangle color2 color1 spotId →Sets a linear gradient fill.
SETSPOTPATHGRADIENTuseSpotPoint centerColor surround spotId →Sets a radial/path gradient fill.
SETSPOTPICMODEpicMode spotId →Sets picture display mode.

Loose-prop query & removal

OpcodeStackDescription
NBRLOOSEPROPS→ nCount of loose props in the room.
LOOSEPROPidx → propIdProp id of the loose prop at index idx.
LOOSEPROPIDXpropId → idxIndex of a loose prop by its prop id, or -1.
LOOSEPROPPOSidx → x yRoom position of a loose prop by index.
REMOVELOOSEPROPidx →Removes one loose prop by index.
MOVELOOSEPROPx y idx →Repositions one loose prop by index.
WHEREPROP→ x yOnly meaningful inside a loose-prop-added/moved/deleted event, which this client never dispatches — always pushes 0 0.

UI & dialogs

OpcodeStackDescription
ALERTBOXmsg →Native blocking alert dialog.
CONFIRMBOXmsg → 0|1Native blocking OK/Cancel dialog.
PROMPTmsg defaultValue → strNative blocking text-input dialog.
SETCURSORn →Sets the room cursor to a classic cursor number, mapped to a CSS cursor.
SETCURSORPICfileName x y →Sets a custom image cursor with a hotspot offset.
SETTOOLTIPmsg →Sets a tooltip on the room canvas (native browser title tooltip).
SETHELPTAG (alias of SETTOOLTIP)msg →Same effect.
CLEARTOOLTIP / CLEARHELPTAGClears the room tooltip.
UPDATELATERIncrements a redraw-defer counter. This renderer redraws immediately on state changes regardless, so this doesn't suppress interim redraws.
UPDATENOWClears 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.

OpcodeStackDescription
SOUNDOPENname → handleOpens a sound file, returning a handle.
SOUNDPLAYhandle →Plays from the current position.
SOUNDLOOPhandle →Plays looped from the start.
SOUNDPAUSEhandle →Pauses playback.
SOUNDSEEKposition handle →Seeks to position ms without changing play state.
SOUNDPLAYFROMposition handle →Seeks then plays.
SOUNDSTOPStops and rewinds every open sound handle.
SOUNDISPLAYINGhandle → 0|1Whether one handle is currently playing.
ISSOUNDPLAYING→ 0|1Whether anything is currently playing, across every open handle.
SOUNDGETPOSITIONhandle → msCurrent playback position.
SOUNDLENGTHhandle → msTotal duration.

Remaining user & avatar

OpcodeStackDescription
HASPROPpropId → 0|1Whether the local user is wearing a specific prop.
USERID→ userIdThe local user's own id (equivalent to WHOME).
SETUSERNAMEname →Renames the local user (persists and pushes to the server nickname when connected).
SETLOC / SETLOCLOCALx y →Absolute avatar move — same as SETPOS.
HIDEAVATARS / SHOWAVATARSHides/shows every avatar in the room.
AUTOUSERLAYERon →Records a flag; this renderer already draws avatars in deterministic order, so it has no further effect.
WHOCOLORuserId → nA user's avatar body color.
WHOFACEuserId → nA user's current face-expression number.
PROPDIMENSIONSpropId → w hA prop's pixel size, from the loaded manifest.
PROPOFFSETSpropId → x yA prop's anchor offset, from the loaded manifest.
LOADPROPSidArray →No-op beyond popping — this client loads its whole prop bag up front, nothing to fetch on demand.

Misc / math / hash

OpcodeStackDescription
POWERbase exp → nInteger exponentiation.
BITSHIFTLEFT / BITSHIFTRIGHTvalue amount → nBit shifts.
NEWHASH→ hashCreates an empty key/value hash handle.
HASHTOJSONhash → strSerializes a hash to a JSON string.
JSONTOHASHstr → hashParses JSON into a hash handle.
ISFUNCTIONname → 0|1Whether name is a callable opcode right now (checks classic always, extended only if the preference is on).
GETTIMEZONE→ strThe browser's IANA timezone name.
CLIENTID→ userIdNo separate client-vs-user id concept here — same as the local user id.
NBRSERVERUSERS→ nTotal users connected to the server (not just this room).
GETROOMOPTIONS→ nCount of authored room flags.
ROOMPICNAME→ strThe room background picture's file name.
MEDIAADDRESS→ strBase URL media is served from for the active server.
ISKEYDOWNkeyCode → 0|1Live physical-key state.
ISRIGHTCLICK→ 0Always false — right-click context isn't threaded through this client's click dispatch.
TEXTSPEECHtext →Speaks text aloud via the browser's speech synthesis, when available.
REGEXP (alias of REGEXSTR)subject pattern → 0|1Same as REGEXSTR.
REGEXPREPLACEtemplate → strSubstitutes $1$9 from the last REGEXSTR/REGEXP match.
OPENPALACE→ 0Legacy multi-window opcode — no such window model in a browser client.
PALACECHAT→ nFixed "modern" Palace Chat protocol version number.

Debug

OpcodeStackDescription
_TRACEmsg →Writes a trace line to the Log tab.
TRACESTACK… →Destructive: pops and logs every remaining stack entry.
_BREAKPOINTLogs 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.

OpcodeStackDescription
PENFILLCOLORr g b →Sets the pen's fill color for shapes.
PENFILLOPACITY / PENOPACITYn →Sets fill / stroke opacity (0–255).
PENFONTfontName →Sets the font used by DRAWTEXT.
PENBOLD / PENUNDERLINE / PENITALICon →Text style toggles for DRAWTEXT.
PENSHADOWn|[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.
OVALx y w h →Draws an oval centered at (x,y).
POLYGONflatArr →Draws a polygon from a flat [x0,y0,x1,y1,…] array.
DRAWTEXTtext 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.

OpcodeStackDescription
NBRROOMPICS→ nCount of distinct pictures loaded in the room.
GETPICPIXELx y state spotId → argbReads one pixel's color from a spot's picture.
GETPICDIMENSIONSstate spotId → w hA 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.
GETPICNAMEstate spotId → strA spot's picture's file name.
SETPICANGLE / GETPICANGLE(set) angle state spotId → · (get) state spotId → angleRotation.
SETPICBRIGHTNESS / GETPICBRIGHTNESSvalue state spotIdBrightness, -100…100.
SETPICOPACITY / GETPICOPACITYvalue state spotIdOpacity, 0…100.
SETPICSATURATION / GETPICSATURATIONvalue state spotIdSaturation, -10000…10000.
SETPICHUEdegrees state spotId →Hue rotation, -180…180.
SETPICCONTRASTvalue state spotId →Contrast, -100…1000.
SETPICBLURpx state spotId →Gaussian blur radius, 0…2500.
ADDPICfileName spotId →Sets the picture for a spot's current state.
ADDPICNAMEfileName saveName spotId →Same as ADDPIC; saveName is consumed but unused (no named resource registry here).
INSERTPICfileName index spotId →Sets the picture for an explicit state index.
REMOVEPICindex spotId →Clears the picture for a state index.
SETPICFRAME / PAUSEPIC / RESUMEPIC / NBRPICFRAMESargs → (see note)Argument-consuming stubs — animated per-state frame control was never finished in the PalaceChat reference either, so this mirrors that exactly.
EMBEDLAYERargs →Stub — pops its arguments.
ADDSPOTflatArr xLoc yLoc → spotIdCreates a new runtime-only spot from an outline array, returns its id. Removable only via REMOVESPOT.
REMOVESPOTspotId →Removes a spot previously created by ADDSPOT (authored spots can't be removed this way).
IMAGETOPROPfileName →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
}

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
}