mini-world

Officialneta-search/mini-world
Use it in Cohub

Paste this into your agent's chat — it fetches the skill and follows it.

Use the skill: mini-world. Run `npx -y @neta-art/neta-search-cli@latest skills use neta-search/mini-world --via site` and follow what it prints. Read its complete output, redirecting it to a temporary file first if necessary, and resolve relative paths from the skill directory it prints.
Use in Neta Studio Open it in Neta Studio and see what people made with it.
Install from a terminal
npx -y @neta-art/neta-search-cli@latest skills use neta-search/mini-world
Summary

A tiny planet your characters can explore, zoomed from globe to walkable ground with forests, deserts and oceans.

  • Zoom from a whole little planet down to walking on its surface, with forest, desert and ocean areas named the way you asked.
  • Walk, swim automatically in the ocean, and drag objects from a model library onto the ground to decorate the world.
  • Collect hidden wonders scattered around the planet into a discovery book as you explore.
  • Day turns to night and weather shifts between sunny, cloudy, rainy and snowy, changing the sky, fog and light.
  • Anyone can open and play it right away with no login; works best for worlds with a cosy, adventurous cast of characters and places.
SKILL.md
# Mini World

A Mini World is a **small spherical planet you can hold and walk on**. Zoomed out
it reads as a globe; zoomed in it becomes walkable surface with distinct regions
(forest, desert, ocean). Movement, camera, placement, weather, and asset
generation all live on the sphere — nothing is flat.

This skill is the **production contract AND a runnable template**. The verified
runtime lives in `templates/runtime/`; scripts live in `scripts/`; the rules that
must hold on every build are below.

Resolve every relative path from this skill directory. Nothing here depends on a
particular Space, account, credential, or artwork — copy the template into a new
`works/web/<slug>/` and re-point the Cohub work id (see `references/cohub-runtime.md`).

## Contract

### When to pick it
The user wants a child-friendly spherical 3D exploration world — a small planet you zoom from globe to surface, with distinct regions, automatic swimming, placeable objects, a generated-component library, time & weather and discovery collecting — or wants to add / fix camera, facing, placement, weather or generated-component features in one.

### Stop when the pick is wrong
A flat (non-spherical) scene, a non-exploration web page, or a generic 3D asset-authoring job is not this skill. A world that must log in to browse / play / place violates the guest-first contract — this skill never does that.

### No intake questionnaire — build straight from the request

