pixelkiln 0.90.0 → 0.91.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.
@@ -252,14 +252,44 @@ character from it, with no template to pick. `frames` takes PixelLab's own
252
252
  `skeleton-v3` loop. Measured live on a v3 character: 8 frames at 64px billed
253
253
  exactly 3 generations for one direction, about 15 seconds before the jobs
254
254
  start. `billed` in the lock records the real amount.
255
- - **Directions do not share one motion yet.** PixelLab draws every direction
256
- of a request from one 3D motion, and `animation_group_id` appends a
257
- direction to an existing animation so it moves the same way. pixelkiln
258
- makes one request per direction asset (including each direction of
259
- `directions`), so each direction's motion is written from the sentence
260
- separately and may differ a little. Appending to a recorded group is not
261
- wired up; the lock keeps each direction's `animationGroupId` for when it is.
262
- Verified live is a single south direction only.
255
+ - **Directions share the first direction's motion.** PixelLab draws every
256
+ direction of an animation from one 3D motion, and `animation_group_id`
257
+ adds a direction to an existing animation so it moves the same way. The
258
+ first direction listed in `directions` leads and is submitted as a new
259
+ animation; every later direction follows in the next wave and is sent into
260
+ the leader's group (with the group's own name as `animation_name`, since
261
+ PixelLab does not inherit it). Each direction is still its own asset and
262
+ request, billed separately; mirrors flip a direction and join nothing. The
263
+ expansion writes `animation.motionFrom: "<leader id>"` on each later
264
+ direction, and you can write it yourself when declaring the loops
265
+ explicitly. It is part of the asset's identity; the leader's hash, a single
266
+ direction's, and every template or other loop's are unchanged.
267
+ - **Adding a direction later**: append it to `directions` (new directions
268
+ at the end, so the leader stays the same). The leader is already
269
+ downloaded and current, so the new direction goes straight into its
270
+ recorded group, with no regeneration of the others. Putting a new
271
+ direction first makes it the leader and the old one a follower, which
272
+ regenerates both.
273
+ - **Regenerating**: a regenerated leader is a new animation with a new group,
274
+ so followers drawn into the old group show `stale` ("shares motion with
275
+ `<leader>`, which was regenerated") and are redrawn into the new one.
276
+ Restoring an older generation brings back the group it was drawn into.
277
+ The lock keeps it as `motionShare: { sourceAssetId, animationGroupId }`.
278
+ - **Replacing**: only an asset's own direction is ever deleted, from the
279
+ group it is in, so redrawing a follower never removes the leader's
280
+ animation or the reverse. This relies on PixelLab keeping a group alive
281
+ while any direction remains, and on a follower keeping its motion after
282
+ the leader's direction is deleted; neither is verified.
283
+ - **Fallback**: a leader with no recorded `animationGroupId` (generated
284
+ before groups were recorded, or adopted without one) does not block its
285
+ followers. Each is drawn as its own request and animation, as before, and
286
+ `plan` says so; its `motionShare.animationGroupId` is `null`. If the
287
+ leader's recorded group is gone from the character, the follower fails
288
+ before spending and asks you to regenerate the leader.
289
+ - Verified against mocked HTTP only; not yet run live. A single south
290
+ direction was verified live through PixelKiln on an adopted v3 character:
291
+ "jumping with joy and pumping both fists", 8 frames at 96×96, billed 3,
292
+ with `animationId` and `animationGroupId` saved in the lock.
263
293
 
264
294
  ### Pose frames and other loop controls
265
295
 
package/docs/ENDPOINTS.md CHANGED
@@ -922,9 +922,16 @@ generations and finished in about two minutes:
922
922
  added with `animation_group_id` move the same way (humanoids). pixelkiln
923
923
  sends this mode for a character loop with `mode: "skeleton-v3"`, a prompt,
924
924
  and no template (docs/CHARACTERS.md, "Loops from a sentence"), one request
925
- per direction asset and without `animation_group_id`. Verified live with
926
- one south direction on a v3 character; a standard-engine character is
927
- refused (`has no stored 3D skeleton`).
925
+ per direction asset. The first direction of a `directions` loop is sent
926
+ without `animation_group_id`; each later one is sent after it with the
927
+ leader's recorded `animation_group_id` and the group's own `animation_name`
928
+ (not inherited, so omitting it would rename the group). That append is
929
+ **mock-verified only**: the request shape follows the tool's documentation
930
+ and a live append has not been run. Verified live twice
931
+ with one south direction on a v3 character, once by a direct call and once
932
+ through `pixelkiln gen` (a manifest loop on an `adopt`ed character, 8
933
+ frames at 96×96, billed 3, then `pick` and `fetch`); a standard-engine
934
+ character is refused (`has no stored 3D skeleton`).
928
935
 
929
936
  ## Characters, measured
930
937
 
package/docs/MANIFEST.md CHANGED
@@ -66,7 +66,7 @@ not merely a label edit.
66
66
  | `remoteId` | string | The provider's own id for art that already exists on the account, so `adopt` maps it without matching bytes: an object id, a character id, or `<character id>#<animation group id>`. Not part of the spec's identity. |
67
67
  | `revision` | object | Controlled dependency on another asset: `image-to-image`, `inpaint`, or `outpaint`; on PixelLab also `reduce-colors`, `correct-pixelart`, `remove-background`, `normal-map`, the animate modes (`animate`, `animate-pixminimax`, `animate-skeleton`), `interpolate`, and `edit-animation`. `engine: "pro-flash"` selects PixelLab's Pro Flash tier for `image-to-image`/`inpaint`. See [controlled revisions](REVISIONS.md). |
68
68
  | `state` | object | `character` styles: a pose or outfit of another character asset. See [Characters](./CHARACTERS.md). |
69
- | `animation` | object | `character` styles: a loop of another character asset in one direction. `directions` (a list, instead of `direction`) declares the loop for several directions at once and fills the unnamed flips with mirrors; see [One loop, several directions](./CHARACTERS.md#one-loop-several-directions). `mode: "skeleton-v3"` with no `template` animates the asset's `prompt` as a sentence (3–15 `frames`, default 8; a v3 base); see [Loops from a sentence](./CHARACTERS.md#loops-from-a-sentence). |
69
+ | `animation` | object | `character` styles: a loop of another character asset in one direction. `directions` (a list, instead of `direction`) declares the loop for several directions at once and fills the unnamed flips with mirrors; see [One loop, several directions](./CHARACTERS.md#one-loop-several-directions). `mode: "skeleton-v3"` with no `template` animates the asset's `prompt` as a sentence (3–15 `frames`, default 8; a v3 base); see [Loops from a sentence](./CHARACTERS.md#loops-from-a-sentence). `motionFrom` (an asset id) makes a sentence loop follow another direction's loop and share its motion; `directions` writes it for you. |
70
70
  | `portrait` | object | `character` styles: a bust of a base or state's south sprite, `{ "of": "<asset>", "size": 64 }`; `size` is 16, 32, 48, 64 (default), 128, or 160. See [Portraits](./CHARACTERS.md#portraits). |
71
71
  | `outfit` | object | `character` styles: an existing loop's frames re-clothed from an outfit image, `{ "of": "<loop asset>", "reference": "<image>" }`, with optional `additionalInstructions`. See [Outfit transfer](./CHARACTERS.md#outfit-transfer). |
72
72
  | `mirror` | string | Another asset of the same style flipped left to right, made locally at no cost. See [Mirrors](./CHARACTERS.md#mirrors). |
package/docs/PIXELLAB.md CHANGED
@@ -100,6 +100,52 @@ beside the submit-time `cost` estimate a wave budget spends against;
100
100
  `pixelkiln history`, `plan --json`, and the gallery show both when they
101
101
  differ (docs/ENDPOINTS.md, "Limits and billing").
102
102
 
103
+ ## Custom animation on skeleton v3
104
+
105
+ PixelLab's character creator can now animate a character on its skeleton v3
106
+ model from a sentence or from skeletons you pose, and the same two paths are
107
+ in the API. PixelKiln sends both from a manifest.
108
+
109
+ A loop with `mode: "skeleton-v3"` and no `template` animates its `prompt`:
110
+
111
+ ```jsonc
112
+ "hero.cheer": {
113
+ "prompt": "cheerfully waving both arms overhead",
114
+ "animation": { "of": "hero", "mode": "skeleton-v3", "frames": 8, "directions": ["south"] }
115
+ }
116
+ ```
117
+
118
+ An `animate-skeleton` revision poses the sprite along skeletons you give it.
119
+ `pixelkiln estimate-skeleton` writes the file from an image, you edit the
120
+ joints, and `skeleton-preview` draws them over the sprite before you spend:
121
+
122
+ ```jsonc
123
+ "hero-wave": {
124
+ "prompt": "wave",
125
+ "styles": ["rig"],
126
+ "revision": {
127
+ "mode": "animate-skeleton", "from": "hero",
128
+ "keypointsFile": "poses/wave.json", "direction": "south",
129
+ "description": "cheerful adventurer with short auburn hair, blue tunic"
130
+ }
131
+ }
132
+ ```
133
+
134
+ - Both need a v3 character. A `standard` base has no 3D skeleton for the model
135
+ to pose, so `plan` refuses the loop and names the engine.
136
+ - The revision's `prompt` is the motion ("wave") and its `description` is how
137
+ the character looks. PixelLab requires both, and `plan` refuses a revision
138
+ without a description.
139
+ - Each billed exactly 3 generations for 8 frames, measured live. The sentence
140
+ loop came back at 96×96 from a 64px character; the posed one stayed 64×64.
141
+ [The home page](https://pixelkiln.griffen.codes/#updates) shows both.
142
+ - Each direction of a sentence loop is its own request for now, so a loop with
143
+ several directions can move a little differently in each. PixelLab can draw
144
+ every direction from one motion, but PixelKiln does not send that yet.
145
+
146
+ See [Loops from a sentence](./CHARACTERS.md#loops-from-a-sentence) and
147
+ [Skeleton-driven animation](./REVISIONS.md#skeleton-driven-animation).
148
+
103
149
  ## What changed in PixelLab 0.4.130
104
150
 
105
151
  PixelLab 0.4.130 (October 2026) added four things. PixelKiln covers each:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixelkiln",
3
- "version": "0.90.0",
3
+ "version": "0.91.0",
4
4
  "description": "Manifest-driven pixel-art generation, review, recovery, and packaging with deterministic provenance.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -1305,6 +1305,10 @@
1305
1305
  },
1306
1306
  "minItems": 1
1307
1307
  },
1308
+ "motionFrom": {
1309
+ "type": "string",
1310
+ "minLength": 1
1311
+ },
1308
1312
  "frames": {
1309
1313
  "type": "integer",
1310
1314
  "minimum": 3,
@@ -35,8 +35,16 @@ and which tutorial(s) demonstrated real (not hypothetical) demand for it.
35
35
  `mode: "skeleton-v3"`, no `template`, and a `prompt` animates the sentence
36
36
  on `/characters/animations` (`action_description`, 3 to 15 `frames`). Billed
37
37
  exactly 3 generations for 8 frames at 64px, frames back at 96×96; the plan
38
- budgets 4. See `docs/CHARACTERS.md`, "Loops from a sentence". Shared motion
39
- across directions is not wired up (one request per direction asset).
38
+ budgets 4. See `docs/CHARACTERS.md`, "Loops from a sentence".
39
+ - **Shared motion across the directions of a sentence loop** — the first
40
+ direction of `directions` is submitted as a new animation; each later one
41
+ follows in the next wave with the leader's recorded `animation_group_id` and
42
+ the group's `animation_name`, so every direction is drawn from one 3D motion
43
+ (`animation.motionFrom`, lock `motionShare`). Adding a direction to a
44
+ generated loop appends into its group, a regenerated leader marks followers
45
+ stale, and a leader with no recorded group falls back to one request per
46
+ direction. **Mock-verified only; live proof pending** (a live append, and that
47
+ deleting one direction leaves the group and the others' motion intact).
40
48
 
41
49
  - **Skeleton-v3 template loops** — `/characters/animations`'
42
50
  `mode: "skeleton-v3"` poses a template with the skeleton video model
@@ -524,17 +532,6 @@ missing:
524
532
  Aseprite. pixelkiln has no frame-set concatenation; `lastFrame` can pin a
525
533
  clip's ending to another clip's frame file, but the two sets stay separate.
526
534
 
527
- ## Sentence-driven skeleton-v3 loops, shared motion across directions
528
-
529
- PixelLab's sentence loops (`mode: "skeleton-v3"`, no template) are supported;
530
- see "Already closed". What is still open: every direction of one request is
531
- drawn from one 3D motion, and `animation_group_id` appends a direction to an
532
- existing animation so it moves the same way. pixelkiln makes one request per
533
- direction asset and never sends the group id, so each direction's motion is
534
- written separately. Closing it means linking the direction assets of a
535
- `directions` loop to one group (a batch like `1dir`'s, or the recorded
536
- `animationGroupId` of a sibling) and passing it on the later requests.
537
-
538
535
  ## Map Workshop (scene composition)
539
536
 
540
537
  PixelLab's actual scene-building surface: lay out a tile floor, place
@@ -398,8 +398,11 @@ and `pro-flash` bases are not refused but are unverified.
398
398
  as a sentence (`frames` 3 to 15, default 8; a v3 base): PixelLab writes it
399
399
  into one 3D motion. Measured: 8 frames at 64px billed exactly 3 generations
400
400
  and came back at 96×96, so the canvas can grow. The plan still budgets 4. One
401
- request goes out per direction asset, so directions are not drawn from one
402
- shared motion yet (docs/CHARACTERS.md, "Loops from a sentence"). The
401
+ request goes out per direction asset: the first direction of `directions`
402
+ leads, and each later one follows in the next wave, sent into the leader's
403
+ `animation_group_id` so every direction moves the same way (`animation.motionFrom`,
404
+ lock `motionShare`; mock-verified only, docs/CHARACTERS.md, "Loops from a
405
+ sentence"). The
403
406
  `prompt` is required and gets no style prefix or suffix.
404
407
 
405
408
  A named `template` loop (`walk`, `breathing-idle`, and similar) is trained