Appearance
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.
| Component | Fields | Notes |
|---|---|---|
Transform (1) | pos: Vec3; rot?: Quat; scale?: Vec3 | Relative to the parent, in world block coordinates when there's none. Quat is [x, y, z, w]. |
Model (2) | asset: AssetRef; tint?: Color; detailBias?: number | A 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?: BlockWaterBlend | BlendWithBlockWater: 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?: number | A point light, or a spot with cone. |
Emitter (6) | particles: AssetRef; rate?: number; burst?: number; seed?: number; to?: Vec3; color?: Color | Particles 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?: number | A positional sound on the entity. |
Nametag (8) | text: string; color?: Color | A 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?: Vec3 | Size 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?: number | For players, use players.setMovement. |
Velocity (11) | v: Vec3 | The room moves the entity; projectiles stop at your plot's boundary. |
Billboard (12) | text: string; color?: Color; size?: number | Text drawn in the scene, facing the viewer (a public board) |
Lifetime (13) | seconds: number | The room removes the entity after this long. |
Custom (14) | schema: string; bytes: Uint8Array | Plot-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: number | The 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:
| Collider | Blocks a walk | Taps, 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 model | the 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" | never | the box |
a glTF Model with no Collider | the 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 solid | that 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.