Appearance
Limits and refusals
Every ceiling in one place, and what every refusal message means.
Nothing here crashes a script. A verb that cannot do what you asked answers nil and a reason in plain words, and your script carries on.
luau
local ok, why = self:move_to(0, 5, 0)
if not ok then
print("could not move: " .. why)
endCeilings
Per slice
A slice is one event firing, or the script being loaded.
| Limit | Value |
|---|---|
| Instruction units | 50,000 |
| World effects | 8 |
Events sent (emit + send) | 16 |
print lines | 32 |
Length of one print line | 512 bytes |
A world effect is anything that changes the world: a move, a rotation, a tween, a spawn, a property write, a sound, a terrain stroke, a block edit, damage. Reads are free — raycast, players:list, world:clock, self:get and the whole of vec cost nothing.
Per script
| Limit | Value |
|---|---|
| Memory | 4 MB |
Keys in one self:set | 32 |
| Animation layers | 8 |
| Locomotion overrides | 16 |
Per object
| Limit | Value |
|---|---|
| Scripts | 8 |
| Tags | 16 |
| Store keys | 256 |
| Store key length | 256 bytes |
| Store value size | 64 KB |
| Store total | 1 MB |
A personal HUD is tighter, because its memory lives on the central server for everyone, forever:
| Limit | Value |
|---|---|
| Store keys | 64 |
| Store value size | 8 KB |
| Store total | 32 KB |
| All your HUDs together | 1 MB |
Per player, per experience
What one person keeps in a world — players.store. It lives on the central server and follows them between machines:
| Limit | Value |
|---|---|
| Store keys | 128 |
| Store key length | 256 bytes |
| Store value size | 64 KB |
| Store total | 256 KB |
| All your experiences together | 16 MB |
Per region
The region's shared memory — world.store — is one drawer for the whole world, not one per object:
| Limit | Value |
|---|---|
| Store keys | 4096 |
| Store key length | 256 bytes |
| Store value size | 64 KB |
| Store total | 8 MB |
The loading screen
What world.loading accepts. Past any of these the call is refused with a reason, not silently trimmed — a screen that quietly dropped four of your twelve tips is a bug you would hunt in the wrong place.
| Limit | Value |
|---|---|
| Headline | 80 characters |
| Subtitle | 160 characters |
| Tips | 8 |
| One tip | 160 characters |
| Tip dwell | 2–30 seconds |
| Background / logo, longest side | 4096 px |
| Limit | Value |
|---|---|
| Active scripts | 768 |
| Script-made objects alive at once | 256 |
| Named actions | 32 |
Objects one tagged query may return | 256 |
| Pieces in a copied group | 64 |
http
What one world may send outside. The request count is generous because requests are cheap; the ceiling that actually binds is in bytes, because bandwidth is what costs.
| Limit | Value |
|---|---|
| Requests a second | 10, banking up to 60 |
| Bytes a second | 512 KB, banking up to 16 MB |
| Bytes a day | 5 GB |
| Requests in the air, per script | 8 |
| Requests in the air, per world | 64 |
| Requests waiting for a slot | 64 |
| Body sent | 64 KB |
| Answer accepted | 256 KB |
| Time before it gives up | 10 s (5 s to connect) |
| Headers | 16, up to 1 KB each and 4 KB in total |
| URL | 2 KB |
The per-second budgets bank quiet time. A world that has been idle for a minute can fire all 60 at once — which is what makes forty players arriving together work — and then settles back to 10 a second.
A destination that fails 5 times in a row is left alone for 30 seconds, and calls to it answer http cooling immediately. A 404 is not a failure.
HUD stages
A stage is a live 3D render every frame — the most expensive thing a HUD can hold, by a wide margin.
| Limit | Value |
|---|---|
| Stages in one HUD | 4 |
| Pieces on one stage | 64 |
| Parts in a set on the shelf | 256 |
Over either of the first two, the HUD is refused with a reason. A menu backdrop is dozens of pieces, not a city.
A set counts as the one piece you wrote, however many parts it brings — the ceiling on those is the shelf's own, applied when you publish it.
Ranges
| Thing | Range |
|---|---|
| Coordinates | ±100 km |
| Scale | 0.01 – 10,000 |
| Velocity and push | up to 100 m/s |
| Tween length | 0.05 – 600 s |
| Region radius | 0.5 – 96 m |
| Raycast reach | up to 512 m |
| Spawn life | default 30 s, max 300 s (0 = while the region runs) |
| Rate of fire and cycle | 0.05 – 5 s |
| Light radius | 0 – 96 m |
| Sound volume | 0 – 4 |
| Sound pitch | 0.25 – 4 |
| Wind | up to 40 m/s |
| Terrain brush radius | 0.5 – 64 m |
| Terrain brush strength | 0 – 8 m |
| Animation speed | −8 to 8 |
| Event payload | 3 levels deep, 32 entries, 512-byte strings |
| Channel name | 1 – 64 characters |
What stops a script
Two very different outcomes.
The event is abandoned, the script lives
An ordinary mistake — indexing something that is nil, calling a method that does not exist, arithmetic on text. The error is written to your console and the next event runs normally.
The script is killed
| Message | Meaning |
|---|---|
script budget exceeded | one slice used more than 50,000 instruction units |
script memory limit exceeded | the script went over 4 MB |
It does not run again until you save it. Both mean the same thing in practice: a loop without an exit. Use a timer instead — a slice may not take long, but you may have as many slices as you like.
Refusal reasons
Anywhere
| Reason | What happened |
|---|---|
permission | this world, or this player, has not granted what the verb needs. See Permissions |
too many commands | more than 8 world effects in one event |
too many moves | the same, from a movement verb |
read-only key | that property is not writable by scripts — see the list |
Moving and physics
| Reason | What happened |
|---|---|
not dynamic | push or set_velocity on an object that is not a physics body. Set dynamic = true first |
unknown part | that part was deleted, or never existed |
look_at: target is not a direction | the target is directly above, directly below, or the object's own position |
Memory
| Reason | What happened |
|---|---|
store full | the key ceiling is reached (256 per object, 4096 for the world). Existing keys still read, overwrite and delete |
value too large | over 64 KB (8 KB on a HUD) |
Carrying
| Reason | What happened |
|---|---|
permission | that player has not interacted with this object |
socket | no attachment point by that name — see Sockets |
child | hold the root of a group, not one of its pieces |
held | somebody else has it |
full | their hands are full |
not held | drop on something nobody was carrying |
Making things
| Reason | What happened |
|---|---|
too many spawned objects | the region already has 256 script-made objects alive |
| a spawn is made of one thing | you named more than one of shape, model, copy |
Damage
| Reason | What happened |
|---|---|
this object has no max_health — it cannot be damaged | set max_health first. This is how a world says what is breakable |
Terrain and blocks
| Reason | What happened |
|---|---|
invalid brush | radius or strength outside the allowed range |
invalid position | outside the world |
box too big | a fill covering too many cells — refused whole, and said now rather than half-done later |
Modules
| Reason | What happened |
|---|---|
unknown module '…' | built-ins are self, events, players, vec, world, objects. For your own module, attach a sibling script with that name to the same object and re-save this one |
cyclic require of module '…' | two of your modules require each other |
Reading a refusal
Three shapes, and they mean different things:
luau
local hit = world:raycast(...)| Answer | Meaning |
|---|---|
| a value | it worked |
nil | a real answer meaning "nothing" — no hit, no such item, nobody here |
nil, reason | the call could not be carried out |
world:raycast returning plain nil is a clear line of sight, not an error. self.contents:get returning plain nil means there is no such item. Absent is an answer.
See also
- How scripts run — slices and budgets explained
- Reading errors — finding the line
- Permissions
