# Exhibitions in Hyperfy

import { Callout } from "zudoku/ui/Callout";
import { Stepper } from "zudoku/ui/Stepper";

Anyone can curate a museum at
[museumofcryptoart.com/rooms/world](https://museumofcryptoart.com/rooms/world)
— place 3D rooms, hang artworks, resize and arrange them. With the
[Hyperfy](https://github.com/hyperfy-xyz/hyperfy) integration, that curation
becomes a **walkable, multiplayer world**: provide a world URL and its admin
key, and the whole exhibition spawns into the Hyperfy v2 world of your choice.

<Callout type="note" title="The privacy contract" icon>
  Curation data never leaves the curator's device on its own. The world
  builder stores everything in browser localStorage; spawning — from the
  builder's dialog or the CLI — is the explicit upload moment, and the
  exhibition travels directly from the curator to the world they chose,
  never through museum servers.
</Callout>

## From curation to world

<Stepper>

1. **Curate**

   Build the exhibition in the world builder: place rooms, hang artworks,
   adjust each piece. Everything autosaves locally.

1. **Get a world**

   Any self-hosted Hyperfy v2 world works. Deploying your own takes one
   docker-compose file — the **MOCA world template** in
   `apps/hyperfy/world-template/` pins the engine version, sets sane
   defaults, and documents the whole operate/backup story. You need two
   values: the **world URL** and the **admin key** (the world's
   `ADMIN_CODE`).

1. **Spawn**

   In the builder's **Exhibits** tab hit **Spawn to Hyperfy**, paste URL +
   key, spawn. Or use the exported file with the CLI:

   ```bash
   cd apps/hyperfy && npm install
   node spawn-exhibition.mjs my-show.moca-exhibition.json \
     --url https://your-world.example.com --key YOUR_ADMIN_CODE
   ```

   No engine checkout needed — the spawner speaks Hyperfy's wire protocol
   directly. Every spawn ends with a verification pass confirming the world
   really received every room.

1. **Walk in & refine**

   Open the world in a browser — it's multiplayer, bring people. Admins
   refine in-engine (see below); curators push updates by spawning again.

</Stepper>

## Modular by design

Every placed room arrives as **its own Hyperfy app**: the room GLB plus a
generated script that hangs the curated works onto the model's `Slot_NNN`
nodes — images as planes at true aspect ratio, motion works as live spatial
video, optional title/artist placards. Artworks are **child nodes of their
room**, so a room can be grabbed, rotated, even duplicated in-engine and its
curation stays attached, always.

The builder's layout translates faithfully: it arranges rooms on uniform
tiles (every model normalized to fit one), and the spawner reproduces that —
each room is scaled so a tile spans a configurable number of meters
(**Room size**, default 16 m; `--tile-size` on the CLI), positions convert
with it, and artworks stay at their metric size inside the scaled rooms.

Spawning is **idempotent**: blueprint and entity ids derive from the
exhibition's stable id, so re-spawning updates the same rooms in place.
Curation changes flow in from the museum; the arrangement and tuning done
inside the engine survive. (`--relayout` reapplies the museum room layout;
`--fresh` spawns an independent copy.)

## Refining in-engine

Anyone holding the admin key types `/admin <code>` in the world chat, then:

- **Tab** toggles build mode.
- **Right-click a room** opens its inspector. The **App pane** holds the
  curation controls every room ships with: artwork size, wall gap, placards,
  art lighting (unlit true-color vs world-lit), video volume. Changes apply
  live for everyone.
- **P** unpins a room (rooms spawn pinned so visitors can't drag them); then
  grab to move, **1–4** for translate/rotate/scale modes, **R** duplicates,
  **X** deletes.
- **Per-slot editing, in place:** flip the room's **Slot editing** toggle,
  then hold **E** at any hung work — arrow keys nudge it along the wall
  (Shift = faster), the scroll wheel resizes, R resets, Enter finishes.
  Changes sync live to everyone in the world, are permission-checked
  server-side, and persist in the world itself — they survive server
  restarts and even curation re-spawns from the museum.
- Right-click → **Download** exports any room as a portable `.hyp` file.

Both refinement layers and the museum builder compose: museum data defines
the curation, the inspector tunes the room's mood, and slot editing
fine-places individual works — pushing a curation update from the museum
never wipes what was refined in-world.

## Host worlds

Each Hyperfy world is one lightweight container with its own database,
assets, and admin key — perfect isolation per exhibition or curator.

- Single world: `apps/hyperfy/world-template/` (deploy guide included).
- Many worlds: `apps/hyperfy/docker-compose.worlds.yml` — copy a service
  block per world, point subdomains at the ports, hand each curator their
  key. Self-hosters need nothing but Docker.

## The slot convention (build your own renderer)

Rooms from `GET /v1/rooms` ship as GLBs whose `Slot_001…Slot_NNN`
placeholder quads define where art hangs — use `model_optimized_url`, which
always carries them (un_MUSEUM sculptures get theirs generated from the
onchain slot amount; `model_url` is the untouched HQ original) — transform = anchor, bounding box = frame
size, `slots` field = count. Hide the placeholder, place the work at its
transform, fit preserving `ratio`. That single convention powers the museum
site, the Hyperfy integration, and whatever you build next.

## Agents that know Hyperfy

Run `apps/hyperfy/harvest-hyperfy-docs.mjs` to ingest the official
[docs.hyperfy.xyz](https://docs.hyperfy.xyz) content into the
[Library](/library). From then on `/v1/library/ask` — and the chat widget on
this very site — answers Hyperfy scripting and world-building questions with
citations, so curators and their agents can collaborate on richer worlds:
custom interactions, scripted openings, generative scenography.

For agent ground truth, fetch the skill:
`https://docs.museumofcryptoart.com/skills/exhibitions/SKILL.md`.
