wobnimo 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/AGENTS.md +18 -4
  2. package/README.md +38 -7
  3. package/dist/characters/bao.d.ts +6 -0
  4. package/dist/characters/bao.js +1 -0
  5. package/dist/characters/berry.d.ts +6 -0
  6. package/dist/characters/berry.js +1 -0
  7. package/dist/characters/boo.js +1 -1
  8. package/dist/characters/bumble.d.ts +6 -0
  9. package/dist/characters/bumble.js +1 -0
  10. package/dist/characters/cacti.d.ts +6 -0
  11. package/dist/characters/cacti.js +1 -0
  12. package/dist/characters/cap.d.ts +6 -0
  13. package/dist/characters/cap.js +1 -0
  14. package/dist/characters/drift.d.ts +6 -0
  15. package/dist/characters/drift.js +1 -0
  16. package/dist/characters/ember.d.ts +6 -0
  17. package/dist/characters/ember.js +1 -0
  18. package/dist/characters/hop.d.ts +6 -0
  19. package/dist/characters/hop.js +1 -0
  20. package/dist/characters/index.d.ts +11 -1
  21. package/dist/characters/index.js +11 -11
  22. package/dist/characters/inky.js +1 -1
  23. package/dist/characters/latte.d.ts +6 -0
  24. package/dist/characters/latte.js +1 -0
  25. package/dist/characters/microduck.js +1 -1
  26. package/dist/characters/miso.js +1 -1
  27. package/dist/characters/mochi.js +1 -1
  28. package/dist/characters/pebble.js +1 -1
  29. package/dist/characters/pip.js +1 -1
  30. package/dist/characters/rex.js +1 -1
  31. package/dist/characters/swirl.d.ts +6 -0
  32. package/dist/characters/swirl.js +1 -0
  33. package/dist/characters/tad.js +1 -1
  34. package/dist/characters/volt.js +1 -1
  35. package/dist/cli.js +1888 -0
  36. package/dist/element.d.ts +20 -2
  37. package/dist/element.js +11 -11
  38. package/dist/index-Cm5wWKoT.d.ts +12 -0
  39. package/dist/index.d.ts +7 -2
  40. package/dist/index.js +6 -6
  41. package/dist/{status-DVudJbtp.d.ts → mascot-DZ91_ss-.d.ts} +1 -10
  42. package/dist/react.d.ts +2 -1
  43. package/dist/react.js +16 -16
  44. package/dist/status-C14-19fT.d.ts +12 -0
  45. package/dist/wobnimo.js +15 -15
  46. package/package.json +30 -4
  47. package/skill/SKILL.md +719 -0
  48. package/skill/examples/bao.ts +73 -0
  49. package/skill/examples/berry.ts +97 -0
  50. package/skill/examples/boo.ts +87 -0
  51. package/skill/examples/bumble.ts +70 -0
  52. package/skill/examples/cacti.ts +79 -0
  53. package/skill/examples/cap.ts +59 -0
  54. package/skill/examples/drift.ts +74 -0
  55. package/skill/examples/ember.ts +78 -0
  56. package/skill/examples/hop.ts +66 -0
  57. package/skill/examples/inky.ts +168 -0
  58. package/skill/examples/latte.ts +73 -0
  59. package/skill/examples/microduck.ts +222 -0
  60. package/skill/examples/miso.ts +182 -0
  61. package/skill/examples/mochi.ts +78 -0
  62. package/skill/examples/pebble.ts +174 -0
  63. package/skill/examples/pip.ts +170 -0
  64. package/skill/examples/rex.ts +193 -0
  65. package/skill/examples/swirl.ts +69 -0
  66. package/skill/examples/tad.ts +117 -0
  67. package/skill/examples/volt.ts +214 -0
  68. package/skill/reference/recipes.md +551 -0
  69. package/skill/reference/spec.md +258 -0
  70. package/skill/templates/starter.ts +73 -0
  71. package/dist/element-DRDEWSGV.d.ts +0 -8
