Writing a .scene · celworld docs

Writing a .scene

Name the models you want, lay them out by structure, and give them something to do.

A .scene script composes library models and built-in generators into a living diorama. You name what you want, the compiler places it by its real footprint, and life lines keep it moving. The quickest start is the skeleton:

bun run cel scene new <slug> --template <t> [--seed N] [--size N] [--mood day|dusk|night|dark] [--title "…"]

It writes a file that checks clean as written: one area or room, six people who wander it, a light when the mood is night or dark, a close view, and a commented example of every verb sized to the template. Scene scripts live in packages/engine/src/scene/scripts/<slug>.scene.

Header, templates and moods

scene <name> "<Title>" template=<t> seed=<n> [size=<radius m>] [mood=day|dusk|night|dark]

Units are metres; +x is right, +z is toward the default camera, y is up; face 0 looks at +z. The ground is a disc of radius size round the origin; on retro, interior and dungeon it is a square.

TemplateSizeGroundNotes
town36grass disc, trees ring the edgestreets, houses, a square
forest22a clearing in a thick wood
beach30sand; the sea covers the back thirdboats float on the sea
arena24sand floor in grass, stones and trees
plaza24a paved square in a park ring
tabletop10a round toy base you orbit, no fog
retro30square grass walled in by lollipop treesan old handheld-game town
carnival34a dirt circle; mood nightneeds light lines
mountain40a valley floor ringed by snowy peaks
interior24a square floor plate, cut awayrooms, halls, storeys
dungeon30a dark stone plate; mood darklit only by light lines

mood replaces the template's light, sky and fog: day, dusk, night or dark.

Assets

use <alias> = <ref> [seed=<n> | seeds=<a>..<b>] [scale=<k> | height=<m> | width=<m>] [float]
  • ref is a library slug (the latest version), slug@v (pinned), or a built-in: nature/<kind>[:<form>], prop/<kind>[:<form>], building/<kind>[:<form>], person/<archetype> or person/any.
  • seeds=1..6: each placement draws one of those variants. Each seed is built once and shared.
  • height= and width= scale the model to that size, whatever it was built at.
  • float lets it stand in water (lilies, boats, ducks) and skip floor checks.
  • bun run cel catalogue lists every built-in with its sizes and forms: nature tree bush hedge rock flower tuft; props from bench and lamp to busstop and vehicle; buildings cottage townhouse shop cafe civic tower; people kid chibi average lanky stocky round athletic elder.

Ground

area <name> circle <x> <z> r=<r>  |  area <name> rect <x0> <z0> <x1> <z1>  |  area <name> ring <x> <z> r=<r0>..<r1>
plaza <name> <x> <z> r=<r> [paved|dirt|sand|grass|flowers]
ground <area> <grass|flowers|sand|dirt|water|paved|tallgrass|stone|snow>
street <name> <x z> <x z> … [width=4]
path <name> <x z> <x z> … [width=2] [dirt|paved|sand|water|…|none] [loop]

Every ground surface is textured procedurally: grass tufts, dirt speckles, paving joints, plank seams. You never ship an image file.

Layout

The compiler places things by their real footprints. Never compute many coordinates by hand.

lots <street> <list> [side=left|right|both] [gap=1.5] [setback=1] [from=0..1] [to=0..1]
place <alias> at <x> <z> [face <deg>|face <x> <z>]
row <list> from <x z> to <x z> [gap=<m>] [count=<n>] [face …]
grid <list> at <x z> <cols>x<rows> [gap=1]
ring <list> at <x z> r=<r> [count=<n>] [face in|out|<deg>]
scatter <list> in <area> [count=<n>] [spacing=1]
cluster <list> at <x z> [r=3] [count=<n>] [spacing=0.3]
along <path> <list> [every=6] [side=left|right|both] [offset=0.6]

<list> is alias, alias*3 or a weighted mix house*3,shop. Any layout line can end with seed=<n>, as <group> (a name life lines can target) and lift=<m>. A scene holds at most 1,500 placements.

Life

