Wish We Were Here - the guide for AI assistants. The plain-text original is llms.txt; the place itself is here.
# Wish We Were Here — for an agent
> A cosy 3D diorama sandbox: https://idyll.cc/
Wish We Were Here is a browser toy: small hand-built 3D places to begin from, which you nudge into
something of your own and send to someone as a link. Nobody needs an account or an install — the person
opens the link and the place is there. A place travels inside the URL itself, so a link can be sent
anywhere and keeps working; a short link is the optional alternative, and it is the only thing here
that is stored on a server, encrypted, for 30 days.
This file is written for an *agent*: what the app is first, then how to drive it directly.
Everything runs in the visitor's browser. It is a static site — no backend, no accounts, no cookies,
no analytics — and a scene handed to someone travels in the URL fragment (`…/#s=…`), which a browser
never sends to a server.
You can help a person in three ways. Pick the one that needs least of you.
When a person asks you to create, imagine or show a 3D scene or diorama: write the link by hand from the
rules below, and hand it over as one unbroken address. If you can render HTML — a ChatGPT Canvas, a
Claude Artifact, a page of your own — you can put the diorama in front of them instead of describing it:
<iframe src="https://idyll.cc/#txt=theme=summer_meadow&title=Blue+Hour&p=campfire@0,0,0&p=log_bench@1.2,0,1.6,45" width="100%" height="450" frameborder="0" allow="fullscreen"></iframe>
Embedding is allowed — the page carries no `frame-ancestors` policy — and the address inside the `src`
is the same link as any other, under the same rules.
1. **Write the link yourself, in plain text.** No code, no encoding, nothing computed: every character
is legal in a URL fragment as it stands, and this is the route to prefer.
https://idyll.cc/#txt=theme=summer_meadow&title=Blue+Hour&p=campfire@0,0,0&p=log_bench@1.2,0,1.6,45
`theme=` is a world id from the list below; `p=` repeats, one object each, written `type@x,y,z` or
`type@x,z` (then y is 0). It also accepts an optional yaw rotation in degrees: `type@x,y,z,yaw` (rotation
around Y, so chairs and benches face each other or the center), and optional uniform scale:
`type@x,y,z,yaw,scale`. `filter=` (postcard look), `mood=` (soundtrack), `title=`, `note=` and
`author=` are optional, with `+` or `%20` for spaces. Spaces are the only thing worth encoding, and
a `+` will do.
**A `#txt=` link carries a type, a position, a yaw and a scale, and nothing else.** There is no room
in `type@x,y,z` for a colour or a shape, so a place built this way is built from the catalogue
exactly as it stands. That is the right answer for most places, and the wrong one when the scene
needs an object nobody has — which is what route 2 is for, and why it carries a whole scene rather
than a list of names. A link can only carry what the app already knows.
**The whole address is the link, and the `#` is part of it.** Hand it over as one unbroken line of
text: the full `https://idyll.cc/`, then `#txt=`, then the scene written exactly
as the example above writes it. The `#` is what tells the browser that everything after it is the
scene rather than a path on the server — without it the browser asks for a file called `txt=…`, which
does not exist, and the person gets an error page instead of a place. Never give the fragment on its
own for someone to paste behind a domain name, and do not offer it as a labelled link: a chat window
is where addresses get wrapped, shortened or quietly cut, and this one does not survive that.
Positions take the full `x`, `y` and `z`, so objects can be stacked and shaped: this app has no
dinosaur, but a dinosaur made of boulders is a perfectly good place, and `y` is the interesting
one. It is a sculpture medium as much as a diorama.
**Prefer many small objects to a few large ones — that is advice about shaping, not about
sourcing.** A house is a house, however it is placed; forty rocks can be a dinosaur, a dragon, a
mountain range or a ship, because small pieces are what give a silhouette its outline. One
assistant built a T-Rex out of `boulder` and `small_rock` and it read as a T-Rex; an earlier
attempt reached for the biggest objects in the catalogue and produced something that looked like a
village. Density is the point when you are shaping something — and when the thing you want is not
in the catalogue at all, invent it (see the section below) and hand the place over as a reply.
2. **Hand over a reply they paste in.** Any chat, no code needed: produce
`{"ops":[{"op":"generate_scene","args":{"state":{ ... }}}]}` and the person pastes it into the app's
"Ask your AI" panel, which documents the state shape in its own prompt.
**This is also the route to use when you cannot be sure a link arrives intact** — a chat window that
wraps or shortens addresses, a person copying by hand, a channel you do not control. It carries no
address to mangle, it fits in one message, and it cannot half-arrive: either the person pastes it and
the place opens, or nothing happens and they still have the words. Prefer it over the link when the
transport between you and the person is not yours to trust.
3. **Drive the tab, if you can run script inside it.** Open
https://idyll.cc/?agent=1 and call `window.wwwhAgent` (documented below). No
install, no bridge, no account - the app skips its title screen in that mode and is ready to drive.
The local MCP bridge on `127.0.0.1:8787` offers the same vocabulary to a client that can run a
process.
**Do not invent an encoded link.** If you write a `#...=` URL, it must be built character by character
from the rules above - plain text, nothing computed. A link you cannot verify yourself will be refused by
the app, and the person is left with an error instead of a place. If you cannot write the plain-text
form, hand over a reply for the app's "Ask your AI" panel instead.
## Two routes that work
The link above opens a small place — a campfire, a bench and a pine — and it was checked against the
app before it was written here. Read it: it is the whole scene, in plain text, and nothing in it was
computed. Copy its shape.
https://idyll.cc/#txt=theme=summer_meadow&title=A+Small+Example&p=campfire@0,0,0&p=log_bench@1.2,0,1.6&p=pine_tree@-3,0,2
That link says: world `summer_meadow`, title `A Small Example`, and three objects —
`campfire` at 0,0,0, `log_bench` at 1.2,0,1.6, `pine_tree` at -3,0,2. The same place as JSON, which is
what the second route carries:
{
"theme": "summer_meadow",
"meta": { "title": "A Small Example" },
"props": [
{ "type": "campfire", "position": [0, 0, 0] },
{ "type": "log_bench", "position": [1.2, 0, 1.6] },
{ "type": "pine_tree", "position": [-3, 0, 2] }
]
}
## Worlds and objects, so you do not have to guess
None of this needs script. These are the ids and type names this build has; a client that can run
script gets more detail from `wwwhAgent.themes()` and `wwwhAgent.catalogue()`, but nothing here
requires it.
Worlds, for `theme`:
summer_meadow japanese christmas beach city space pumpkin moon fireplace aquarium greenhouse orrery workshop starlight_hill sunset_cliff highland_cascade countryside_road medieval_stronghold stargate farm ember_woods summer_porch lakeside_picnic reading_nook ski_chalet treehouse balcony_garden boathouse bakery_kitchen hot_spring allotment lantern_festival nomad_camp torchlit_ruins observatory midsummer_night diwali_courtyard bayou_night village_forge crystal_grotto vardo_camp
Object types, for `type`:
meadow_pine meadow_flowers meadow_boulder meadow_path_stones meadow_tent meadow_campfire meadow_log_bench meadow_bike meadow_eagle meadow_clouds meadow_fireflies meadow_grass meadow_path_pebbles meadow_stone_moss jp_pond_edge jp_gravel_patch jp_pond jp_pond_rocks jp_bamboo_fence jp_stepping_stones jp_sakura jp_red_bridge jp_stone_lantern jp_torii jp_koi jp_crane jp_petals jp_fireflies xmas_stars xmas_woods_ring xmas_pond xmas_wall xmas_pebble_path xmas_path_edge xmas_garland xmas_light_strand xmas_fir xmas_gazebo xmas_fire_pit xmas_bench xmas_bird xmas_hare xmas_fireflies beach_sand beach_water beach_pebbles beach_palm beach_lounge_chair beach_umbrella beach_jetty beach_boat beach_crab beach_seagull city_sidewalk city_skyscraper city_wall city_tree city_streetlamp city_cafe_table city_bike_rack city_kiosk city_dust space_stars space_planet space_asteroids space_station space_solar_wing space_comms_dish space_cargo_pod space_shuttle space_plasma pumpkin_back_wall pumpkin_ground pumpkin_moss pumpkin_crescent_moon pumpkin_stars pumpkin_cabin pumpkin_framing_tree pumpkin_jack pumpkin_ghost pumpkin_leaves moon_ground moon_craters moon_stars moon_earth moon_lander moon_rover moon_flag moon_experiment moon_astronaut moon_dust hearth_fireplace hearth_flames hearth_room_shell hearth_window hearth_rug hearth_armchair hearth_side_table hearth_wood_basket hearth_cat hearth_dust aquarium_back_wall aquarium_seabed aquarium_shipwreck aquarium_chest aquarium_coral aquarium_kelp aquarium_fish aquarium_bubbles greenhouse_back_wall greenhouse_ground greenhouse_structure greenhouse_potting_bench greenhouse_potted_plant greenhouse_hanging_pot greenhouse_wicker_chair greenhouse_bluebird greenhouse_rain greenhouse_pollen orrery_stars orrery_sun orrery_orbit_ring orrery_planet orrery_asteroid_belt workshop_floor workshop_walls workshop_car_lift workshop_light_fixture workshop_sports_car workshop_tool_chest workshop_tire_rack workshop_engine_hoist workshop_sparks workshop_dust hill_ground hill_stars hill_moon hill_peaks hill_tree hill_bench hill_bush hill_rock hill_fireflies hill_shooting_star cliff_sky cliff_sea cliff_sand cliff_plateau cliff_island cliff_beam cliff_bench cliff_bush cliff_rocks cliff_beach_rocks cliff_gull cascade_massif cascade_rocks cascade_plateau cascade_bed cascade_river cascade_backfall cascade_fall cascade_lagoon cascade_bench_cliff cascade_pond_rocks cascade_bench cascade_pine cascade_bush cascade_tufts cascade_butterfly cascade_mist lane_ground lane_hill lane_road lane_barn lane_fence lane_tree lane_bush lane_bird castle_sea castle_island castle_path castle_keep castle_cloud castle_pine castle_barrel castle_bird mesh_spec pine_tree snow_pine oak_tree sakura_tree palm_tree bamboo bush boulder small_rock wildflowers grass_tuft fallen_log reeds tent gazebo log_bench bench campfire lantern street_lamp tiki_torch string_lights fairy_lights stepping_stones stone_path fence stone_lantern torii_gate red_bridge pond koi_pond ocean lily_pad fountain rowboat road_bike bicycle camper_van boat building neon_sign traffic_cone cafe_set mailbox fire_hydrant bus_stop planter_box asteroid station_core solar_array coms_dish cargo_pod shuttle planet gas_giant space_dust eagle robin hare deer koi crane crab fireflies petals snow dust clouds farm_barn farm_silo farm_hay_bale farm_chicken farm_scarecrow candle_cluster glass_candle reading_lamp pendant_lamp fire_bowl wood_stove wall_lamp portable_lantern taper_candles tea_for_two coffee_for_two coffee_mug loose_cushion folded_blanket tea_table armchair reading_chair hammock cat dog book_stack bookshelf area_rug potted_plant picnic_basket wreath wishing_well wooden_footbridge porch_swing rocking_chair porch_deck porch_railing watering_can flower_box lemonade_jug wind_chime welcome_mat screen_door picnic_blanket camp_chair lake_shore fruit_bowl lunch_plate picnic_table canoe kite fishing_rod cool_box bunting cabin_room window_seat wingback_chair writing_desk knitting_basket record_player framed_picture log_pile kettle footstool chalet_front chalet_fire_pit sheepskin_bench ski_rack sledge snowman snowball_lantern snowshoes snow_drift treehouse rope_ladder tire_swing hammock_chair bird_box paper_lanterns wooden_crate telescope pulley_bucket balcony_terrace bistro_chair bistro_table herb_pots tomato_plant rose_trellis macrame_planter bird_bath garden_gnome boathouse river_dock sailing_dinghy oar_rack creel_stack rope_coil life_ring_post deck_chair bollard kitchen_room bread_oven kitchen_table ladderback_chair dresser flour_sacks pie_stand copper_pans drying_herbs onsen_pool bathhouse onsen_bench steam_wisps bath_bucket towel_rack sake_tray bamboo_spout wagasa_umbrella allotment_plot garden_shed veg_patch bean_poles compost_bin wheelbarrow sunflowers water_butt allotment_bench garden_tools star_canopy lampion_string sky_lanterns floating_candles lantern_gate festival_stall lampion_post lantern_river festival_bench taiko_drum bedouin_tent brass_fire_pit majlis_cushions brass_tea_table iron_torch pierced_lantern resting_camel clay_jars desert_dunes ruined_temple broken_column stone_brazier wall_torch carved_bench fallen_statue ivy_arch offering_candles ruin_paving observatory_dome armillary_sphere star_chart_table astronomer_chair celestial_globe oil_lamp_post constellation big_moon observatory_hill midsummer_bonfire maypole silver_birch jar_lantern_line feast_trestle plank_bench flower_crown fiddlers_stool haveli_facade diya_steps diya_ring rangoli marigold_garland brass_hanging_lantern jhoola_swing sweets_thali tulsi_planter courtyard_tiles stilt_shack bald_cypress bayou_water pirogue piling_lantern cypress_bench boil_pot heron cattails smithy forge_hearth anvil forge_sparks quench_barrel smith_tool_rack grindstone smith_bench cobbled_yard cavern_walls giant_crystal glow_crystals stalagmites glow_pool lantern_niche glow_mushrooms moss_bench glowworms vardo_wagon tripod_fire piebald_horse lantern_crook wagon_awning painted_bench washing_line flower_buckets
A type that is not in that list is dropped, with a note, and the rest of the state still opens. The
*parameters* a type accepts are not listed here because they live in the app's builders - they are what
`catalogue()` reports.
## Inventing an object the app has not got
The list above is what the app ships with, not the limit of what a place can hold. When a scene needs
something no type expresses — a clothesline, a wheelbarrow, a lamp post, a particular animal — invent
it: a prop whose `type` is `mesh_spec`, described out of primitives.
**It travels in a reply, not in a link.** `p=type@x,y,z` has no room for a shape, so a `mesh_spec`
named in a `#txt=` link arrives as nothing in particular. Hand the place over as route 2 and the
object arrives whole.
Here is one, as a whole answer — the deepest nesting you will ever write, so copy the shape of it
exactly, closing braces and all. It is one line:
{"ops":[{"op":"generate_scene","args":{"state":{"meta":{"title":"The Yard"},"props":[{"type":"mesh_spec","position":[2,0,1],"params":{"spec":{"version":1,"parts":[{"primitive":{"kind":"box","width":3,"height":0.04,"depth":0.04},"material":{"color":10033947}}]}}}]}}}]}
`parts` is a list of primitives — `box`, `cylinder`, `cone`, `sphere`, `torus`, `dodeca`, `icosa`,
`plane`, `prism` (a triangular wedge, apex up and ridge along depth, which is a roof with no rotation at
all) and `extrude` (`points` — three to forty-eight `[x, y]` pairs, or a flat list of coordinates —
pushed out by `depth`, with an optional `bevel`; for blades, wings, shields, signs, arches) — each with
its own dimensions and a `material`; a dozen of them make a convincing object. A part may also carry a
`transform` to place it within the object: `position`, `scale`, and **`rotation` in radians or
`rotationDeg` in degrees** (`[pitch, yaw, roll]`, and degrees are the better choice — `rotationDeg` with
any value past a full turn is read as degrees too, so `[0, 180, 0]` means half a turn whatever you
call it). Cylinders, cones and prisms stand upright along Y; planes face +Z; four fins or legs are the
same part at `rotationDeg` `[0,0,0]`, `[0,90,0]`, `[0,180,0]`, `[0,270,0]`. The spec may name one
`animation` (`spin`, `bob`, `pulse`) that the app applies itself. An explicit mesh — `vertices` and
`indices` — is there for the rare shape primitives cannot make.
**Style & Era Rule**: a catalogue prop has a fixed visual identity — `building` is a modern concrete
skyscraper with lit office windows, `tent` is a modern nylon dome, `neon_sign` is cyberpunk. When that
identity does not match the place's era or material — medieval, ancient, fantasy, pastoral, sci-fi —
**do not use the mismatched catalogue prop**. Compose an authentic object out of `mesh_spec` primitives
instead, which is what the section above is for.
**Stay inside the budgets, or the object is not built at all**: at most **24 parts** and **12 distinct
materials** — and a material differs by every field, so giving each rock its own shade of grey counts
as a material each time. Reuse one colour for a family of rocks. (One colour too many is folded onto
the nearest one already in use rather than losing the object, but the geometry budget still refuses
it.)
**Invent where it earns its place.** An invented object costs a few hundred characters where a
catalogue one costs a few dozen, so it is the wrong tool for a rock — and the right one for the thing
nobody has, which is what makes a place feel made for the person who asked for it.
## 1. Drive a tab you control — `?agent=1`
If you can run script in a browser page (you are a browser agent, a devtools session, an extension),
open the app with the flag appended:
https://idyll.cc/?agent=1
That installs `window.wwwhAgent`. Nothing listens and nothing dials out — this is for a tab you
already control, and the flag is the whole of the permission. Without it the object does not exist.
The flag is a mode rather than a decoration: **there is no title screen** (the world is already open
when the tab finishes loading, camera and all), and the app's own two "Ask your AI" buttons — the pill
in the corner and the Wizard's — are disabled and read *Agent driving*, since a prompt to carry to some
other assistant has no purpose in a tab you are already driving. Sound starts on the first real
gesture, because a page nobody has clicked is not allowed to make noise.
wwwhAgent.version // op protocol version
wwwhAgent.ops // every op, with the argument shape it takes
wwwhAgent.apply(op, args) // run one
wwwhAgent.catalogue() // { type: { label, category } } — what add_prop may place
wwwhAgent.themes() // [{ id, label }] — what set_theme may name
wwwhAgent.moods() // [{ id, label, feel }] — what set_music may name
wwwhAgent.docs // 'llms.txt'
Look before you edit:
wwwhAgent.apply('get_scene', {}) // the whole scene state
wwwhAgent.apply('describe', {}) // the theme, and the looks this scene offers
Then edit:
wwwhAgent.apply('add_prop', { prop: { type: 'log_bench', position: [0, 0, 2] } })
wwwhAgent.apply('update_prop', { id: 'log_bench_1', patch: { rotation: [0, 0.6, 0] } })
wwwhAgent.apply('set_filter', { filter: 'custom2' }) // a look describe() named
wwwhAgent.apply('set_music', { mood: 'vast' })
wwwhAgent.apply('set_environment', { patch: { sky: { color: 0x2b3a55 } } }) // the world around the objects
`set_environment` changes the sky and its haze, the light, the ground, or the look's own colours —
the op for "the sky is brown", "darker", "warmer". `patch.sky.color` is the sky, and the app keeps
the sky and the haze it fades into one colour, so one colour is all you send. A *look* that carries a
`sky` of its own wins over the stage while that look is chosen: the app moves the look's copy with the
stage's and lists what it moved under `alsoMoved`.
Every call goes through the same command layer as the app's own buttons, so an edit is an ordinary
edit: undoable with the app's own Undo, autosaved into the visitor's browser, and validated. A bad
type, an unknown id, or an edit declaring a stale revision is refused by throwing an `Error` carrying
a `code` and one plain sentence to act on.
The ops, in full — `wwwhAgent.ops` carries the argument shape of each:
get_scene describe update_prop remove_prop add_prop clear_scene
set_filter set_music set_title set_environment set_theme generate_scene
Every call records an undo step and autosaves, so prefer one big change over a hundred small ones:
`generate_scene` for a whole place, and one op per intention.
## 2. Hand a scene over, with no tab to drive
If you cannot run script in the page, write the scene and let a person carry it.
- **A whole place.** Send exactly one JSON object:
`{"ops":[{"op":"generate_scene","args":{"state":{ … }}}]}` and the person pastes it into the app's
AI panel ("Ask your AI" while creating, or the pill in the corner while editing). It is applied as
one undo step. That panel also writes the prompt to hand *you*: it contains the current scene, the
reply format, the prop catalogue, and the ranges everything is clamped to.
- **A link.** A scene is data — compressed into a share link. A person can send one; whoever holds it
can open that place. Two shapes exist, and the second is opt-in: an ordinary link carries the whole
scene in the URL **fragment** (`…/#s=…`), which no server ever receives; a **short link**
(`…/#p=<id>.<key>`) is made only when a person presses *Make a short link*, and then the scene is
sealed with AES-GCM in the browser and the ciphertext is kept for 30 days by the small endpoint at
`/w/` on this site. The key travels in the fragment, so the store cannot read what it holds, and the
sender's name and note are never uploaded at all. If that endpoint refuses — down, rate-limited,
gone — the app falls back to the full fragment link, which needs no server.
- **A link is also a picture.** Besides the scene, a link may name the **print the sender was looking
at it through** — the medium menu and the aged-paper switch — in **one character**, in front of the
payload's magic: `…#s=2w2…`. It is the medium's index plus 16 for aged paper, in base36, over the
frozen alphabet `modern, crt, watercolor, canvas, drawing, vhs, ascii, cel, pure, halftone, lcd` — so `0`
is a plain modern print, `1` the 90s TV, `2` watercolour, `3` oil on canvas, `4` a pencil drawing, `5` a
VHS tape, `6` ASCII art, `7` cel shading, `8` a pure print (the look's colours, none of its effects),
`9` halftone pop art, `a` a handheld LCD, and `h` is the app's own print (the 90s TV) on aged paper.
(A medium absent from the menu is still nameable in a link and still settable with `?medium=` — the menu
is a shop window, not the vocabulary, and four prints are off it: the ASCII, the watercolour, the oil and
the halftone. A code is never reused or renumbered, so an old link always opens on the print it was sent
on.)
A **short link** carries none of
it in the URL at all: there it is sealed with
the scene in the store, which cannot read it and which costs the link nothing, and which also means it
is the one part of a short link nobody can edit on the way. None of it is part of the scene: a place
never carries a medium, no reply can set one, `medium` and `postcardWear` are not fields a `state` may
have, and a link says nothing at all when the sender was on the app's own print with the paper plain.
A viewer's own print is what they come back to.
## The shape of a scene
{
"meta": {"title": "<this place>", "subtitle": "<the world it belongs to>"},
"terrain": {"color": <decimal>, "size": <metres>},
"sky": {"color": <decimal>, "fog": {"color": <decimal>, "density": <0.004–0.03>},
"horizon": {"kind": "<none|hills|peaks|dunes|treeline|city|custom>"}},
"lighting": {"ambient": {"color": <decimal>, "intensity": <0–2>},
"sun": {"color": <decimal>, "intensity": <0–3>, "position": [x, y, z]}},
"camera": {"position": [x, y, z], "target": [x, y, z]},
"filter": "custom",
"grades": [{"label": "<two or three words>", "bloom": <0–2>, "exposure": <0.4–2>,
"colorTint": [r, g, b], "saturation": <0–2>, "contrast": <0.5–2>,
"vignetteAmount": <0–1>, "sepiaAmount": <0–1>, "grainAmount": <0–1>,
"warmth": <-1–1>}],
"music": {"mood": "<an id from moods()>"},
"props": [{"type": "<a type from catalogue()>", "position": [x, y, z]}]
}
- Colours are one whole number in decimal — red × 65536 + green × 256 + blue — never hex.
- The ground is the app's own flat plane, which `terrain` colours. Water, shores, floors and back walls
are *props*, placed on that ground; water sits just above it (y 0.02–0.2). A distant ridge is either a
prop the world already places (`hill_peaks`, `cascade_massif`, `lane_hill`) or the place's own
`sky.horizon` — the next line but one.
- `sky.horizon` is the skyline, and it is the one part of the stage whose subject is a **shape** rather
than a colour. Seven words: `none` (open water, a plain, a void), `hills` (soft low hills), `peaks`
(snow-capped mountains), `dunes`, `treeline` (a close line of conifers), `city` (a blocky skyline with
lit windows), and `custom`. `{"kind":"peaks"}` is a whole horizon; `color`, `distance`, `height` as
[min, max], `count` and `snow` are yours on top of it. It is placed *for* you: the app stands the
skyline beyond whatever the place already has and grows it until it clears the trees in front of it,
so `distance` is rarely worth sending. **Leave `horizon` out and the place keeps the range belonging
to whichever world it landed on** — which is why two places with nothing to do with each other can
come out in front of the same mountains — so a place with a country of its own should name one.
**`custom` invents a skyline**: it takes `parts`, the same mesh-spec vocabulary as an invented object,
repeated around the ring with `repeat` (1–16), `distance` and `scale`. One silhouette unit, authored
standing on the ground and facing +Z; the app turns each copy to face the middle of the place. A
whole spec looks like this, inside the scene's `sky`:
{"kind":"custom","repeat":8,"scale":2.5,"parts":[
{"primitive":{"kind":"cone","radius":2,"height":9,"radialSegments":5},
"material":{"color":2241092}},
{"primitive":{"kind":"box","width":3,"height":1,"depth":3},"material":{"color":1118481}}]}
It travels in a reply, like every other invented thing here — a `#txt=` link has no room for it.
- `grades` are a place's own postcard looks: up to five, the first being the one it opens with.
`filter` selects one of them, or a world's own grade.
- `theme` is optional, and is only a substrate — a palette a prop falls back on, the soundtrack, and
the baseline a share link is differenced against. A place that states its own ground, sky, light and
camera needs no world, and its prompt names none.
- `lighting` is one ambient and one sun, and that is the whole of it. Every other light belongs to an
object: placing `campfire`, `lantern`, `street_lamp`, `tiki_torch`, `stone_lantern` or `neon_sign`
gives that object a real point light, so what stands near it is lit by it (the eight lights nearest
the camera are the live ones, and the types that flicker already flicker). `string_lights` and
`fairy_lights` only glow — an `unlit` part reads as lit without lighting anything.
- Anything left out is filled from the world the scene lands on, and every number is clamped rather
than refused — except the two names: `meta.title` is this place, and `meta.subtitle` is the world it
belongs to, two or three words for a kind of country ("Frozen Lake Country"). The subtitle is the
line under the title, the words in the share message and the postcard caption, so write one; a place
that names no world is shown as "Somewhere quiet".
- `position` is `[x, y, z]` in metres, y up, ground at y 0; about 20×20 metres around the middle is a
place, and 12–30 objects reads as somewhere real.
## What this is not
- **No remote interface.** There is no endpoint to POST to, and nothing on the deployed site listens.
A scene lives in a browser tab, so an agent needs either a tab it controls (above) or a person to
carry the data. The app's MCP bridge is a process on the machine that has the project, bound to
`127.0.0.1`, and it is deliberately not reachable from the web — do not look for it here.
- **Nothing is stored on the server**: no accounts, no sessions, no cookies, no analytics. Drafts live
in the visitor's own browser, and a shared place is private to whoever holds the link.
- **A person is watching.** Ask before changing a scene someone is looking at, prefer one clear edit
over many, and leave their arrangement recognisable.