NEOLearn

The neo module

API reference for the neo script module, the World class and its members, the on decorator, sleep, all and Python's gather.

Updated October 17, 2026

On this page

neo is the module every script imports its API from. It is the only built-in module in JavaScript; in Python it sits beside NEO's standard modules. The module is frozen: scripts can read it but not change it.

JavaScript
import { World, on, sleep, all } from "neo";

For a guided tour, see The World API.

Summary#

MemberKindLanguagesDescription
WorldclassbothThe base class of every script's world class.
statepropertybothThe script's saved state.
emitmethodbothSends an event to the game.
querymethodbothLists the visible entities of a kind.
spawnmethodbothCreates an entity of a spawnable kind.
rngpropertybothThe world-seeded random stream.
timepropertybothSimulation time in seconds.
ondecoratorbothSubscribes a method to an event.
sleepfunctionbothWaits for simulation time to pass.
allfunctionJavaScript (also importable in Python)Waits for a list of promises.
gatherfunctionPythonWaits for several awaitables.

Names are the same in both languages. Fields and state keys that you define are camelCase in JavaScript and snake_case in Python.

World#

JavaScript
export default class MyWorld extends World { ... }
Python
class MyWorld(World): ...
default = MyWorld

The base class of a script's world class. Each script has exactly one:

  • JavaScript: the class must be the file's export default class, and it must extend World. Loading fails with a script's default export must be a class extending World otherwise.
  • Python: the class named by default = Name, or, without that line, the last class in the file whose bases include World.

The engine creates one instance when the script loads. Inside methods, this (Python: self) is that instance. A state class field, if present, gives initial state values (see state).

state#

JavaScript
this.state
TypeA world-backed object (JavaScript) / an object with attribute access (Python)
WritableIts keys are; state itself can't be replaced after loading

The script's state. The world holds it: it survives hot reloads and is included in the script's state hash. Declare initial values as a class field; keys the world doesn't hold yet are set to them at load.

  • Reads return values. Lists and maps come back as frozen copies, so assign a new list or map to change one.
  • Writes accept numbers, strings, booleans, null/None, lists, string-keyed maps and entity objects. Each write is one world call.
  • delete this.state.key sets the key to null.
  • Key names follow the camelCase/snake_case rule: this.state.lastOpener and self.state.last_opener are the same key.
JavaScript
state = { opened: 0, lastOpener: null };

@on("interact", { tag: "Door" })
open(player, door) {
  this.state.opened += 1;
  this.state.lastOpener = player.id;
}

See Script state.

emit#

JavaScript
emit(event, payload)
NameTypeDescription
eventstringThe event name.
payloadany world value (optional)Data sent with the event. Omitted: null.

Returns: nothing.

Sends an event to the game. The game's Rust systems receive it as a ScriptEmitted message (script, event, payload) after the script runs. It doesn't reach other scripts' handlers. Throws TypeError if the payload holds a function, a symbol or a Python set. Counts as one world call.

JavaScript
this.emit("puzzle_solved", { by: this.state.lastOpener });

query#

JavaScript
query(kind)
NameTypeDescription
kindstringThe kind name, as declared in Rust ("Platform").

Returns: a list of entity objects, in id order. Empty if no entity of that kind exists, or if the kind isn't exposed to the script.

Counts as one world call.

JavaScript
const platform = this.query("Platform")[0];
if (platform) platform.position = { x: 0, y: 1, z: 0 };

spawn#

JavaScript
spawn(kind, fields)
NameTypeDescription
kindstringA kind the script may spawn.
fieldsmap (optional)Initial values for fields the script may write. Field names follow the camelCase/snake_case rule.

Returns: the new entity's object. Its id is assigned at once and never reused.

Throws TypeError when the kind isn't spawnable by the script (Cube isn't spawnable by scripts), when a field can't be set by the script, when a number is outside its field's range, or when fields isn't a map. In Python, the first two are PermissionError. The game creates the entity right after the script's turn; if the game has no spawner for the kind, that is recorded as the script's fault. Counts as one world call.

JavaScript
const cube = this.spawn("Cube", { size: 0.5, position: { x: -4, y: 3, z: 0 } });
this.emit("cube_spawned", { id: cube.id });

rng#

JavaScript
this.rng.random()
MemberReturns
random()A float in [0, 1).

The world's random stream for this script, seeded from the world seed and the script's name. In JavaScript, Math.random() draws from the same stream. Every run of the same game draws the same numbers.

JavaScript
if (this.rng.random() < 0.25) this.state.rolls += 1;

time#

JavaScript
this.time
Typenumber (Python: float)
WritableNo

Simulation time in seconds. It follows the game's fixed timestep, not the wall clock. A script loaded into a running game starts at the game's current time.

JavaScript
this.emit("round_go", { at: Math.round(this.time * 1000) });

Functions#

on#

JavaScript
@on(event, options)
Python
@on(event, tag=None, every=None)
NameTypeDescription
eventstringThe event to handle. "tick" runs every simulation tick with dt (milliseconds since the last tick).
tagstring (optional)A kind name. The handler runs only when the event's second argument is an entity of that kind the script can see.
everynumber (optional)Seconds. The handler runs at most once per every simulation seconds, counted from its first run.

Returns: a method decorator.

Decorates an instance method of the world class (not a static method, getter or setter). The method receives the event's arguments. A method can have several @on decorators. Handlers for the same event run in the order the methods appear in the class. Handlers may be async.

JavaScript
@on("touch", { tag: "Platform" })
touched(player, platform) { this.state.touches += 1; }

@on("tick", { every: 2 })
drop() { /* every 2 s */ }

See Events and handlers.

sleep#

JavaScript
sleep(seconds)
NameTypeDescription
secondsnumberSimulation seconds to wait. Rounded to whole milliseconds.

Returns: a promise (Python: an awaitable) that resolves with no value.

Resolves at the first tick whose simulation time is at or past the target, before that tick's tick handlers. Timers due on the same tick resolve by target time, then creation order. sleep(0) resolves on the next tick. A hot reload drops pending sleeps.

JavaScript
@on("round_start")
async start() {
  await sleep(1.0);
  this.emit("round_go", { at: this.time });
}

all#

JavaScript
all(promises)
NameTypeDescription
promiseslist of promises or valuesWhat to wait for.

Returns: a promise of a list with each result, in argument order.

The same as Promise.all: it rejects if any input rejects. Python scripts can import it too, but it hides Python's built-in all; use gather.

JavaScript
const got = await all([sleep(0.05).then(() => "late"), Promise.resolve("now")]);
// ["late", "now"]

gather#

Python
gather(*awaitables)
NameTypeDescription
*awaitablescoroutines or awaitablesWhat to wait for, as separate arguments.

Returns: an awaitable of a tuple with each result, in argument order.

Python only. Raises if any input raises.

Python
async def after(s, v):
    await sleep(s)
    return v

got = await gather(after(0.05, "late"), after(0.016, "soon"))
self.state.got = list(got)  # ["late", "soon"]

Entity objects#

query, spawn and event arguments return entity objects. They have:

MemberDescription
idAlways readable. A number in JavaScript, an int in Python.
Exposed fieldsRead and written by name; access is checked on every use. position is a { x, y, z } map.
Exposed methodsCalled by name. In a running game the call returns null; the Rust method runs right after the script's turn.

Nothing else exists on an entity object. Denied reads, writes and calls throw (TypeError in JavaScript, PermissionError in Python). See Exposure and permissions.

See also#