capsule-gacha-machine

Community@rain/capsule-gacha-machine
Community skill — not reviewed. It was shared by caramel pudding and has not been checked by Neta. Read SKILL.md below before letting an agent use it.
Use it in Cohub

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

Use the skill: capsule-gacha-machine. Run `npx -y @neta-art/neta-search-cli@latest skills use @rain/capsule-gacha-machine --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.
Install from a terminal
npx -y @neta-art/neta-search-cli@latest skills use @rain/capsule-gacha-machine
Summary

Turn your prize art into a capsule-toy machine that people can shake, open and collect.

  • You get a small web page: a gacha machine to play, plus a warehouse page showing everything won so far.
  • Each pull shakes the machine, drops a capsule, and splits it open to reveal one prize with its name.
  • The warehouse groups prizes by type, shows a counter like "7 / 15 collected", and keeps unwon prizes as silhouettes.
  • Tilt your phone and the machine leans with it; on devices without tilt it follows your cursor. Music is optional.
  • Works best with a set of prize images and names, such as merch for a character. No machine art is needed, one is built for you.
SKILL.md
# Capsule gacha machine

One loop, three beats: shake the machine, open the capsule, keep the prize. The reward is the reveal, so the machine
must feel heavy and the capsule must feel sealed until it is not.

## What the user gets

A published web Work with two pages: the machine and the warehouse. Prizes are the user's images; progress ("7 / 15
collected") persists in the browser.

## Inputs — and when to ask

| Item | Required | Counts as given when | Default |
|---|---|---|---|
| Prize images + names | yes | Files or a folder are given | Ask once, then stop |
| Prize categories | no | Named | Group by the file names |
| Machine image | no | Given | Build one in CSS/3D from primitives |
| Music | no | Given | None; the page stays quiet |

Ask once for the prize set; everything else has a default.

## Build order

1. **Prize pool.** One array: `{id, name, type, image, claimed:false}`. Keep the user's file names as ids so a later
   replacement maps cleanly. Downscale every prize image to ≤ 800 px on the long edge, WebP, ≤ 120 KB each.
2. **Machine.** Either a supplied illustration or a 3D build: a glass sphere, a metal base, a chute, a coin slot, a
   crank. If 3D, a single ring of capsules inside is enough — nobody sees the back.
3. **Idle state.** The machine breathes (scale 1.00 → 1.012, 3 s ease-in-out) and the capsules very slowly churn.
   Music, if any, starts only after the first interaction.
4. **Pull sequence.**
   - Lock input; shake the machine for **1750 ms** (rotation ±6°, 8 cycles, plus a sparkle layer of 12–18 points
     around the glass);
   - play the drop, the capsule rolls into the chute (450 ms), then a short pause (250 ms);
   - open the capsule: two halves separate and the prize rises with a 3D float (rotate ±8° on Y, bob 6 px), scale
     from 0.85 to 1 over 600 ms;
   - show the prize name and a single "keep it" button.
   Do not pick the prize until the shake ends — picking first tempts an early reveal if the animation is interrupted.
5. **Claim.** Mark claimed, remove it from the available pool, update the counter, return to the machine. When the pool
   is empty, the button reads as finished and is disabled — do not re-enable by accident on reload.
6. **Warehouse.** A second page: grid of claimed prizes, grouped by type, with a count badge on the entry button.
   Unclaimed slots stay as silhouettes so the user sees what is missing.
7. **Tilt.** On devices with `deviceorientation`, map beta/gamma to ±25° rotation of the machine and a small parallax
   of the foreground. On iOS 13+, request permission from a visible button on first use; if refused, fall back to
   pointer-based tilt (the machine follows the cursor by ±8°).
8. **Persistence.** `localStorage`: claimed ids + a schema version. Migrate by keeping unknown ids instead of wiping.
9. **Publish** with `neta work init web`, then `neta work publish web`.

## Fallbacks

- No machine art: build with CSS gradients and one radial highlight; it reads fine at phone size.
- No tilt permission: pointer parallax, as above.
- Autoplay blocked: first tap starts music; a mute button always reflects the real state.
- A prize image fails to load: show a labelled placeholder with the prize name, not a broken icon.

## Gotchas

- Do not use `Math.random()` per frame to pick a prize; pick once per pull and keep it until claimed.
- If the user reloads mid-animation, the pool must not lose a prize that was never shown.
- Tilt must be clamped; an unclamped beta flips the machine upside down when the phone lies flat.
- The sparkle layer is DOM or canvas — do not spawn hundreds of elements per pull.

## Verify

`node scripts/verify.mjs <work-dir>` checks: entry, no remote assets, the prize array, the shake duration, the claim
handler, the warehouse view, the tilt clamp and the storage key.

Then pull five times, claim each, reload and confirm the warehouse and the remaining pool, test with tilt refused.
Report what you ran.

## Deliver

Publish the Work, put its card on the Board, and hand it over with an interaction-components card whose target is the
Work — the id comes from the publish output.

## Out of scope

No real money, no shipping, no prize odds displayed as a promise, no world-setting changes, no deleting files.

## Language

Reply in the user's language, whatever language this file is in — every message, the final handover included. Button,
counter and warehouse labels follow the user's language through a locale map; prize names stay exactly as the user
wrote them.