pixelkiln 0.89.6 → 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.
@@ -222,6 +222,75 @@ single direction before a batch.
222
222
  "mira.walk": { "animation": { "of": "mira", "template": "walk", "mode": "skeleton-v3", "directions": ["south", "west", "north"] } }
223
223
  ```
224
224
 
225
+ ### Loops from a sentence
226
+
227
+ `mode: "skeleton-v3"` with **no `template`** animates the asset's `prompt`
228
+ instead: PixelLab writes the sentence into one 3D motion and draws the
229
+ character from it, with no template to pick. `frames` takes PixelLab's own
230
+ 3 to 15 for this combination (odd counts included, default 8); a template
231
+ `skeleton-v3` loop still takes none.
232
+
233
+ ```jsonc
234
+ "mira.swing": {
235
+ "prompt": "swinging a sword in a wide arc",
236
+ "animation": { "of": "mira", "mode": "skeleton-v3", "frames": 8, "directions": ["south", "west"] }
237
+ }
238
+ ```
239
+
240
+ - **A v3 base is required.** The sentence is written onto the character's
241
+ stored 3D skeleton, which a `standard` base lacks, so `plan` and `validate`
242
+ refuse the pairing and name the engine. `pro` and `pro-flash` bases are
243
+ not refused but are unverified. An `objectPro` has no skeleton at all.
244
+ - **The prompt is the sentence**, sent as written as `action_description`
245
+ (a loop's prompt never takes the style's prefix or suffix, same as a v3
246
+ loop). It is required; `startFrame`, `endFrame`, `enhancePrompt`,
247
+ `subject`, and the template style hints are refused.
248
+ - **The canvas can grow.** The measured 64px loop came back at 96×96. The
249
+ frames are saved as PixelLab returns them; a mirror flips them locally like
250
+ any other loop.
251
+ - **Cost**: the plan budgets 4 per direction, the same ceiling as a template
252
+ `skeleton-v3` loop. Measured live on a v3 character: 8 frames at 64px billed
253
+ exactly 3 generations for one direction, about 15 seconds before the jobs
254
+ start. `billed` in the lock records the real amount.
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.
293
+
225
294
  ### Pose frames and other loop controls
226
295
 
227
296
  A v3 loop can start and end where you say. `startFrame` is a
@@ -489,11 +558,12 @@ reference image change alone also makes it stale.
489
558
  style (engine, size, directions, body, view, folder) or an existing one,
490
559
  the base with an optional sprite of your own (uploaded into `refs/`, so
491
560
  only the rotations are billed), any number of loops (each a template
492
- posed by `skeleton-v3` by default, or a plain template, v3, or pro loop,
561
+ posed by `skeleton-v3` by default, a `skeleton-v3` loop described in words
562
+ with a 3 to 15 frame count, or a plain template, v3, or pro loop,
493
563
  in the directions you tick, mirrors added free), and a portrait. A
494
564
  `standard` base has no 3D skeleton for `skeleton-v3` to pose, so its loops
495
- start on `template` and the skeleton-v3 choice is greyed out; the new-loop
496
- form on an existing base does the same. The
565
+ start on `template` and both skeleton-v3 choices are greyed out; the
566
+ new-loop form on an existing base does the same. The
497
567
  sheet prices the draft live, the way `plan` would. Pro Flash bases are
498
568
  priced by PixelLab's own quote (`/pro-flash/cost`, a free call) when the
499
569
  project has a key; those rows are marked live, and the offline estimate
package/docs/ENDPOINTS.md CHANGED
@@ -920,7 +920,18 @@ generations and finished in about two minutes:
920
920
  the top-level amount is a dollar zero. PixelLab says the action is written
921
921
  into one 3D motion that every direction is drawn from, so later directions
922
922
  added with `animation_group_id` move the same way (humanoids). pixelkiln
923
- does not send this mode yet.
923
+ sends this mode for a character loop with `mode: "skeleton-v3"`, a prompt,
924
+ and no template (docs/CHARACTERS.md, "Loops from a sentence"), one request
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`).
924
935
 
925
936
  ## Characters, measured
926
937
 
package/docs/MANIFEST.md CHANGED
@@ -56,7 +56,7 @@ not merely a label edit.
56
56
 
57
57
  | Field | Type/default | Meaning |
58
58
  |---|---|---|
59
- | `prompt` | string, required unless `mirror`, `portrait`, `outfit`, or a template loop (`animation.template`) | The subject wording wrapped by the selected style's prompt prefix and suffix. A template loop is described by its template; a v3 or pro loop still needs the motion in words. |
59
+ | `prompt` | string, required unless `mirror`, `portrait`, `outfit`, or a template loop (`animation.template`) | The subject wording wrapped by the selected style's prompt prefix and suffix. A template loop is described by its template; a v3, pro, or sentence (`skeleton-v3` with no template) loop still needs the motion in words. |
60
60
  | `width` / `height` | integer 16–8192 | Per-asset dimensions for generators that accept rectangular output. Provider limits may be lower. |
61
61
  | `size` | integer 16–8192 | Per-asset square size where the generator uses one dimension. |
62
62
  | `file` | string | Output path below the style's `outDir`; defaults to `<category>/<assetId>.png`. |
@@ -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). |
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/docs/REVISIONS.md CHANGED
@@ -492,7 +492,8 @@ a supplied 18-joint skeleton per frame instead of a text motion description:
492
492
  "mode": "animate-skeleton",
493
493
  "from": "hero-idle",
494
494
  "keypointsFile": "poses/hero-swing.json",
495
- "direction": "south"
495
+ "direction": "south",
496
+ "description": "a knight in steel armour holding a sword"
496
497
  }
497
498
  }
