Skip to content

Scene components ​

An entity is a set of components. Each is plain data with a fixed wire layout (the number is its wire id). Pass them to scene.create and scene.setComponent. The table is generated from the schema by gen.mjs. Status: Built (replicated headless, and drawn by the plots dev page, every surface lit by the world's lighting like the blocks). Not in the live world yet.

ComponentFieldsNotes
Transform (1)pos: Vec3; rot?: Quat; scale?: Vec3Relative to the parent, in world block coordinates when there's none. Quat is [x, y, z, w].
Model (2)asset: AssetRef; tint?: Color; detailBias?: numberA cuboid model, a glTF mesh or an item model. detailBias: above 0 keeps more detail at a distance. A glTF model made with no Collider gets one fitted to its solid part (see Collider)
Pose (3)clip?: string; time?: number; blendClip?: string; blendTime?: number; blend?: number; overrides?: BoneOverride[]Animation by clip and time; BoneOverride is { bone, rot?, offset?, scale? }.
Material (4)params: number[]; color?: Color; emissive?: Color; blendWithBlockWater?: BlockWaterBlendBlendWithBlockWater: the Model is a water surface drawn over the plot's real water blocks (BlockWaterBlend; register #117) Per-instance parameters of the model's node material.
Light (5)color: Color; intensity: number; range: number; cone?: numberA point light, or a spot with cone.
Emitter (6)particles: AssetRef; rate?: number; burst?: number; seed?: number; to?: Vec3; color?: ColorParticles in one of the core game's designs, drawn by every viewer's page: particles is "inventery:" and one of mote, flame, fire, smoke, spark, spell, enchant, glint, crit, heart, note, stars, confetti, drip (plot/protocol/particles.mjs; no other name is taken). rate: particles a second while it's there (up to 30); burst: that many at once when a viewer first sees it, and again whenever burst or seed changes (up to 40: change seed to fire it again, or make a short-lived entity with a Lifetime); to: each particle flies there (a world point: a tracer, a beam of sparks); color tints it (not fire, heart or confetti). Viewers' pages cap the plots' particles alive at once; the harness doesn't draw them.
AudioSource (7)sound: AssetRef; volume?: number; pitch?: number; loop?: boolean; range?: numberA positional sound on the entity.
Nametag (8)text: string; color?: ColorA plot's own label; never in the trusted style (the room sets trusted tags on real players only)
Collider (9)shape: "box" | "capsule" | "sphere" | "mesh"; size?: Vec3; body: "static" | "kinematic" | "dynamic"; restAfterSeconds?: number; offset?: Vec3Size is in blocks, standing on the entity's position and turned with its Transform; offset moves the box (its bottom centre from the position, before the turn). A "mesh" collider is its model's own shape: the model's own triangles, as drawn, less its foliage (alpha-masked or translucent materials: leaves, fronds, grass, which you walk through), simplified past 1024 triangles; it blocks only through that shape, on the room and the page alike (until a client has it, the client lets the player through; a model with nothing solid, or that isn't a mesh, blocks nothing, and the room warns you); size, its box for taps and raycasts, is filled in when missing. capsule and sphere are the box for now. A static body blocks players walking (not one under 0.25 tall and 0.5 across: a cup is only tapped and hit); a tap on it is a use (onUse). Kinematic and dynamic bodies never block walking: they are for taps, hits and raycasts. A glTF Model made with no Collider gets a box fitted to its solid part (its real bounds, scaled, with their offset; the foliage left out): static in the shared base, kinematic in a lobby or when nothing of it is solid. restAfterSeconds: a dynamic body that stays still this long becomes a resting prop.
Controller (10)kind: "walk" | "swim" | "fly" | "vehicle" | "mount" | "glide" | "rail" | "fixed" | "custom"; owner?: "server" | "client"; speed?: number; gravity?: number; jump?: numberFor players, use players.setMovement.
Velocity (11)v: Vec3The room moves the entity; projectiles stop at your plot's boundary.
Billboard (12)text: string; color?: Color; size?: numberText drawn in the scene, facing the viewer (a public board)
Lifetime (13)seconds: numberThe room removes the entity after this long.
Custom (14)schema: string; bytes: Uint8ArrayPlot-defined; drawn by no one; read by the plot's client code (the lobby's players only) Client code is In progress.
Speech (15)at: number; dur: numberThe entity is speaking: from server time at (time.now()) for dur seconds. Scene state, so everyone who sees the entity sees it speak (its head lifts with the speech and it gestures; a model with no bones nods and sways whole). ui.showDialogue sets it on its speaker; a plot may set it itself for speech with no dialogue.

Colliders: what you walk into ​

The room and every page collide with exactly the same shapes (plot/gltf/prop-collision.mjs, one definition for both; CI checks every sample and creator project for it). What each Collider means:

ColliderBlocks a walkTaps, hits, raycasts
{shape: "box", size, offset?, body: "static"}its box: size [w, h, d] standing on the position (moved by offset, the box's bottom centre from the position, before the turn), turned with the Transform; not if it's small (under 0.25 tall and 0.5 across)the box
{shape: "mesh", body: "static"} on a glTF modelthe model's own triangles, as drawn, less its foliage (below), simplified past 1024 triangles; through the Transform (scale, rotation, position)its box first, then its triangles
body: "kinematic" or "dynamic"neverthe box
a glTF Model with no Colliderthe host fits a box to the model's solid part (its real bounds, scaled, with their offset): static in the shared base, kinematic in a lobby or when nothing of the model is solidthat box

Walk-through parts. A model's foliage is its alpha-masked or translucent materials (glTF alphaMode MASK or BLEND: leaves, palm fronds, grass, cut-out fences). You walk through them; they're still drawn, tapped and hit. A palm blocks at its trunk only, a fern or a palm crown made only of leaves blocks nothing. Want leaves solid (a hedge maze)? Give the entity a box Collider.

No fallbacks. A "mesh" collider blocks only through its collision mesh, on the room and the page alike. Until a page has it (it comes with the terrain, before the models, within about a second of entering) the page lets the player through and the room's move check allows for it. A "mesh" collider on a model that isn't a mesh the room has (not loaded, over the asset cap, a cuboid or item model) blocks nothing, and the room says so in the plot's log, which inv test, inv push and inv draft show you.

Checking it. inv test <dir> lists any collider that's bigger than its model, a solid box over a model that's mostly leaves, or a "mesh" one with no collision mesh (--colliders: every collider, its kind and size); inv test --walk walks a body over the whole plot and reports invisible walls (held by a collider with nothing drawn near). In game, /debug hitboxes draws what collides: boxes, mesh colliders' triangles, and mesh colliders still on their way.

Players are entities you don't own ​

Real players have Transform, Model (their avatar), Pose, Controller, Nametag (trusted) and Collider, but the room owns them. scene.setComponent on a player is refused with playerBody; use players.teleport, players.setMovement, players.setAvatar and the rest. players.get(p).entity is their entity id, handy as a parent (a hat) or a sound's at.

Assets ​

AssetRef is a path in your bundle ("models/bed.glb", relative to plot.json's assets folder), a namespaced model id ("inventery:villager": base models arrive with pack:base, Planned), or a content hash ("h:" plus 32 hex characters). Supported formats: PNG (16 px a block for block and item art), the glTF subset (.glb, .gltf), cuboid models, and sounds. A missing asset draws as its average colour, or a box the size of its collider.