do <target> <state> [speed=<k>]
do <target> sit at <thing>
do <target> stand at <thing>
ride <target> on <thing>
cycle <target> <state>[,<state>] every <a>..<b>s [for <s>s]
wander <target> in <area> [speed=0.8] [pause=1..4s] [moving=<state>]
wander <target> on <thing> [speed=0.8] [pause=1..4s] [moving=<state>]
follow <target> <path> [speed=1] [pingpong] [moving=<state>]
face <target> <x> <z> | face <target> camera | face <target> <other target>

Seats, saddles, stand spots and decks come from a model's spot lines or a built-in prop's seats. Each rider takes the nearest free spot and stays attached as the host animates: teacup riders spin with the cup, carousel riders rise and fall with the horse.

1use desk = desk_set
2use folk = person/any seeds=1..40
3grid desk at 12 5 3x1 gap=0.3 face 180
4place folk at 10.3 4.3 as deskers
5do deskers sit at desk
6ring kid,folk at 14.5 8 r=2.4 count=6 face in as cuppers
7ride cuppers on cups
8wander dockers on dock speed=0.45

speed= on do scales that state's clock, from -10 to 10: do wheel spin speed=2, do cups spin speed=-1. A mover plays the first state it has from walk run hop move waddle trot scurry crawl fly swim; a crab whose gait is called scuttle needs moving=scuttle.

Lights, camera and views

light at <x> <z> [y=2.4] [color=<swatch|#hex>] [radius=6] [power=1] [flicker]
light on <alias|group> [y=<m above its top>] [color=…] [radius=…] [power=…] [flicker]
camera orbit <distance> height <h> [at <x> <z>] [spin=<deg/s>] [pitch=<deg>] [fov=<deg>]
view <name> at <x> <z> [y=1.6] dist=<m> height=<m> [yaw=<deg>] [hide above <n>]

At most 16 lights after light on expands. overview is the default view and its name is reserved. hide above <n> on a view shows storeys 0 to n only: a cutaway of the ground floor under the upper storeys. Fog scales with the camera's distance and lens, so a far view stays as clear as the overview.

Interiors

level <n> [y=<floor top m>]
room <name> rect <x0> <z0> <x1> <z1> [floor=plain|checker|tiles|planks|flagstone|carpet] [wall=plain|panel|stone|brick|rail|none]
hall <name> <x> <z> <x> <z> … width=<m> [room options]
door <x> <z> [width=1.1] [height=2.2]
window <x> <z> [width=1.4] [sill=0.9] [head=2.3]
stairs <x> <z> to <level> face <deg> [width=1.2]
against <room> <alias> side=n|s|e|w [gap=0.05] [from=<m>] [to=<m>] [count=<n>]

Rooms are areas, so scatter … in <room> and wander … in <room> work. Rooms that share an edge share one wall. A door snaps to the nearest wall within half a metre; keep 1.2 m clear on both sides.

Checks and budgets

bun run cel scene check <file> lays the scene out against the real footprints of the latest library versions. Errors name the line and a code (unknown-asset, overlap, in-wall, blocks-path, blocks-door, in-water, too-many-lights), and placement errors end with the nearest legal spot.

MeasureWhere to read itHealthy
Errorscel scene check0
Placementscel scene checka few hundred; 1,500 is the cap
Draw callscel shot scene stats100 to 500 for a showcase scene
Dynamic placementscel shot scene statsas few as the scene needs
Lightscel shot scene stats16 or fewer
fpscel shot scene stats55 to 60 in the headless shot

Tricks from the showcase scenes

  • Bridges over paths. Give the bridge model deck spots and place it over the path or water; walkers cross on its deck.
  • Standing things on things. lift= raises a placement onto a ledge, an outcrop or a planter.
  • Groups move together. Three copies of one follow loop started a little apart keep a party together; two at equal speed and a fixed gap make a chase.
  • `wander` needs room. In an area that is mostly built over, use follow loops instead.
  • Patch models beat crowds of tiny live ones. One grass patch instead of six animated tufts took a town from 183 dynamic placements to 92.
  • Upper floors hide lower back rooms from above: add a view with hide above <n>.

Every scene on this site shows its full script: open one and press Script in the dock.