package/skill/SKILL.md ADDED
@@ -0,0 +1,719 @@
1
+ ---
2
+ name: wobnimo-mascot
3
+ description: Design a custom animated 3D mascot for the Wobnimo engine from a short description ("a sleepy cloud wearing headphones"), check it, preview it and wire it into the app. Use whenever someone asks for a new mascot, character, avatar or assistant buddy built with wobnimo.
4
+ ---
5
+
6
+ # Designing a Wobnimo mascot
7
+
8
+ You are going to write one TypeScript file that describes a 3D character as
9
+ plain data (a `CharacterSpec`). The Wobnimo engine turns that data into a
10
+ living mascot: it breathes, blinks, looks around, hops, talks and acts out ten
11
+ states (idle, thinking, listening, speaking, working, error, surprised,
12
+ celebrating, sad, sleeping). You never write animation code. You describe the
13
+ body, and the shared rig makes it move.
14
+
15
+ You are done when **all** of these are true:
16
+
17
+ 1. `npx wobnimo check <file>` reports **no errors and no warnings**. A clean
18
+ result prints `✓ No problems found.`, or `0 errors, 0 warnings, N notes.`
19
+ when there are only notes.
20
+ 2. You looked at the picture from `npx wobnimo preview <file>` (if you can see
21
+ images) and it matches your plan.
22
+ 3. The mascot is wired into the app (section 9), if there is an app.
23
+
24
+ Follow the steps in order. Do not skip the check. The checker measures the
25
+ real 3D shape, so it catches mistakes you cannot see in the numbers, and
26
+ every message tells you the exact value to change.
27
+
28
+ ---
29
+
30
+ ## 1. The workflow
31
+
32
+ (`npx wobnimo skill` installs this same skill into both `.claude/skills/`
33
+ and `.agents/skills/`, for different agents. The copies are identical.)
34
+
35
+ ```
36
+ plan -> npx wobnimo new <id> -> edit -> npx wobnimo check -> fix -> (repeat check)
37
+ -> npx wobnimo preview -> look -> fix -> wire into the app
38
+ ```
39
+
40
+ 1. **Plan** (section 2). Write the plan as a short list before any code.
41
+ Pick the one or two closest examples (section 10) at the same time.
42
+ 2. **Start from the template**: `npx wobnimo new <id>` writes
43
+ `mascots/<id>.ts`, a small character that already passes every check.
44
+ (`<id>` is lowercase letters, digits and dashes: `sprout`, `sleepy-cloud`.)
45
+ 3. **Read one or two examples** that are closest to your idea
46
+ (section 10 lists what each one shows). Copy their techniques.
47
+ 4. **Edit the file** one feature at a time, in this order: body, head,
48
+ limbs and big features, eyes, mouth, paints, small details, bones and
49
+ acting, frame and bound.
50
+ 5. **Check**: `npx wobnimo check mascots/<id>.ts`. Fix every error, then
51
+ every warning, using the `fix:` line under each one. Run it again. Repeat
52
+ until there are no errors and no warnings (notes are fine to leave if they
53
+ are intended, such as a tail that only shows from behind). Check after
54
+ each big feature rather than once at the end: fixes are easier one at a
55
+ time.
56
+ 6. **Preview**: `npx wobnimo preview mascots/<id>.ts` writes
57
+ `.wobnimo/<id>.png` (and prints the check again): front, three-quarter,
58
+ side and back in idle, a face close-up, the nine other states and a 56px
59
+ avatar. Open the image (with your file-reading tool, if it shows images)
60
+ and compare it with your plan (section 8). **The preview is not
61
+ optional**: the check measures geometry, but only a picture shows whether
62
+ it looks like the thing. Fix what is off, then check again.
63
+ - People can watch it live with `npx wobnimo preview mascots/<id>.ts --live`.
64
+ 7. **Wire it in** (section 9) and tell the person what you made.
65
+
66
+ If `npx wobnimo` is not available, install the package first:
67
+ `npm install wobnimo` (or use `npx wobnimo@latest ...`). For `.ts` spec
68
+ files the commands need **Node 22.6 or newer**; the CLI turns on Node's
69
+ TypeScript support by itself. On older Node, save the spec as `.mjs` (the
70
+ same code without the `import type` line and the `: CharacterSpec`
71
+ annotation).
72
+
73
+ The template from `npx wobnimo new` is a placeholder chick. Replace
74
+ **everything** that describes it: the shapes, colours, `tagline`, `accent`
75
+ and the comments, so nothing of the chick is left.
76
+
77
+ ---
78
+
79
+ ## 2. Plan before you write
80
+
81
+ Answer these in a few words each. Keep the answers; you will check the
82
+ preview against them at the end.
83
+
84
+ | Question | Example: "a sleepy cloud wearing headphones" |
85
+ | --- | --- |
86
+ | Silhouette in one sentence | a wide puffy cloud, flat-ish bottom, headphones over the top |
87
+ | Body plan: one blob, head on a body, or an object with a face | one blob (the cloud is the head and body) |
88
+ | The signature feature people must recognise | the headphones: band over the top, cups on both sides |
89
+ | 2 to 3 secondary features | small puffs on the sides, a soft blush |
90
+ | Palette, 3 to 5 colours | cloud `#9db8ff`, band `#20253a`, cups `#4f7cff` |
91
+ | Material feel of each colour (section 5) | cloud: soft and fluffy; cups: glossy plastic |
92
+ | Eyes: `ball` (big, expressive, with lids) or `bead` (small, glossy, simple) | ball, half closed when idle feels sleepy |
93
+ | Mouth | a small painted mouth |
94
+ | What moves on its own: springy parts (`jiggle`), arms (`flap`), a jaw (`talk`) | nothing springy; the whole cloud floats |
95
+ | Does it float? | yes: `motion.float` 0.15 |
96
+ | How 3 or 4 states should look on this body (section 6) | sleeping: head droops; listening: leans toward one cup; celebrating: bounces |
97
+
98
+ Design rules that make a mascot look cute and premium:
99
+
100
+ - **Big head, small body.** The head is as wide as or wider than the body.
101
+ - **Eyes low and wide apart** on the face, at or a little below the
102
+ head's middle height. Big eyes read better at small sizes.
103
+ - **Small mouth**, just below the eyes (0.06 to 0.12 lower).
104
+ - **Round, soft shapes.** Use `blend` to melt parts together. Avoid sharp
105
+ boxes unless the character is a robot or an object.
106
+ - **One strong signature feature**, big enough to read at 56px. A detail
107
+ smaller than about 0.03 units disappears at avatar size.
108
+ - **3 to 5 colours.** One main colour covering most of the body, one or two
109
+ accents, dark eyes. Blush cheeks (pink paints) add warmth to almost
110
+ anything.
111
+ - **20 to 40 parts** is the sweet spot. More than 48 slows rendering.
112
+ - **Colours look lighter on screen** than the hex suggests: the studio
113
+ lighting adds highlights and a rim. Pick colours a little richer and
114
+ darker than you want them to look (golden toast is `#d9913f`, not
115
+ `#f0d080`), and avoid near-white for anything that should read as a
116
+ colour.
117
+
118
+ **Objects with a face** (a toaster, a cup, a pot, a phone): the object *is*
119
+ the head and body in one. Use a single `body` bone, put every part, the
120
+ eyes, the mouth and the anchors on it, and place the face on the front
121
+ surface at about 55 to 65% of the object's height. Keep the object chunky
122
+ and rounded (`round` 0.05 to 0.08 on boxes), add small feet or a base so it
123
+ stands, and give it one moving part on its own bone with `jiggle` (the
124
+ toast, the steam, a lid, a flower) so it feels alive. See `latte`,
125
+ `cacti` and the "Bodies that are objects" recipes.
126
+
127
+ ---
128
+
129
+ ## 3. Space, units and the most important rule
130
+
131
+ ```
132
+ +y (up)
133
+ |
134
+ | the camera is out here, looking at the character's face
135
+ | /
136
+ | +z (toward the camera, the front of the character)
137
+ | /
138
+ -x --------+-------- +x +x is to YOUR right as you look at the character
139
+ /
140
+ (floor is y = 0; the character stands on it, centred on x = 0, z = 0)
141
+ ```
142
+
143
+ - **1 unit is about the height of a small mascot.** Typical total height:
144
+ 0.7 to 1.0. The template is about 0.85 tall.
145
+ - The character **faces +z**. Eyes, mouth and anything on the face have a
146
+ positive z.
147
+ - The **left eye in the list is at negative x** (the one on your left when you
148
+ look at the picture). Always list it first.
149
+ - Angles are **radians**: 0.5 rad is about 29 degrees, 1.57 (π/2) is 90
150
+ degrees, 3.14 (π) is 180 degrees.
151
+ - Make everything **mirror symmetric**: if something is at `[-0.12, y, z]`,
152
+ its twin is at `[0.12, y, z]`.
153
+
154
+ ### THE MOST IMPORTANT RULE: positions are relative to the bone
155
+
156
+ Every `at` (in parts, eyes, face, paints and anchors) is measured **from its
157
+ bone's pivot**, not from the floor. A bone's pivot is measured from its
158
+ parent bone's pivot. To find where something really is, add up the chain:
159
+
160
+ ```
161
+ bones: body pivot [0, 0, 0] (parent: root, at the floor)
162
+ head pivot [0, 0.3, 0] (parent: body)
163
+ part: { bone: 'head', shape: 'sphere', at: [0, 0.2, 0], radius: 0.27 }
164
+
165
+ world centre of the head sphere = body pivot + head pivot + part at
166
+ = [0,0,0] + [0,0.3,0] + [0,0.2,0] = [0, 0.5, 0]
167
+ top of the head = 0.5 + 0.27 = 0.77
168
+ ```
169
+
170
+ Write the world height of each big part in a comment while you work. Most
171
+ broken first drafts come from mixing up bone space and world space: an eye
172
+ placed at the world height of the face, on a head bone whose pivot is
173
+ already 0.3 up, ends up 0.3 too high and floats in the air.
174
+
175
+ ---
176
+
177
+ ## 4. The spec, field by field
178
+
179
+ The full reference with every option is in
180
+ [reference/spec.md](reference/spec.md). This is what you need most of the
181
+ time. Start from the template; it has every section already.
182
+
183
+ ```ts
184
+ import type { CharacterSpec } from 'wobnimo';
185
+
186
+ export const sprout: CharacterSpec = {
187
+ id: 'sprout', // lowercase, unique
188
+ name: 'Sprout', // shown to people
189
+ tagline: 'One line on what makes it special',
190
+ accent: '#ff9f43', // a UI colour that goes with it
191
+ frame: { target: 0.42, radius: 0.62 }, // camera: look-at height, half the visible height
192
+ bound: { y: 0.4, radius: 0.6 }, // a sphere that contains the whole body
193
+ motion: { hop: 0.16, squash: 1.2 }, // temperament (section 6)
194
+ materials: { ... }, // named looks: skin, fur, plastic (section 5)
195
+ bones: [ ... ], // the skeleton (section 6)
196
+ parts: [ ... ], // the shapes (below)
197
+ paints: [ ... ], // colour on the surface: cheeks, bellies, spots
198
+ eyes: { ... },
199
+ face: { ... }, // the mouth (or an LED screen face)
200
+ anchors: { ... }, // where effects come from
201
+ acting: { ... }, // how each state moves the bones
202
+ fx: { ... }, // optional extra particle effects
203
+ };
204
+
205
+ export default sprout; // keep this line: the CLI loads the default export
206
+ ```
207
+
208
+ ### Parts: the shapes
209
+
210
+ Parts are drawn **in list order**. Each one is attached to a bone, has a
211
+ material, and sits at `at` in that bone's space. Optional on every part:
212
+ `rot: [rx, ry, rz]` (radians, turns the part about its own centre), `blend`
213
+ (how softly it melts into the parts **before** it; 0 is a hard edge, 0.02 to
214
+ 0.08 is a soft seam, 0.1 to 0.25 is one smooth blob), and `clip` (cuts the
215
+ part flat, see below).
216
+
217
+ | shape | fields | use it for |
218
+ | --- | --- | --- |
219
+ | `sphere` | `radius` | heads, cheeks, noses, buttons, pom-poms |
220
+ | `ellipsoid` | `radii: [x, y, z]` (half sizes) | bodies, heads, ears, feet, leaves, wings, bellies |
221
+ | `capsule` | `from: [x,y,z]`, `to: [x,y,z]`, `radius` | arms, legs, tails, antennae, handles, stems, straps |
222
+ | `roundCone` | `from`, `to`, `r1` (radius at from), `r2` (radius at to) | tapering things: horns, spikes, tails, tufts, beaks, limbs |
223
+ | `roundBox` | `half: [x, y, z]`, `round` | robots, phones, boxes, books, blocks; `round` ≤ the smallest half |
224
+ | `cylinder` | `radius`, `half` (half height along y), `round` | cups, pots, cans, coins, hats; stands upright along y; `rot: [1.5708, 0, 0]` lays it toward the camera |
225
+ | `plate` | `half: [x, y]`, `corner`, `depth` (half thickness in z) | flat panels facing the camera: screens, badges, cards |
226
+ | `frame` | `half: [x, y]`, `corner`, `width`, `depth` | a rounded rectangular ring facing the camera: bezels, picture frames |
227
+ | `hem` | `y`, `amplitude`, `waves`, `speed`, `blend` | cuts off everything before it below a wavy line: a ghost's skirt |
228
+
229
+ `from` and `to` are positions in the part's space (add `at` if you set it).
230
+
231
+ **Subtracting** (`op: 'sub'`) carves the part's shape out of the parts
232
+ **listed before it**: nostrils, mouths you can see into, grooves, the hole of
233
+ a cup. A subtraction cannot cut anything listed after it, so put it right
234
+ after the parts it should cut.
235
+
236
+ ```ts
237
+ { bone: 'head', shape: 'sphere', at: [0.05, 0.16, 0.32], radius: 0.015, op: 'sub', material: 'skin' }
238
+ ```
239
+
240
+ **Clipping** keeps only the side of a plane: `clip: { normal: [0, -1, 0] }`
241
+ keeps the top half of the part (the normal points at the side that is cut
242
+ away). Use it for a flat-bottomed pot, a half-dome mushroom cap, a beak cut in
243
+ two. `offset` moves the cut along the normal.
244
+
245
+ ### Eyes
246
+
247
+ ```ts
248
+ // ball eyes: big, with lids that blink, squint, close and droop per state
249
+ eyes: { kind: 'ball', bone: 'head', at: [[-0.11, 0.2, 0.22], [0.11, 0.2, 0.22]],
250
+ radius: 0.06, iris: '#2b1d14', irisSize: 0.85, pupilSize: 0.55, lid: 'skin' },
251
+
252
+ // bead eyes: small glossy ovals, simple and very cute
253
+ eyes: { kind: 'bead', bone: 'head', at: [[-0.1, 0.2, 0.25], [0.1, 0.2, 0.25]],
254
+ radii: [0.035, 0.05, 0.022], material: 'bead' },
255
+ ```
256
+
257
+ - Ball eyes sit **about 70% inside the head**: the centre is about
258
+ `0.7 × radius` behind the face's surface. Bead eyes sit about 30% inside.
259
+ You do not need to calculate it: put the eye roughly there and the checker
260
+ tells you the exact z.
261
+ - Ball eye radius: 0.045 to 0.07. Bead radii: about `[0.03–0.045, 0.045–0.066, 0.02–0.03]`.
262
+ - `lid` is a material name, usually the head's skin or fur.
263
+ - A bead eye needs a dark glossy material:
264
+ `bead: { color: '#1a1414', f0: 0.06, spec: 2.2, shininess: 400, rim: 0.2, gloss: 1 }`.
265
+
266
+ ### The mouth
267
+
268
+ ```ts
269
+ face: { kind: 'mouth', bone: 'head', on: ['skin'], at: [0, 0.09, 0.26],
270
+ width: 0.05, color: '#5a2a1a', tongue: '#ff7d8a' },
271
+ ```
272
+
273
+ The mouth is painted onto the surface, smiles, frowns and opens when
274
+ speaking. `at` must be **on the surface** of the face (z equal to the
275
+ surface, or up to 0.02 in front). `on` lists the materials it may be painted
276
+ on: the material of the face where the mouth is. Width 0.03 to 0.07.
277
+
278
+ Robots can use an LED screen face instead (see the `volt` example).
279
+
280
+ ### Paints: colour on the surface
281
+
282
+ ```ts
283
+ // soft round colour: cheeks, spots, a nose tip
284
+ { bone: 'head', on: ['skin'], kind: 'sphere', at: [-0.2, 0.11, 0.22], radius: 0.05,
285
+ color: '#ff8f8f', soft: 0.6, opacity: 0.8 },
286
+ // an oval seen from the front: bellies, face masks, ear insides
287
+ { bone: 'body', on: ['fur'], kind: 'ellipse', at: [0, 0.2, 0.1], radii: [0.14, 0.16],
288
+ color: '#fff2dc', soft: 0.3 },
289
+ ```
290
+
291
+ - A **sphere** paint colours the surface within `radius` of `at`. Put `at`
292
+ **on the surface** (a point on the skin), not inside the body.
293
+ - An **ellipse** paint colours the surface **in front of** `at.z` inside the
294
+ `radii` oval as seen from the front. Put `at.z` a little behind the
295
+ surface, between the body's centre and its front.
296
+ - `on` lists the materials that may be coloured. A paint never colours a
297
+ material that is not in `on`.
298
+ - Cheeks: just below and outside the eyes, radius 0.04 to 0.06, pink,
299
+ `soft: 0.6`, `opacity: 0.8`.
300
+
301
+ ### Anchors: where effects come from
302
+
303
+ ```ts
304
+ anchors: {
305
+ top: { bone: 'head', at: [0.14, 0.44, 0.05] }, // just above the head: thinking dots, zzz, sparkles
306
+ mouth: { bone: 'head', at: [0, 0.09, 0.29] }, // just in front of the mouth: speech
307
+ ear: { bone: 'head', at: [-0.27, 0.2, 0] }, // side of the head: listening
308
+ feet: { bone: 'root', at: [0, 0.02, 0.15] }, // on the floor: landing puffs
309
+ },
310
+ ```
311
+
312
+ Always give all four. `top` sits on or just above the head, a little to one
313
+ side; `mouth` a little in front of the mouth.
314
+
315
+ ### Frame and bound
316
+
317
+ - `frame` is the camera. `target` is the height it looks at (about the middle
318
+ of the character); `radius` is half the visible height. The character
319
+ should fill about 70 to 80% of the picture.
320
+ - `bound` is a sphere (centre height `y`, `radius`) that must contain the
321
+ whole body in every state, including raised arms and swinging tails. Parts
322
+ outside it are cut off.
323
+ - **Do not calculate these by hand.** Put in a rough guess (`frame: { target:
324
+ 0.45, radius: 0.65 }`, `bound: { y: 0.45, radius: 0.65 }`), run the check,
325
+ and copy the values it suggests.
326
+
327
+ ---
328
+
329
+ ## 5. Materials
330
+
331
+ Each material is a named look. Every part, paint `on`, eye `lid` and mouth
332
+ `on` refers to materials by name. Copy a preset from this table, then
333
+ change the colours to yours. Every preset is valid as written; the example
334
+ colours show the relation between the fields:
335
+
336
+ - `color`: your colour.
337
+ - `sssColor`: your colour mixed about halfway with white (`#4cb873` gives
338
+ `#a8e0b8`).
339
+ - a scale or speckle `surface.color`: your colour mixed about 40% with black
340
+ (`#78cf7f` gives `#4b9f5d`).
341
+
342
+ | feel | preset |
343
+ | --- | --- |
344
+ | soft vinyl toy (default for most mascots) | `{ color: '#ffb35c', f0: 0.04, spec: 0.8, shininess: 60, rim: 0.8, sss: 0.2, sssColor: '#ffd9a8' }` |
345
+ | glossy plastic | `{ color: '#e53935', f0: 0.06, spec: 1.6, shininess: 160, rim: 0.5, gloss: 0.8 }` |
346
+ | matte plush / felt | `{ color: '#eaa15c', f0: 0.015, spec: 0.06, shininess: 6, rim: 1.1, sheen: 0.95, gloss: 0, sss: 0.18, sssColor: '#ffcf9e' }` |
347
+ | fur | `{ color: '#f07a2a', f0: 0.03, spec: 0.4, shininess: 20, rim: 1, sheen: 0.7, surface: { kind: 'fur', scale: 100, strength: 0.3, bone: 'head' } }` |
348
+ | feathers | `{ color: '#f4a63c', f0: 0.03, spec: 0.22, shininess: 14, rim: 0.9, sheen: 0.55 }` |
349
+ | jelly / gummy | `{ color: '#3fcf9f', f0: 0.06, spec: 1.6, shininess: 140, rim: 0.7, sss: 0.55, sssColor: '#9effd9', gloss: 0.9 }` |
350
+ | wet skin (frog) | `{ color: '#5fc24e', f0: 0.07, spec: 2, shininess: 260, rim: 0.7, gloss: 0.95, sss: 0.3, sssColor: '#c6ff8f' }` |
351
+ | scaly hide (dragon, lizard) | `{ color: '#78cf7f', f0: 0.04, spec: 0.7, shininess: 40, rim: 0.7, gloss: 0.35, surface: { kind: 'scales', scale: 24, strength: 0.5, color: '#4b9f5d', bone: 'head' } }` |
352
+ | ceramic / porcelain | `{ color: '#f4efe6', f0: 0.06, spec: 1.6, shininess: 140, rim: 0.6, gloss: 0.85 }` |
353
+ | terracotta / matte clay | `{ color: '#d9774a', f0: 0.03, spec: 0.4, shininess: 25, rim: 0.4 }` |
354
+ | leaf / petal | `{ color: '#3fa34d', f0: 0.04, spec: 0.7, shininess: 50, rim: 0.6, sss: 0.25, sssColor: '#b8f0a0' }` |
355
+ | bread / pastry / dough | `{ color: '#d9944f', f0: 0.03, spec: 0.5, shininess: 30, rim: 0.5, sheen: 0.3 }` |
356
+ | painted metal | `{ color: '#c3cce3', f0: 0.07, spec: 0.8, shininess: 90, metal: 0.25, rim: 0.4, gloss: 0.5 }` |
357
+ | bare metal / chrome | `{ color: '#8f98ae', f0: 0.45, spec: 1, shininess: 120, metal: 0.85, rim: 0.3, gloss: 0.7 }` |
358
+ | glass lens / glossy black nose | `{ color: '#1d1410', f0: 0.06, spec: 1.8, shininess: 200, rim: 0.3, gloss: 0.9 }` |
359
+ | bead eyes | `{ color: '#1a1414', f0: 0.06, spec: 2.2, shininess: 400, rim: 0.2, gloss: 1 }` |
360
+ | ghost / cloud / steam (soft glow) | `{ color: '#eceaff', f0: 0.03, spec: 0.35, shininess: 20, rim: 1.2, sss: 0.55, sssColor: '#ffffff', emit: '#6f52d9', emitStrength: 0.06 }` |
361
+ | lamp that changes colour with the state | `{ color: '#ffffff', f0: 0.05, spec: 1, shininess: 120, rim: 0, glows: true, emitStrength: 1.2 }` |
362
+ | mouth interior / dark cavity | `{ color: '#3b1510', f0: 0.04, spec: 0.5, shininess: 30, rim: 0 }` |
363
+
364
+ Rules:
365
+
366
+ - **Fur, scales, speckle and ripple surfaces are fixed to one bone**
367
+ (`surface.bone`, default the root). If the same fur covers parts on two
368
+ bones that move separately (a head and a body), make **two materials**: one
369
+ with `surface.bone: 'head'` for the head parts, one for the body parts, and
370
+ mark the second `shadeOf: 'fur'` so recolouring one recolours both. See
371
+ `hop`, `ember`, `bao`.
372
+ - `shadeOf: 'other'` links a material to another one for recolouring (a wing
373
+ a little darker than the body). It does not copy any settings; give it its
374
+ full preset.
375
+ - Pick colours with enough contrast: eyes dark against the face, the
376
+ signature feature clearly different from the body.
377
+
378
+ ---
379
+
380
+ ## 6. Bones, motion and acting
381
+
382
+ ### Bones
383
+
384
+ ```ts
385
+ bones: [
386
+ { name: 'body', pivot: [0, 0, 0], drive: { ry: [['yaw', 0.3]], rz: [['roll', 0.4]] } },
387
+ { name: 'head', parent: 'body', pivot: [0, 0.3, 0],
388
+ drive: { rx: [['pitch', -0.6]], ry: [['yaw', 0.6]], rz: [['roll', 0.5]] } },
389
+ { name: 'tuft', parent: 'head', pivot: [0, 0.45, 0],
390
+ jiggle: { axis: [0, 1, 0], length: 0.12, freq: 2.4, damping: 0.12 } },
391
+ { name: 'armL', parent: 'body', pivot: [-0.22, 0.3, 0.05], rest: { rz: -0.4 }, drive: { rz: [['flap', -1]] } },
392
+ { name: 'armR', parent: 'body', pivot: [0.22, 0.3, 0.05], rest: { rz: 0.4 }, drive: { rz: [['flap', 1]] } },
393
+ ],
394
+ ```
395
+
396
+ - A bone **pivots** (rotates) around its `pivot`, which is in its parent's
397
+ space. Parents must come **before** their children in the list. The
398
+ implicit `root` bone sits on the floor and is moved by the engine (hops,
399
+ squash, floating). `parent` defaults to `root`.
400
+ - Put the pivot where the joint is: a head pivots at the neck, an arm at the
401
+ shoulder, an ear at its base, a tail where it meets the body.
402
+ - Build limbs **pointing out from the pivot**: an arm whose pivot is at the
403
+ shoulder has its parts going down and out from `[0, 0, 0]` in bone space.
404
+ - `rest`: a fixed starting rotation (a droopy ear, an arm held out).
405
+ - `drive`: the engine's live channels mapped onto rotations, as
406
+ `[channel, amount]` pairs:
407
+ - `yaw`, `pitch`, `roll`: where the character looks. Give the head
408
+ `rx: pitch -0.6 to -1, ry: yaw 0.6 to 0.8, rz: roll 0.5 to 0.7` and the
409
+ body smaller amounts, as in the template.
410
+ - `flap`: raised arms or wings. 0 at rest, 1 when celebrating. Mirror the
411
+ sign between left and right (`-1` on the left, `1` on the right, with
412
+ `rz`).
413
+ - `talk`: 0 to 1 per syllable while speaking. Drive a jaw or beak with it
414
+ (`rx: [['talk', 0.8]]`).
415
+ - `wave`: a gentle idle sway, `{ rz: [amplitude, hz, phase] }`, like
416
+ `[0.05, 0.8, 0]`.
417
+ - `jiggle`: springy follow-through for ears, antennae, tails, leaves, hair.
418
+ `axis` is the direction the bone points at rest (`[0, 1, 0]` up,
419
+ `[0, 0, -1]` backward for a tail), `length` its length, `freq` 2 to 4 Hz,
420
+ `damping` 0.1 (wobbly) to 0.3 (firm), optional `maxAngle`.
421
+
422
+ Which way a rotation turns, looking at the character from the front. The
423
+ angle swings everything on the bone around its pivot:
424
+
425
+ - `rz > 0` swings counter-clockwise: a point **above** the pivot moves to
426
+ your left (−x), a point **below** it moves to your right (+x), a point to
427
+ the **left** of it moves down.
428
+ - `rx > 0` tips the top **toward the camera** (forward, a nod down); `rx < 0`
429
+ tips it back.
430
+ - `ry > 0` turns the front toward **your right** (+x).
431
+
432
+ So, for mirrored pairs (left is the one at negative x):
433
+
434
+ | the part points... from its pivot | left one: `rz > 0` | left one: `rz < 0` | right one |
435
+ | --- | --- | --- | --- |
436
+ | up (ear, horn, antenna) | leans out | leans in | opposite sign |
437
+ | down (arm, leg) | swings in, against the body | swings out and up (a wave) | opposite sign |
438
+ | sideways, outward (wing) | droops down | lifts up | opposite sign |
439
+
440
+ `rest`, `drive`, `wave` and `acting` all add up: an acting angle is added on
441
+ top of the bone's `rest` angle, so to droop an arm that rests at `rz: -0.35`,
442
+ add a positive angle for the left arm. Check the result in the preview: if a
443
+ pose goes the wrong way, flip the sign.
444
+
445
+ ### Motion (temperament)
446
+
447
+ `motion: { float, hop, squash, snap, look, snapLook, talkSquash }`, all
448
+ optional:
449
+
450
+ - `float`: hover height. 0 stands on the floor; 0.1 to 0.25 floats (ghosts,
451
+ clouds, bees, balloons). A floating character's parts are built from the
452
+ floor up as usual; the engine lifts the whole thing.
453
+ - `hop`: hop height, 0.1 (heavy) to 0.25 (bouncy).
454
+ - `squash`: squash and stretch, 0.8 (stiff, robots, cups) to 1.9 (jelly).
455
+ - `snap`: spring speed, 0.8 (slow, dreamy) to 1.2 (quick).
456
+ - `talkSquash`: how much the body bounces while speaking, 0.3 to 1.3.
457
+
458
+ ### Acting: how each state moves this body
459
+
460
+ The engine already handles the face (lids, smile, frown, wide eyes), the
461
+ glow colour, hops, breathing and effects for every state. `acting` adds
462
+ **bone motion** on top, so the state reads on this particular body:
463
+
464
+ ```ts
465
+ acting: {
466
+ thinking: { head: { rz: 0.12 }, tuft: { rz: [0, 0.15, 1.2, 0] } },
467
+ listening: { earL: { rz: 0, rx: -0.2 }, earR: { rz: 0, rx: -0.2 } }, // ears perk up
468
+ sleeping: { earL: { rz: 1.2, rx: 0.5 }, earR: { rz: -1.4, rx: 0.5 } },
469
+ error: { tuft: { rz: [0, 0.3, 10, 0] } }, // fast shiver
470
+ celebrating: { tail: { ry: [0, 0.6, 3, 0] } }, // tail wag
471
+ sad: { earL: { rz: 1.4, rx: 0.6 }, earR: { rz: -1.4, rx: 0.6 } },// ears droop
472
+ },
473
+ ```
474
+
475
+ Each value is either a **held angle** (radians, added to the bone's rotation)
476
+ or `[base, amplitude, hz, phase, spin]`: a sway of `amplitude` at `hz` around
477
+ `base`, and optionally `spin` radians per second of continuous rotation (a
478
+ propeller: `[0, 0, 0, 0, 12]`). States: `idle`, `thinking`, `listening`,
479
+ `speaking`, `working`, `error`, `surprised`, `celebrating`, `sad`,
480
+ `sleeping`, and `happy`. `happy` is not a state an app sets: it plays when
481
+ someone clicks or taps the mascot (or the app calls `mascot.poke()`).
482
+
483
+ Good acting, in short: **perk up** for listening and surprised, **droop** for
484
+ sad and sleeping, **tilt** for thinking, **shiver** fast (8 to 12 Hz, small
485
+ amplitude) for error, **bounce or wag** for celebrating and happy. Keep held
486
+ angles under about 1.5 rad.
487
+
488
+ ### Effects
489
+
490
+ Every character already gets the shared effects (thinking dots, zzz, sparkles,
491
+ notes, a landing puff) from its anchors. Add your own only for the signature
492
+ touch: hearts from a bunny, steam from a cup, drops from a jelly:
493
+
494
+ ```ts
495
+ fx: {
496
+ happy: [{ glyph: 'heart', anchor: 'top', rate: 1.2, velocity: [0, 0.22, 0.04],
497
+ jitter: 0.1, life: [1, 1.3], size: [0.04, 0.06], color: '#ff8fb1' }],
498
+ },
499
+ ```
500
+
501
+ Glyphs: `z bubble sparkle heart note arc puff ring drop spark mote feather dot
502
+ exclaim warning confetti cloud star gear`. Details in
503
+ [reference/spec.md](reference/spec.md).
504
+
505
+ ---
506
+
507
+ ## 7. Reading the check output
508
+
509
+ ```
510
+ Sprout (sprout): 6 parts, 4 bones, 3 materials, 2.5 KB of spec
511
+ ✖ error eyes.at[0]: This eye is buried 0.028 inside the head, so it cannot be seen.
512
+ fix: Move it forward to at [-0.1, 0.15, 0.262] (the face's surface is at z 0.301 there).
513
+ ▲ warning frame: The body fills less than half the picture, so it will look small and far away.
514
+ fix: frame: { target: 0.45, radius: 0.58 }
515
+ · note bones: Bone "tail" moves nothing: no part, paint, eye, face, anchor or child bone uses it.
516
+ 1 error, 1 warning, 1 note.
517
+ ```
518
+
519
+ What the check measures: parts that are hidden, slivers, or floating free of
520
+ the body; subtractions that cut nothing; eyes (buried, floating, covered,
521
+ mirrored); the mouth; every paint; the frame and the bound in every state;
522
+ standing on the floor; anchors; unused materials and bones. What it cannot
523
+ judge: whether it looks like the thing, its proportions and colours, and
524
+ whether a pose reads. That is what the preview is for.
525
+
526
+ When the check says nothing about `frame` or `bound`, your values are fine:
527
+ keep them.
528
+
529
+ - The text after the level is **where** in the spec (`eyes.at[0]`,
530
+ `parts[12]`, `paints[3]`, `frame`).
531
+ - **`fix:` lines are exact.** Copy the numbers into the file. Do not round
532
+ them and do not "improve" them.
533
+ - Fix **errors first**, then warnings, then run the check again: one fix can
534
+ change other measurements (moving the head moves where the eyes must be).
535
+ - `parts[i]` counts from 0 in the `parts` list.
536
+ - Common messages and what they mean:
537
+ - *completely hidden* / *only a sliver shows*: the part is inside another
538
+ part. Move it outward, make it bigger, or delete it.
539
+ - *floats free of the body*: the part (or a group of parts on one bone)
540
+ does not touch the rest. Move it by the amount given, or move its
541
+ bone's pivot.
542
+ - *This subtraction cuts nothing*: it is listed before the part it should
543
+ cut, or it is not touching it. Move it after that part in the list.
544
+ - *buried* / *floats in front of the face* (eyes): use the z from `fix:`.
545
+ - *paint never touches the surface*: its `at` is inside the body or in the
546
+ air. Use the point from `fix:`.
547
+ - *lands on "x", not its on*: add that material to `on`, or move the paint.
548
+ A paint never colours materials missing from `on`, so if "x" is a part
549
+ that is meant to sit on top of the paint (a pit in the middle of a
550
+ belly patch), leaving it out of `on` is right: make the paint bigger
551
+ than that part so the colour shows around it.
552
+ - *The mouth is drawn only on surface less than 0.02 behind face.at*: set
553
+ the mouth's z to the face's surface (from `fix:`).
554
+ - *cut off* (frame) / *outside the bound*: copy the suggested `frame` or
555
+ `bound`.
556
+ - *hovers* / *dips below the floor*: move all parts up or down by the
557
+ amount given (easiest: change the y of the body bone's pivot), or set
558
+ `motion.float` if it should hover.
559
+ - Notes are information. "Not visible from the front" is fine for tails and
560
+ backs. "Material not used" or "bone moves nothing" usually means a typo.
561
+
562
+ ---
563
+
564
+ ## 8. Looking at the preview
565
+
566
+ Open `.wobnimo/<id>.png` and go through this list. Fix anything that fails,
567
+ then check again.
568
+
569
+ - [ ] From the front, it is obviously the thing that was asked for. Cover
570
+ the name: would someone guess it?
571
+ - [ ] The signature feature is clear in the front view **and** in the 56px
572
+ avatar.
573
+ - [ ] Nothing floats loose or is cut off; nothing pokes through where it
574
+ should not (side and back views).
575
+ - [ ] The face: two eyes looking forward, a mouth below them, cheeks under
576
+ the eyes. In the close-up the eyes look like eyes, not dents or
577
+ stickers.
578
+ - [ ] Colours: the palette from the plan, enough contrast, eyes readable.
579
+ - [ ] States: sleeping looks asleep, sad looks sad, celebrating looks happy,
580
+ thinking tilts or looks up. Your `acting` shows (ears, tails, arms)
581
+ and goes the way you meant (section 6 has the signs).
582
+ - [ ] Separate pieces (wings, ears, hats, accessories) sit **on** the body
583
+ in the side and back views, not beside it.
584
+ - [ ] It stands on the floor (or clearly floats, if it should).
585
+
586
+ If you cannot open images, rely on the check and re-read your plan against
587
+ the spec: every planned feature must have parts, and every part must have a
588
+ reason.
589
+
590
+ ---
591
+
592
+ ## 9. Wiring it into the app
593
+
594
+ Install: `npm install wobnimo`.
595
+
596
+ Web component (any framework, or plain HTML):
597
+
598
+ ```ts
599
+ import 'wobnimo/element';
600
+ import sprout from './mascots/sprout';
601
+
602
+ const el = document.querySelector('wobnimo-mascot')!; // <wobnimo-mascot style="width:160px;height:160px">
603
+ el.spec = sprout;
604
+ el.state = 'thinking'; // idle | thinking | listening | speaking | working | error | surprised | celebrating | sad | sleeping
605
+ ```
606
+
607
+ React (Next.js: put it in a `"use client"` file):
608
+
609
+ ```tsx
610
+ import { Mascot } from 'wobnimo/react';
611
+ import sprout from './mascots/sprout';
612
+
613
+ <Mascot character={sprout} state="thinking" style={{ width: 160, height: 160 }} />
614
+ <Mascot character={sprout} status={status} /> // drives the state from an AI SDK useChat status
615
+ ```
616
+
617
+ Plain API:
618
+
619
+ ```ts
620
+ import { mount } from 'wobnimo';
621
+ import sprout from './mascots/sprout';
622
+
623
+ const mascot = mount(document.getElementById('buddy')!, sprout, { state: 'idle' });
624
+ mascot.setState('celebrating');
625
+ ```
626
+
627
+ Put the mascot file wherever the app keeps components (`mascots/` from
628
+ `npx wobnimo new` is fine). Map the app's real events to states: waiting for
629
+ a reply is `thinking`, streaming a reply is `speaking`, a failed request is
630
+ `error`, a finished task is `celebrating`, no activity for a while is
631
+ `sleeping`.
632
+
633
+ ---
634
+
635
+ ## 10. Examples to copy from
636
+
637
+ All 20 built-in characters are in [examples/](examples/). They pass every
638
+ check. The first ten in this table were each made from the single sentence
639
+ shown, the same way you are working now. Read the closest one before you
640
+ start: a dragon or lizard is closest to `rex`, a bird to `pip` or `pebble`,
641
+ a furry animal to `ember`, `bao` or `hop`, an object to `latte` or `cacti`,
642
+ a robot or gadget to `volt` or `microduck`, a floating thing to `drift` or
643
+ `boo`.
644
+
645
+ | example | made from | read it for |
646
+ | --- | --- | --- |
647
+ | `drift` | "A sleepy cloud in brand blue, wearing headphones" | a floating blob made of overlapping spheres; an accessory (headband, ear cups) |
648
+ | `cacti` | "A tiny potted cactus with a pink flower on its head" | an object with a face; a clipped flat pot; arms; a flower from petals |
649
+ | `latte` | "A coffee cup with a foam-art heart and steam for hair" | a cylinder object; a liquid surface; a heart painted with two paints; a handle |
650
+ | `bumble` | "A round bumblebee with tiny wings" | stripes as paints; wings that buzz (`wave`); antennae with jiggle; a floater |
651
+ | `swirl` | "A snail whose shell looks like a cinnamon roll" | grooves carved with subtraction (sub then add, in order); eye stalks |
652
+ | `ember` | "A fox cub with a big fluffy tail" | fur on two bones; a big jiggling tail; pointed ears; dark paws and a muzzle |
653
+ | `cap` | "A red mushroom with white spots" | a clipped dome; spots as paints that follow a curved surface |
654
+ | `berry` | "A strawberry with a leafy hat" | many small seed parts placed on a surface; a leaf crown |
655
+ | `bao` | "A chubby panda" | colour patches as parts (eye patches, ears, arms); a round body |
656
+ | `hop` | "A white bunny with long floppy ears" | long floppy ears with jiggle and ear paints; acting with ears |
657
+ | `volt` | (hand-built) a robot | a screen face (LED matrix); glowing bulbs; boxes, plates and frames; metal |
658
+ | `pip` | (hand-built) a chick | a beak with a talking jaw; wings driven by `flap` |
659
+ | `mochi` | (hand-built) a jelly drop | one squashy blob; a spinning sprout; a custom landing splash |
660
+ | `boo` | (hand-built) a ghost | `float`; the `hem` shape; a soft inner glow |
661
+ | `inky` | (hand-built) an octopus | many waving tentacles built in a loop |
662
+ | `miso` | (hand-built) a plush cat | matte plush material; whiskers; paws that wave |
663
+ | `tad` | (hand-built) a frog | wet speckled skin; eyes on top of the head |
664
+ | `pebble` | (hand-built) a penguin | a beak and jaw; flippers; a knitted scarf (scales surface as knit) |
665
+ | `rex` | (hand-built) a dinosaur | scales; spikes down the back; nostrils by subtraction; a tail |
666
+ | `microduck` | (hand-built) a robot duck | many small mechanical parts; lens eyes; a status light that glows |
667
+
668
+ [reference/recipes.md](reference/recipes.md) has ready-made recipes for
669
+ common features: ears, arms, legs, wings, tails, horns, hats, headphones,
670
+ glasses, noses, bellies, stripes, spots and objects, including a box with a
671
+ slot and something popping out of it.
672
+
673
+ ---
674
+
675
+ ## 11. Mistakes that break mascots (check yours)
676
+
677
+ 1. **World positions on a bone.** Every `at` is relative to its bone's
678
+ pivot (section 3).
679
+ 2. **Eyes listed right first** or not mirrored. Left eye (negative x) first,
680
+ same y and z.
681
+ 3. **Eyes on the back** (negative z) or deep inside the head. Use the
682
+ check's z.
683
+ 4. **A part completely inside another part.** It cannot be seen. Make it
684
+ stick out.
685
+ 5. **A subtraction listed before what it should cut.** It cuts nothing.
686
+ 6. **A paint whose `on` does not include the material it sits on.**
687
+ 7. **A mouth floating in front of the face or buried in it.**
688
+ 8. **Fur, scale or speckle materials shared between bones that move
689
+ separately.** Make one material per bone with `shadeOf`.
690
+ 9. **Child bones listed before their parent.**
691
+ 10. **Huge rotations in acting** (over about 1.5 rad) that swing parts out
692
+ of the bound.
693
+ 11. **A floating character with `float: 0`**, or a standing one hovering.
694
+ 12. **Forgetting `export default`** at the end of the file.
695
+ 13. **Guessing `frame` and `bound`.** Copy them from the check.
696
+ 14. **Tiny details** (under 0.03) that vanish at avatar size, or more than
697
+ 48 parts.
698
+ 15. **Parts beside the body instead of on it** (wings, ears). Look at the
699
+ side and back views.
700
+ 16. **Leaving the template's tagline, colours or comments in place.**
701
+ 17. **Stopping before the check is clean, or skipping the preview.**
702
+
703
+ ---
704
+
705
+ ## 12. Final checklist
706
+
707
+ - [ ] `npx wobnimo check <file>` shows no errors and no warnings.
708
+ - [ ] The preview matches the plan (section 8), or you checked the plan
709
+ against the spec line by line.
710
+ - [ ] `id`, `name`, `tagline` and `accent` are filled in and make sense.
711
+ - [ ] All four anchors exist.
712
+ - [ ] At least four states have `acting` that suits the body.
713
+ - [ ] The file ends with `export default <name>;`.
714
+ - [ ] It is wired into the app (section 9), with the app's events mapped to
715
+ states.
716
+ - [ ] You told the person: what you made, where the file is, how to preview
717
+ it (`npx wobnimo preview <file> --live`), and how to change colours
718
+ (the `palette` option: `palette={{ skin: '#ff8fb1' }}` recolours a
719
+ material by name).