498
499
  }
@@ -522,8 +523,8 @@ over the source image, locally and for free.
522
523
  `direction` is **required** for this mode (unlike `animate-pixminimax`,
523
524
  where it is optional). `frames`/`fps`/`lastFrame`/`enhancePrompt` are all
524
525
  rejected — the frame count comes from the keypoints file itself
525
- (`frames.length`), never a manifest number. `description` (optional: what the
526
- character *looks like* — colours, clothing, held items) is a new field
526
+ (`frames.length`), never a manifest number. `description` (**required**: what
527
+ the character *looks like* — colours, clothing, held items) is a new field
527
528
  distinct from the asset's own `prompt`, which this mode sends as the motion's
528
529
  short label (PixelLab's `action`) — the same `prompt`-as-motion convention
529
530
  `animate`/`animate-pixminimax` already use, just under this endpoint's own
@@ -560,8 +561,10 @@ anchor, and returned 8 frames at the sprite's own 64×64 size. The endpoint
560
561
  **requires both `description` and `action`**: the asset's `prompt` is sent as
561
562
  `action` (a short motion label such as "wave"), and `revision.description`
562
563
  (what the character looks like) is sent as `description`. Without a
563
- `description` PixelLab answers `422` with `description: Field required`,
564
- and `plan` does not catch that yet.
564
+ `description` PixelLab answers `422` with `description: Field required`, so
565
+ `plan` refuses an `animate-skeleton` revision without a non-empty
566
+ `revision.description` (or an empty `prompt`) and the gallery's form asks for
567
+ one.
565
568
 
566
569
  ComfyUI does not support this mode either, for the same structural reason as
567
570
  `animate`/`animate-pixminimax`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixelkiln",
3
- "version": "0.89.6",
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,9 +1305,13 @@
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
- "minimum": 4,
1314
+ "minimum": 3,
1311
1315
  "maximum": 16
1312
1316
  },
1313
1317
  "fps": {
@@ -31,6 +31,21 @@ and which tutorial(s) demonstrated real (not hypothetical) demand for it.
31
31
 
32
32
  ## Already closed since this catalog's research began
33
33
 
34
+ - **Sentence-driven skeleton-v3 loops** — a character loop with
35
+ `mode: "skeleton-v3"`, no `template`, and a `prompt` animates the sentence
36
+ on `/characters/animations` (`action_description`, 3 to 15 `frames`). Billed
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".
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).
48
+
34
49
  - **Skeleton-v3 template loops** — `/characters/animations`'
35
50
  `mode: "skeleton-v3"` poses a template with the skeleton video model
36
51
  (2 to 4 generations and 3 to 5 minutes per direction, beta, Tier 1 and
@@ -299,7 +314,7 @@ and which tutorial(s) demonstrated real (not hypothetical) demand for it.
299
314
  `resolveSpecs`/`estimate()`-time call — so `animate-skeleton`'s submission
300
315
  composes exactly one PixelLab endpoint, not two chained together. Both
301
316
  endpoints have since run live once (8 frames billed exactly 3), and the
302
- run found that `description` is required: `plan` does not check for it yet. **Correcting this catalog's own prior research**: this entry
317
+ run found that `description` is required, which `plan` now enforces. **Correcting this catalog's own prior research**: this entry
303
318
  used to also claim a distinct "animation to animation" endpoint existed,
304
319
  for transferring an existing walk cycle's motion onto a different
305
320
  character. Checked against the live `animate_with_skeleton_v3` MCP tool
@@ -517,20 +532,6 @@ missing:
517
532
  Aseprite. pixelkiln has no frame-set concatenation; `lastFrame` can pin a
518
533
  clip's ending to another clip's frame file, but the two sets stay separate.
519
534
 
520
- ## Sentence-driven skeleton-v3 loops
521
-
522
- PixelLab's October 2026 character-creator update adds custom animation on the
523
- skeleton v3 model: a sentence becomes one 3D motion that every direction of
524
- the animation is drawn from, so a direction added later moves the same way.
525
- It is `POST /characters/animations` with `mode: "skeleton-v3"`, an
526
- `action_description`, and no `template_animation_id` (3 to 15 frames, humanoid
527
- v3 characters). **Measured once**: an 8-frame south loop billed exactly 3 and
528
- came back at 96×96 (docs/ENDPOINTS.md, "Skeleton-driven motion, measured").
529
- Still a gap: a character loop's `mode: "skeleton-v3"` needs a `template`,
530
- and the request never sends `action_description` for it. Closing it means a
531
- loop that names `skeleton-v3` and a `prompt` without a template, a
532
- `frames` count from 3 to 15, and adding a direction to an existing group.
533
-
534
535
  ## Map Workshop (scene composition)
535
536
 
536
537
  PixelLab's actual scene-building surface: lay out a tile floor, place
@@ -394,6 +394,17 @@ stored 3D skeleton: a `v3` base has one (measured, 3 generations); a
394
394
  refuses the loop at `plan`/`validate`, pointing at `mode: "template"`. `pro`
395
395
  and `pro-flash` bases are not refused but are unverified.
396
396
 
397
+ `"mode": "skeleton-v3"` with **no** `template` animates the asset's `prompt`
398
+ as a sentence (`frames` 3 to 15, default 8; a v3 base): PixelLab writes it
399
+ into one 3D motion. Measured: 8 frames at 64px billed exactly 3 generations
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: 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
406
+ `prompt` is required and gets no style prefix or suffix.
407
+
397
408
  A named `template` loop (`walk`, `breathing-idle`, and similar) is trained
398
409
  mostly on characters with empty hands, and reliably struggles once a
399
410
  character holds an item — a weapon, a tool, anything gripped. For a