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.
- package/AGENTS.md +18 -4
- package/README.md +38 -7
- package/dist/characters/bao.d.ts +6 -0
- package/dist/characters/bao.js +1 -0
- package/dist/characters/berry.d.ts +6 -0
- package/dist/characters/berry.js +1 -0
- package/dist/characters/boo.js +1 -1
- package/dist/characters/bumble.d.ts +6 -0
- package/dist/characters/bumble.js +1 -0
- package/dist/characters/cacti.d.ts +6 -0
- package/dist/characters/cacti.js +1 -0
- package/dist/characters/cap.d.ts +6 -0
- package/dist/characters/cap.js +1 -0
- package/dist/characters/drift.d.ts +6 -0
- package/dist/characters/drift.js +1 -0
- package/dist/characters/ember.d.ts +6 -0
- package/dist/characters/ember.js +1 -0
- package/dist/characters/hop.d.ts +6 -0
- package/dist/characters/hop.js +1 -0
- package/dist/characters/index.d.ts +11 -1
- package/dist/characters/index.js +11 -11
- package/dist/characters/inky.js +1 -1
- package/dist/characters/latte.d.ts +6 -0
- package/dist/characters/latte.js +1 -0
- package/dist/characters/microduck.js +1 -1
- package/dist/characters/miso.js +1 -1
- package/dist/characters/mochi.js +1 -1
- package/dist/characters/pebble.js +1 -1
- package/dist/characters/pip.js +1 -1
- package/dist/characters/rex.js +1 -1
- package/dist/characters/swirl.d.ts +6 -0
- package/dist/characters/swirl.js +1 -0
- package/dist/characters/tad.js +1 -1
- package/dist/characters/volt.js +1 -1
- package/dist/cli.js +1888 -0
- package/dist/element.d.ts +20 -2
- package/dist/element.js +11 -11
- package/dist/index-Cm5wWKoT.d.ts +12 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.js +6 -6
- package/dist/{status-DVudJbtp.d.ts → mascot-DZ91_ss-.d.ts} +1 -10
- package/dist/react.d.ts +2 -1
- package/dist/react.js +16 -16
- package/dist/status-C14-19fT.d.ts +12 -0
- package/dist/wobnimo.js +15 -15
- package/package.json +30 -4
- package/skill/SKILL.md +719 -0
- package/skill/examples/bao.ts +73 -0
- package/skill/examples/berry.ts +97 -0
- package/skill/examples/boo.ts +87 -0
- package/skill/examples/bumble.ts +70 -0
- package/skill/examples/cacti.ts +79 -0
- package/skill/examples/cap.ts +59 -0
- package/skill/examples/drift.ts +74 -0
- package/skill/examples/ember.ts +78 -0
- package/skill/examples/hop.ts +66 -0
- package/skill/examples/inky.ts +168 -0
- package/skill/examples/latte.ts +73 -0
- package/skill/examples/microduck.ts +222 -0
- package/skill/examples/miso.ts +182 -0
- package/skill/examples/mochi.ts +78 -0
- package/skill/examples/pebble.ts +174 -0
- package/skill/examples/pip.ts +170 -0
- package/skill/examples/rex.ts +193 -0
- package/skill/examples/swirl.ts +69 -0
- package/skill/examples/tad.ts +117 -0
- package/skill/examples/volt.ts +214 -0
- package/skill/reference/recipes.md +551 -0
- package/skill/reference/spec.md +258 -0
- package/skill/templates/starter.ts +73 -0
- 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).
|