This skill **does not ask an opening round**, however open the request is ("make a little planet for
this world" is enough). Do not run the `questionnaire` skill and do not ask setup questions in chat
before building: take the world's own characters and places, the regions the request names (else
the template's), the default
tone (cosy discovery), the default time & weather, and decide the rest yourself. Name those choices in
one clause when you hand over the Work — the creator changes them afterwards, on a world that already
plays. The only stops are the hard ones under "Stop when input is missing".

### Input
Required: none beyond the intent — the verified runtime lives in `templates/runtime/` and is copied into a new `works/web/<slug>/`. Nothing here is a reason to ask (see "No intake questionnaire").
Optional: a roster of placeable objects, Blender-authored asset packs, time / weather defaults.

### Transform
(no opening questions) → copy `templates/runtime/` → set the Cohub work id in `cohub-runtime.js` → build / extend per the non-negotiable rules (sphere math, input interaction, asset architecture, guest-first auth, persistence, time & weather) → run `scripts/verify.mjs` on the Work folder (deterministic + Playwright suites, report saved in the Work) → publish the static folder.

### Deliver
A browser-playable static Work published to a Cohub public share, opened from the Work page so `space.id` is available for generation; a changelog entry for every published version; the URL, folder, verification result and known limitations; the Work itself handed over as an `interaction-components` work card (see Output / delivery).

### Explicitly not doing
Any login (or Cohub request) for opening, playing, browsing the library, placing objects, changing weather or local saves; a fixed part-count on generated components; making terrain / ocean / wonder-beacon placeable; treating `localStorage` as a remote save; shipping a GLB without a primitive / procedural fallback.

### Stop when input is missing
No browser / Playwright available for the required suites. Generation access denied with no guest fallback path (guest play must keep working and the error must be recoverable). A template copy without a re-pointed Cohub work id.

## Cases

### should
「做一个可缩放、能游泳、能放置模型的小星球探索世界。」
Expect: this skill — no opening questions; the spherical runtime, guest-first, deterministic + browser suites run, published with a changelog entry, the defaults it chose named in the handoff.

### shouldNot
「做一个平面的 2D 地图页面。」
Expect: not this skill — flat, not a spherical exploration world.

### missing
"Publish this world." (no browser / Playwright to run the required suites)
Expect: stop; the deterministic and Playwright suites must pass before delivery.

### theme
「只要森林和雪原两个区域,天气默认下雪。」
Expect: same skill — regions on the sphere plus a weather default; guest-first still holds.

## Start here

1. Copy `templates/runtime/` into `works/web/<slug>/`.
2. Set the Cohub work id / broker origin in `cohub-runtime.js`.
3. Build / extend per the rules below.
4. Run the self-check with this skill's script on the Work folder — see
   "Verification" for the exact command, pass mark and report file.
5. Publish the static folder; keep a changelog entry per published version.
6. Hand over the Work card (see "Output / delivery") — next-steps options do not
   replace it.

## Core experience (non-negotiable)

- The world is a **sphere** (`R = 24`): `world-math.js` gives `terrain()`, `walk()`,
  `tangent()`, `transport()`, continents, `PLACES`, and 12 `WONDERS`.
- Moving walks along the surface via `walk(normal, direction, distance)`; the
  explorer's "up" is always the local surface normal.
- Zoom spans **whole planet ⇄ ground**. The camera **anchors on the explorer's
  current surface point** — moving the explorer and then zooming must keep the
  explorer as the focus (no drift).
- **Swimming is automatic** when the explorer enters ocean terrain.
- **Named regions are the whole region list.** When the request names regions, the
  region nav (`.destination` buttons), `labels` and region heading list exactly those,
  in that number, each labelled with the request's own wording in full — in the
  visible button caption and heading, not a shorter canon name and not only in
  `title`/`aria-label`. Give each its own land kind in `world-math.js` (continents, `PLACES`,
  adding or retiring kinds to match the count). The ocean stays as unnamed,
  swimmable base water: unless the request names a water region, delete its nav button
  and its `labels` key — never invent a name for it or add it as an extra region.
- Collecting wonders fills a persistent discovery book.

## Input interaction contract (these were real bugs — do not regress)

- **Mouse drag**: zoomed OUT → orbit the planet; zoomed IN → steer the explorer.
  Wheel / pinch zoom always stays anchored on the explorer.
- **WASD / arrows** follow the explorer's current facing.
- **Facing**: forward moves face forward, back faces backward, left/right and all
  four diagonals are consistent; joystick and tap-to-walk also face the travel
  direction. (`tests/facing-browser.mjs` asserts this.)
- **Space ALWAYS jumps**, even when a HUD button (sound, zoom, map, back, pause,
  changelog, delete) currently has focus, and it must NOT activate that button.
  Intercept both `keydown` and `keyup`. Long-press must not repeat-jump. A focused
  text input still types spaces normally.
- Touch: joystick, drag-to-look, pinch-zoom, tap-to-walk, large child-friendly
  action buttons.
- **Mobile header**: on narrow viewports the top action buttons must wrap onto
  their own row and **never overlap** each other or the zoom rail. (The shipped
  build had `#weather` absolutely positioned on top of `#components`; the
  `components-browser` suite catches it by asserting the button is clickable.)

## Asset architecture

- A catalog with **stable model ids** (`model-catalog.js`, 25 built-ins) and
  reusable prototypes; the explorer and environment are registered once.
- **Placeable objects** are separate instances, aligned to the surface (upright,
  base at local ground `y=0`), persisted with a stable id + surface normal,
  selectable, and removable. Global terrain / ocean / wonder-beacon are NOT
  placeable and must throw `/cannot be placed/`.
- **Generated components** (`generated-components.js`) are validated before they
  enter the catalog: whitelisted primitives (`ball cone cylinder box rock gem
  ring`), hex colors, bounded position/scale/rotation, and a **1 MB** total payload
  cap. **Do not impose a fixed part-count** — let the model choose complexity for
  the request.
- Generated components land in the catalog, are searchable, export to Three.js
  JSON, and can be dragged onto the planet.

## Auth contract (guest-first — the most important rule)

- **Opening, playing, browsing the asset library, placing objects, changing
  weather, and local saves must NEVER trigger login or any Cohub request.**
- Auth is obtained **only** when the user submits a generated component; that is
  exactly **one** authorization flow (`generation-access.js` / `auth-gate.js`).
- If the viewer denies, keep guest play fully working and show a recoverable
  error on the generation form. Never re-prompt from a timer, page load,
  asset-library open, weather change, placement, or normal gameplay.
- Guest state is cached in `localStorage` and must not claim to be saved remotely.

## Persistence

- `world-persistence.js` writes `.neta/mini-world/state.json` in the Cohub World:
  generated component definitions, placements, and climate.
- Startup prefers the world file; `localStorage` is an **offline fallback** only.
- Remote writes happen only when generation access is active; 401/403 revokes it.
- Caps: 20 components, 60 placements.

## Time & weather

- `climate.js` / `weather.js`: a flowing clock (morning → day → dusk → night) and
  modes **Auto, Sunny, Cloudy, Rainy, Snowy**.
- Weather drives sky color, fog color/distance, sun color/brightness, ambient
  light, sea tint, clouds, and rain/snow particles.
- **Particles follow the explorer's current surface normal**, not the world
  origin. Weather fog must NOT hide the planet when zoomed out.
- "Save to world" persists `clockHours`, `weatherSelection`, `timeRunning`.

## Blender asset packs (high-complexity authored assets)

When a world needs detail beyond primitives, author it in Blender:

1. Create `blender/build_assets.py` in the Work source; keep a reproducible
   `.blend` source plus a `.glb` game asset under `blender/out/`.
2. Run it headless through this skill's launcher (handles the sandbox's missing
   `libSM.so.6` / `libICE.so.6` and Blender's `4.5/scripts` tree without root):
   ```bash
   cd <work>/blender
   <skill>/scripts/blender-headless.sh --python build_assets.py
   ```
3. Named objects/materials, clean transforms, applied scale, origin at the asset
   base, reasonable mesh density; export glTF 2.0, Y-up, selected objects only.
4. Copy GLB into the Work's `assets/` and load with Three.js `GLTFLoader`.
5. **Always keep a primitive/procedural fallback** if the GLB fails to load.
   See `references/blender-pipeline.md`.

## Verification (must pass before delivery)

**The self-check is this skill's script, run on the Work folder you will publish** —
not a hand-rolled loop over the suites, not a copy in `/tmp`:

```bash
cd works/web/<slug> && mkdir -p qa && set -o pipefail && \
  node <skill>/scripts/verify.mjs . --serve --regions "<name 1>|<name 2>|…" 2>&1 | tee qa/verify-report.txt
```

`--regions` takes the region names exactly as the request wrote them (omit it only
when the request names none); it fails unless the region nav has exactly that many
buttons and each name appears in full in one button's visible text.

- **Pass** = exit code 0 and the report ends with `N/N checks passed` and
  `mini-world verification OK`. Anything else is not verified: fix the build (or
  update the Work's own `tests/` where you deliberately renamed regions, counts or
  labels) and re-run the same command until it passes.
- If `playwright` does not resolve from the Work folder, link a `node_modules` that
  has it before running, and remove the link before publishing.
- Re-run it after the last code change before the final publish; the report at
  `works/web/<slug>/qa/verify-report.txt` is what you cite as the verification result.

What the suites cover — deterministic (no browser):
- `node --test templates/runtime/tests/*.test.mjs` — land regions stay distinct and
  swimmable water exists; every wonder sits in its named region; walking preserves the
  sphere; weather modes/clock resolve correctly; 22 placeable types + forbidden
  ids throw; models sit upright with base at ground without mutating prototypes.

Browser (Playwright):
- **Guest path**: open, browse library, place + delete, save weather, reload —
  **zero** `api.cohub` / `gateway.cohub` / `work-auth` requests, zero popups.
- **Auth path**: submitting generation triggers context + authorize, and
  **nothing more**; denial keeps play working and the button re-enabled.
- Camera: orbit out / steer in / wheel zoom anchored to explorer.
- Facing consistency across regions and diagonals; space-jump focus regression.
- Modal layouts at desktop + 3 mobile widths; **no console errors**.

## Output / delivery

- A browser-playable static Work published to a Cohub public share, opened from
  the Work page so `space.id` is available for generation.
- A changelog entry for **every** published version.
- Report URL, folder, verification result, and known limitations plainly.
- **Hand the Work over as a card, not only in prose.** In the final reply — before any
  next-steps options, which never stand in for it — load the
  `interaction-components` skill and put one `link` card on its own line pointing at
  the published Work itself: `"target": { "type": "work", "workId": "<cohubWorkId>" }`,
  where `<cohubWorkId>` is the UUID from this turn's `neta work publish web <work-dir>`
  result (`data.cohubRegisterTask.data.cohubWorkId`), or — if that field is not echoed —
  the `cohubWorkId` written into `<work-dir>/meta.json` by that publish. **The slug /
  folder name is not a Work id** and opens nothing. Set the card's `fallback` to the
  Work's name plus `data.previewUrl` from that same result. Never link the Work as a
  `world-file` (`index.html`) or as an `external-url`, and never invent the id — if
  publish did not return `ok: true` and a `cohubWorkId`, it is not delivered yet. If you
  published more than one folder, the card points at the one you are handing over.