pixel-sway

Verified@siosan/pixel-sway
Use it in Cohub

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

Use the skill: pixel-sway. Run `npx -y @neta-art/neta-search-cli@latest skills use @siosan/pixel-sway --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 @siosan/pixel-sway
Summary

Turn your world's trees, or any picture clipped into a hedge, into pixel plants you can shake in the wind.

  • Pick up to three plants from your world, or give a picture of a character, pet or logo and get it back as a clipped hedge or tree.
  • Play it as a web page: switch between Calm, Breeze and Storm, tap, drag branches and swipe to shake.
  • In a storm the leaves, petals or snow let go and pile up on the ground, then grow back into shape with one button.
  • Optionally get a game-ready pack: transparent images, seamless breeze and storm loops, GIFs and notes for Godot, Unity and Phaser.
  • Works best with trees that have a trunk of a different colour from the crown, or a picture with a clear outline on a plain background.
SKILL.md
# Pixel sway

Pixel plants with a body: a generated pixel picture is snapped to its true grid, split into wood, tone layers and
leaf pieces, and handed to one shared wind. Trees bend from the trunk up and drop their leaves; topiaries keep their
clipped shape, lose their outer leaves in a gale and grow back into it. The creator ends up with a page they can
play with and a sprite pack a game can use.

Read this file fully before generating anything or writing files. Resolve relative paths from this skill's
directory; below, `$SK` is that directory (what `skills use` printed) and every command runs from `/workspace`.

## Language

Reply in the user's language, whatever language this file is in. This holds for every message of the run, above
all the final handover; translate what the scripts print, don't paste it. Text the user sees in the deliverable —
Work title, subtitle, weather names, what falls, the regrow button, page labels — follows the user's language: set
`lang` in the config to the user's language and write `title`, `subtitle` and `labels` in it; the page takes every
other word from its locale dictionary. Names the user typed stay as they wrote them.

## When to use / when not

Two routes:

- **flora** — the world's own plants (or plants the user names) swaying, shaking, shedding.
- **topiary** — a hedge (`bush`) or a shape on a short trunk (`tree`) clipped into the outline of a picture the
  user gives or points at.

Not for: moving characters, hair or capes (this skill does plants only); a frame-by-frame animation; a still
illustration; a video. Say this skill makes touchable pixel plants and topiaries, and stop.

## Inputs

| Item | Required | Counts as given when |
|---|---|---|
| Route | yes | Trees / bushes / leaves / the world's plants → `flora`. "Clip / shape / topiary / hedge like this picture", or a picture with a request to make it a plant → `topiary`. |
| Picture (topiary) | yes | An upload, a material named in the request (its cover), a Work's image, or a world file path. "Something cute" with no picture does not count. |
| Which plants (flora) | no — default: from the world | The user names them; otherwise read the world and pick up to three that belong to it. |
| Form (topiary) | no — default `bush` | "tree / on a trunk / 造型树 / 树" → `tree`; "hedge / bush / 灌木" → `bush`. A head or face alone reads best as `tree`. |
| Look | no — default: the world's colours and mood | Always pixel art; the world's `visualStyle` gives only colour mood and lighting. |
| Sprite pack | no — default off | The user mentions game assets, sprites, a sprite sheet, an engine, 素材包 or 精灵图. |

If a required item is missing, ask once, in one short message, as `references/questionnaire.md` says, then stop. If
every required item is given, ask nothing and do not wait for a go-ahead: build through to delivery, and say in one
line which defaults you took.

## Read the world (no generation before this)

- flora: `world/WORLD.md` (frontmatter + body) and `node /mods/neta/neta atom list` → plants, gardens, seasons,
  weather words, what this world would scatter in a storm (petals, ash, snow, paper charms, sparks, spores). Pick
  plants whose trunk or stem differs in colour from the crown (blossom, maple, oak, willow, mushroom tree, a
  flowering shrub). Bamboo, cactus or grass can only sway as a whole — only when the user asks, and say so.
- topiary: find the picture. A material's cover is the first `media` entry, relative to the material file
  (`world/characters/X.md` + `../media/studio/characters/X.png` → `world/media/studio/characters/X.png`). Look at it
  once. If it holds several subjects (a sheet, a grid), set `crop` to the one panel to use. Write `what` — one
  English sentence naming the shape and the outline details that make it recognisable (ears, tail, horns, hat).

## Config

Write `$RUN/config.json` before generating (`$RUN` = `_runs/pixel-sway/<YYYYMMDD-HHMM>/`; if the user or the
environment asks for outputs elsewhere, use that folder as `$RUN`; never move or rename it afterwards).

flora:

```json
{
  "route": "flora",
  "lang": "<user's language code: zh, en, ja, ko, es, fr, de, pt, ru>",
  "title": "<Work title in the user's language>",
  "subtitle": "<one line in the user's language, optional>",
  "style": "<colour mood and light from the world, English, optional — never a non-pixel medium>",
  "plants": [ { "name": "<name in the user's language>", "look": "<English: what it is, crown shape, colours, what it grows>" } ],
  "labels": { "calm": "<world's word>", "breeze": "<world's word>", "storm": "<world's word>", "fall": "<what falls>", "regrow": "<button>" },
  "bg": "#f3f2ee"
}
```

topiary — same fields, with `topiary` instead of `plants`:

```json
"topiary": [ { "name": "<in the user's language>", "what": "<English, the shape and its outline details>",
               "source": "<world path of the picture>", "crop": [x, y, width, height], "form": "bush",
               "foliage": "green" } ]
```

- 1–3 plants or topiaries; three read best for flora, one to three for topiary. `crop` only when needed.
- `foliage` (topiary): `green` (boxwood, default), `blossom` (cherry blossom), `autumn` (red-orange maple), or a
  short English phrase of your own. The user's words pick it: 樱花 / blossom → `blossom`, 秋天 / 枫叶 / autumn →
  `autumn`. The same picture may appear several times with different foliage.
- What falls follows the plant: `petal` (small, light, swirling), `leaf` (green, longer), `maple` (pointed,
  tumbling, shows its underside). It is read from the crown colour; set `"leaf"` on a plant or topiary entry only
  to override (e.g. white snow-blossom read as petals is right; a dark red-leaved tree you want as leaves → `leaf`).
- `labels`: in the user's language and the world's voice; `fall` is a bare noun ("花瓣", "embers"); omit any you
  have no better word for. `bg`: a light neutral by default.
- flora only: `plantsSource` (top level, with `plants` naming what is in it) for a pixel picture the user already
  has — nothing is generated for it.

## Steps — one command

```bash
node "$SK/scripts/sway.mjs" make --run _runs/pixel-sway/<stamp> --work works/webs/<slug> [--pack]
```

It runs everything — check, generate (1 image; topiary draws the shape guide first), pixel cleanup, layering, the
Work folder, page build, checks, publish — and prints one JSON line. Typically 80–120 s, almost all of it the
image. Do not run the stages one by one and do not open the images it makes; read its output.

- `<slug>`: kebab-case from the title in Latin letters (`pixel-sway-<subject>`); reuse it on a rerun.
- `--pack`: only when the user wants game assets (sprites, sprite sheet, Godot / Unity / Phaser, 素材包, 精灵图) —
  it adds `assets.zip` (transparent PNGs, seamless breeze / storm loops, GIFs, layer data). Without it the Work is
  just the page, with nothing else to hand over.
- `"status": "working"` → the picture is still being generated: run the same command again (nothing is paid twice).
- `"status": "published"` → hand over (Deliver). Read from it: `model` / `fellBackFrom` (name the model in one line
  when it fell back), each plant's `leaf` kind (check it fits the plant) and `shapeMatch` (topiary), `genNotes`,
  `pixelWarn`, `qaFailedNonBlocking` (name each in one line).
- `"ok": false` → read `error`: a generation that did not succeed (tell the user the cause and that failed tries
  cost nothing; stop — never resubmit by hand); the page failing its checks twice (tell the user what failed;
  stop); `world_slug_required` (the world has no public address yet: ask the user once whether you may set one,
  propose a kebab-case slug from the world's name, stop; on their yes run
  `node /mods/neta/neta world slug set <slug>` and the same `make` command again — never set it unasked).
- A tree read as a bush (its `notes` say `no clear trunk` but it is a tree): run
  `node "$SK/scripts/sway.mjs" rig --run <same> --debug`, look once at `rig/flora-<n>-check.png` (panels: ground,
  wood, each tone layer, everything), add `"trunkColors": {"<n>": [<palette indices>]}` to the config if the trunk
  is visibly another colour, and run `make` again (once).

## Deliver

One **web Work** (the playable page); with `--pack`, a sprite pack inside it (`assets.zip`, linked from the page).
The generated source picture also stays in the world as an image Work (the generator always records one).

Then, as the last step — the run is not delivered until it is done:

1. **Hand over with cards**, through the `interaction-components` skill, each on its own line: the page as
   `{"type": "work", "workId": "<workId from make's output>"}` (fallback: `cohubWorkId` in
   `works/webs/<slug>/meta.json`; never a made-up id); only when `pack` is set, the pack as
   `{"type": "world-file", "path": "<pack>"}`. Then two or three lines in the user's language: what stands in the
   wind and how to play (tap, drag, swipe, the weather switch, the regrow button); with a pack, that it holds
   transparent PNGs and seamless breeze / storm loops for a game. For topiary, give each
   shape's match with its picture in plain words. Name in one plain line what `qa` checked and anything it could
   not check.

## Out of scope

- Do not change world settings (WORLD.md config, visual style, slug) except the slug on the user's explicit yes
  as above; do not delete files that existed before the run.
- Do not modify memory, character cards or other materials, installed scripts, existing Works or roleplays; the
  world is read, never written, except the new Work and the run folder.
- Make only this Work: no extra images, videos, takes or variants; do not act on your own next-step suggestions.
- Do not edit this skill's files, write your own pixelizing, layering or physics code, or call `neta generate`
  directly — `sway.mjs` is the pipeline (it holds the prompt rules that make the picture layer cleanly).
- This version animates plants only — no characters, hair or cloth — and does not share counters between
  visitors (the shake counter is kept on each device).

## When to stop

- Topiary with no picture: ask once (see Inputs), stop.
- Asked for something this does not make (see When not): say what it makes, stop.
- `gen` blocked or failed: report the cause and that failed tries cost nothing, stop. Never resubmit by hand.

## Gotchas

- Generated "pixel art" is never grid-true (cells of 5.3 px, blurred edges, hundreds of colours). `pixel` finds
  the real cell size from where the colour edges fall; skipping it makes every moving pixel shimmer.
- Motion quality comes from the runtime, not from tuning in the run: plants shift by whole pixels with a hold
  threshold (no per-pixel rounding), sway by shear (no rotation), and their crowns are split into tone layers that
  move slightly more and later toward the highlights. A first version rotated a skeleton and rounded each pixel:
  the crown shimmered and tore open. Never patch `sway-core.js` in a run.
- The first real flora run drew crowns as big star-shaped leaf stamps on small trees (≈125 px). The prompt now asks
  for large plants and crowns of tiny 2–6-pixel clusters; the model's own grid (≈4–5.3 screen px) caps plants near
  160–220 px. Don't ask for finer than that.
- Topiary works because of the shape guide `gen` draws: the picture's outline filled with three greens that follow
  its own light and dark, so eyes and mouth come back as carved hollows. Sent as the first reference, it gave 87–93%
  outline match on the first real run. A picture with a busy background yields no clean outline — the model then
  shapes it from the picture alone, and the match is lower.
- Human figures work as topiary (a full-body standing picture, `form: bush`): the hair, cape and limbs are part of
  the hedge mass, so nothing moves on its own — the first humanoid run matched 79–81% and stayed legible in a gale.
  This is the way to put a character in the wind with this skill.
- The model sometimes paints "transparency" as a purple checker texture instead of real alpha. `pixel` still removes
  it (key from the border plus border-connected flood), but the pixel grid is then not found and the sheet is
  resampled to plant height — slightly softer cells. No action needed.
- A topiary is stiffer than a tree on purpose (it must stay legible in a gale): smaller sway, tone layers moving
  almost together. Its outer leaves still go first in a storm, and regrow restores the clipped shape.
- The ground patch under each plant is kept as a still layer: below a trunk's narrowest row, or — for a hedge
  figure on its feet — the thin band at the bottom that is clearly wider than the row above it.
- Fallen leaves lie on that patch as on a plane seen from above, each at its own depth between its top and bottom
  rows. A first version laid every leaf on one row just above the patches — between plants that was a dotted line in
  the air. Leaves blown off a patch lie on the same plane but fade within a second or two.
- Every falling leaf used to be the same 2×2 block, which read as debris. Each plant now drops its own kind (petal,
  leaf, maple) in its crown's mid tones; the darkest core colours never fall.
- Bushes and trees are told apart by the trunk's waist: the narrowest row under the crown. Under 30% of the crown's
  width is a tree whatever the colours; up to 42% is a tree when the bark differs from the crown. The first rule looked
  at the bottom rows only: old oaks with flared roots and dark trees whose bark shares the crown's shadow colour were
  read as bushes, and their trunks then blew away as leaves (gate run 456e55a, 4 of 12 trees). A bark colour the crown
  also uses counts as trunk only below the crown, so the crown's dark core still sheds.
- Bushes and topiaries without visible bark get brown bark added and a branch frame grown from every foot of their
  base, so a figure stripped by a storm shows woody stems — before, the hidden branches were drawn in the darkest
  green and an all-green humanoid hedge had no visible wood at all.
- Wind: the top of a plant leans by H·(0.035·F + 0.07·F²) (F ≈ 0.2–0.35 in a breeze, ≈ 0.4–1.4 in a storm), rows bend
  almost linearly from the base, and the crown core moves with the wood. A storm keeps a 160-px tree bent ~12 px with
  gusts to ~20 px, trunk included; the first versions topped out near 5% of the height and the trunk barely moved.
- Withdrawn: a `character` / `both` route (hair, cape and ribbons on soft chains) was tried and dropped — layered
  hair read as strange however it was tuned. Do not rig characters with this skill.
- One `make` command replaced eight separate stage commands: the stages take ~15 s together, but stopping between
  them made a run several minutes long. The pack and the inspection images (pixel preview, layer panels, page
  screenshots) used to be made every run — 20–40 files and 4 MB nobody had asked for; now they are opt-in
  (`--pack`, `--debug`).
- The pack is built in local temp space and shipped as one zip: dozens of small writes into the world's file
  system stalled a build for minutes. Do not unpack it into the Work folder.