pixelkiln 0.46.0 → 0.47.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/docs/MANIFEST.md CHANGED
@@ -13,8 +13,8 @@ directory. The canonical machine-readable contract is
13
13
  "styles": {
14
14
  "base": {
15
15
  "generator": "map",
16
- "promptPrefix": "Pixel-art game prop: ",
17
- "promptSuffix": ", isolated, transparent background",
16
+ "promptPrefix": "pixel-art game prop",
17
+ "promptSuffix": "isolated, transparent background",
18
18
  "outDir": "assets/generated/base"
19
19
  }
20
20
  },
@@ -37,6 +37,17 @@ Unknown properties are rejected at every level.
37
37
  | `styles` | yes | Map of style id to inherited generation/output settings. |
38
38
  | `assets` | yes | Map of stable asset id to subject and per-asset overrides. |
39
39
 
40
+ ```jsonc
41
+ {
42
+ "$schema": "./node_modules/pixelkiln/schema/manifest.schema.json",
43
+ "name": "my-game", // tags every provider object pixelkiln:my-game
44
+ "provider": "pixellab", // the default; a style may name another
45
+ "history": 3, // keep three replaced generations per asset
46
+ "styles": { "base": { "generator": "map", "outDir": "assets/generated/base" } },
47
+ "assets": { "anvil": { "prompt": "a compact blacksmith anvil" } }
48
+ }
49
+ ```
50
+
40
51
  A resolved unit of work is one `styleId/assetId`. Asset ids are stable lookup
41
52
  keys, atlas frame ids, and default filenames; changing one is a data migration,
42
53
  not merely a label edit.
@@ -54,16 +65,17 @@ not merely a label edit.
54
65
  | `sourceByStyle` | object | Per-style `source`, keyed by style id; wins over `source` for that style. Written by `pixelkiln edit` for a hand edit of an asset that is in several styles. |
55
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. |
56
67
  | `revision` | object | Controlled image-to-image or inpaint dependency. See [controlled revisions](REVISIONS.md). |
57
- | `state` | object | `character` styles: a pose or outfit of another character asset. See [Characters](#characters). |
58
- | `animation` | object | `character` styles: a loop of another character asset in one direction. See [Characters](#characters). |
59
- | `mirror` | string | Another asset of the same style flipped left to right, made locally at no cost. See [Mirrors](#mirrors). |
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. See [Characters](./CHARACTERS.md). |
70
+ | `mirror` | string | Another asset of the same style flipped left to right, made locally at no cost. See [Mirrors](./CHARACTERS.md#mirrors). |
60
71
  | `proportions` | preset or object | `character` styles: this base's proportions, over the style's. |
61
- | `reference` | string or object | `character` styles, bases: the character's own south-facing sprite (or `{ "south": ..., "east": ... }`), which PixelLab rotates instead of drawing from the prompt. See [Characters](#characters). |
72
+ | `reference` | string or object | `character` styles, bases: the character's own south-facing sprite (or `{ "south": ..., "east": ... }`), which PixelLab rotates instead of drawing from the prompt. See [Characters](./CHARACTERS.md). |
62
73
  | `concept` | string | `character` styles, `pro` bases: a manifest-relative concept image (up to 1024px) the design is seeded from. |
63
74
  | `styleCharacter` | string | `character` styles, `pro` bases: this base's style anchor, over the style's. |
64
75
  | `providerInputs` | JSON scalar/sequence map, `{}` | Named per-asset inputs consumed by the active provider. ComfyUI accepts scalars and, for `frames`, one ordered 2–64 value sequence. Image bindings upload PNG/JPEG inputs. |
65
76
  | `styles` | string array, `[]` | Restrict the asset to named styles; empty means every style. |
66
77
  | `promptByStyle` | string map, `{}` | Replace only the asset prompt for a named style. |
78
+ | `cell` | `[column,row]` | The grid cell this asset owns in a mounted sheet; see [stable-cell mounting](#stable-cell-mounting). |
67
79
  | `outputRole` | string | Select one structural output when a generator returns a set. |
68
80
  | `tags` | string array, `[]` | Asset tags added to provider objects where supported. |
69
81
 
@@ -75,6 +87,26 @@ Only a provider that declares a sequence contract may accept an array. ComfyUI
75
87
  uses the array named by `providerOptions.comfyui.frames.vary`; other inputs stay
76
88
  constant across the set.
77
89
 
90
+ One asset using the fields most projects reach for:
91
+
92
+ ```jsonc
93
+ {
94
+ "assets": {
95
+ "anvil": {
96
+ "prompt": "a compact blacksmith anvil",
97
+ "promptByStyle": { "mono": "a compact blacksmith anvil, dark iron" },
98
+ "width": 48,
99
+ "height": 32,
100
+ "category": "tools", // assets/generated/base/tools/anvil.png
101
+ "tags": ["prop", "forge"],
102
+ "styles": ["base", "mono"], // skip every other style
103
+ "cell": [2, 0], // its cell on a mounted sheet
104
+ "source": "edits/anvil.png" // a hand edit stands in for the generated file
105
+ }
106
+ }
107
+ }
108
+ ```
109
+
78
110
  ## Style fields
79
111
 
80
112
  | Field | Type/default | Meaning |
@@ -101,7 +133,7 @@ constant across the set.
101
133
  | `tileView` | enum | `top-down`, `high top-down`, `low top-down`, or `side`. |
102
134
  | `tileFeature` | enum | Connectable `roads`, `tileset`, or `building` structural set. |
103
135
  | `outlineMode` | enum | `outline` or `segmentation`; segmentation avoids quilted ground seams. |
104
- | `mode` | `standard` | `character` only. `standard`, `v3`, `pro`, or `pro-flash`; see [Characters](#characters). |
136
+ | `mode` | `standard` | `character` only. `standard`, `v3`, `pro`, or `pro-flash`; see [Characters](./CHARACTERS.md). |
105
137
  | `directions` | `8` | `character` only. `4` or `8`; every engine but `standard` draws 8. |
106
138
  | `template` | `mannequin` | `character` only. Body template: `mannequin`, or `bear`, `cat`, `dog`, `horse`, `lion`; `custom` for `pro-flash`. |
107
139
  | `proportions` | | `character` only, `standard` humanoid bases. A preset (`default`, `chibi`, `cartoon`, `stylized`, `realistic_male`, `realistic_female`, `heroic`) or multipliers `{ headSize, armsLength, legsLength, shoulderWidth, hipWidth }`, each 0.5 to 2. An asset may override it. |
@@ -114,6 +146,36 @@ constant across the set.
114
146
  | `quality` | object | Optional native-grid, final-palette, and human-approval contract; documented below. |
115
147
  | `tags` | string array, `[]` | Tags inherited by every generated provider object in the style. |
116
148
 
149
+ One style using the common fields, and a `pixflux` style with a locked
150
+ palette:
151
+
152
+ ```jsonc
153
+ {
154
+ "styles": {
155
+ "base": {
156
+ "generator": "map",
157
+ "outDir": "assets/generated/base",
158
+ "promptPrefix": "pixel-art game prop",
159
+ "promptSuffix": "isolated, transparent background",
160
+ "size": 64,
161
+ "view": "high top-down",
162
+ "outline": "selective outline",
163
+ "shading": "medium shading",
164
+ "detail": "medium detail",
165
+ "seed": 7,
166
+ "tags": ["props"]
167
+ },
168
+ "gb": {
169
+ "generator": "pixflux",
170
+ "outDir": "assets/generated/gb",
171
+ "palette": ["#0f380f", "#306230", "#8bac0f", "#9bbc0f"],
172
+ "enforcePalette": true,
173
+ "noBackground": false
174
+ }
175
+ }
176
+ }
177
+ ```
178
+
117
179
  ### Style inheritance
118
180
 
119
181
  Use `extends` for a real variant that differs from a base style by a few
@@ -300,470 +362,82 @@ disagreement rejects the whole set. The schema rejects `tiles` and provider
300
362
  `animation`, whose output contracts differ. See
301
363
  [Quality gates](./QUALITY.md#manifest-quality-profile) for the release workflow.
302
364
 
303
- ## Experimental Retro Diffusion
304
-
305
- Retro Diffusion maps `map` and `pixflux` to still generation, `tiles` to its
306
- tileset family, and `animation` to GIF or PNG-spritesheet generation. Durable
307
- sources and lock outputs record `image/png` or `image/gif`, so recovery retains
308
- the correct extension and validates the correct structure.
309
-
310
- ```jsonc
311
- {
312
- "name": "my-game",
313
- "provider": "retrodiffusion",
314
- "styles": {
315
- "base": {
316
- "generator": "map",
317
- "outDir": "assets/generated/base",
318
- "providerOptions": {
319
- "retrodiffusion": {
320
- "promptStyle": "rd_plus__default",
321
- "numImages": 4,
322
- "removeBg": true
323
- }
324
- }
325
- }
326
- },
327
- "assets": {
328
- "anvil": { "prompt": "a compact blacksmith anvil" }
329
- }
330
- }
331
- ```
332
-
333
- `promptStyle` accepts a live Retro Diffusion still-style selector,
334
- `numImages` accepts 1–16 candidates, and `removeBg` overrides
335
- `noBackground`. The Retro Diffusion API accepts 16–512px output. Selected
336
- styles can impose smaller limits. RD Pro and user styles accept up to nine
337
- reference images. Costs are
338
- planned in USD and checked again with Retro Diffusion's free authoritative
339
- quote endpoint before the paid request is sent. Authenticated single-candidate
340
- RD Fast and RD Plus paths have passed from quote through validated output and
341
- recovery.
342
- Multi-candidate, tileset, GIF, and spritesheet paths remain mock-tested, so the
343
- adapter is still experimental.
344
-
345
- Additional Retro Diffusion options are:
346
-
347
- | Option | Meaning |
348
- |---|---|
349
- | `framesDuration` | Animation duration: `4`, `6`, `8`, `10`, `12`, or `16`. |
350
- | `returnSpritesheet` | Return a PNG spritesheet instead of an animated GIF. |
351
- | `extraPrompt` | Outside texture description for `rd_tile__tileset_advanced`. |
352
- | `tileX` / `tileY` | Make supported still styles seamless on either axis. |
353
-
354
- An animation style is declared explicitly:
355
-
356
- ```jsonc
357
- {
358
- "generator": "animation",
359
- "size": 64,
360
- "outDir": "assets/generated/animations",
361
- "providerOptions": {
362
- "retrodiffusion": {
363
- "promptStyle": "rd_animation__any_animation",
364
- "numImages": 1,
365
- "framesDuration": 8,
366
- "returnSpritesheet": false
367
- }
368
- }
369
- }
370
- ```
371
-
372
- The default output is `<assetId>.gif`; `returnSpritesheet: true` produces
373
- `<assetId>.png`. Advanced animation styles require exactly one `styleImages`
374
- input. PixelKiln currently limits animation batches to one so selection never
375
- loses the output media type.
376
-
377
- For a Wang-style tileset sheet:
378
-
379
- ```jsonc
380
- {
381
- "generator": "tiles",
382
- "tileSize": 32,
383
- "outDir": "assets/generated/tiles",
384
- "providerOptions": {
385
- "retrodiffusion": {
386
- "promptStyle": "rd_tile__tileset",
387
- "numImages": 1
388
- }
389
- }
390
- }
391
- ```
392
-
393
- `rd_tile__tileset_advanced` accepts `extraPrompt` and up to two style images;
394
- `rd_tile__tile_variation` requires one style image. Provider-specific size and
395
- input constraints are checked during the free planning phase.
396
-
397
- ## Experimental ComfyUI
365
+ ## Provider-specific fields
398
366
 
399
- ComfyUI runs a committed API-format workflow on a self-hosted server. The
400
- workflow file is resolved relative to the manifest and its parsed content is
401
- part of the spec hash. Adapter success proves transport and output structure,
402
- not pixel-art quality. Use provider-neutral `pixelkiln refine` for native-grid,
403
- final-palette, and recorded audit checks. Prompt coverage and human 1× approval
404
- still require a person.
367
+ Each experimental provider adds style and asset fields of its own, under
368
+ `providerOptions.<provider>` on the style and `providerInputs` on the asset.
369
+ They are documented with the provider:
405
370
 
406
- ```jsonc
407
- {
408
- "name": "my-game",
409
- "provider": "comfyui",
410
- "styles": {
411
- "local": {
412
- "generator": "map",
413
- "outDir": "assets/generated/local",
414
- "seed": 31415,
415
- "providerOptions": {
416
- "comfyui": {
417
- "workflowFile": "workflows/pixel-api.json",
418
- "outputNodeId": "9",
419
- "numImages": 4,
420
- "bindings": {
421
- "prompt": { "nodeId": "6", "input": "text" },
422
- "width": { "nodeId": "5", "input": "width" },
423
- "height": { "nodeId": "5", "input": "height" },
424
- "batchSize": { "nodeId": "5", "input": "batch_size" },
425
- "seed": { "nodeId": "3", "input": "seed" },
426
- "composition": { "nodeId": "19", "input": "image" },
427
- "controlStrength": { "nodeId": "20", "input": "strength" }
428
- }
429
- }
430
- }
431
- }
432
- },
433
- "assets": {
434
- "mountain": {
435
- "prompt": "a snowbound mountain pass",
436
- "width": 768,
437
- "height": 512,
438
- "providerInputs": {
439
- "composition": "controls/mountain-layout.png",
440
- "controlStrength": 0.7
441
- }
442
- }
443
- }
444
- }
445
- ```
371
+ - [Retro Diffusion](./RETRO_DIFFUSION.md#manifest-fields): still styles,
372
+ tilesets, and GIF or spritesheet animation.
373
+ - [ComfyUI](./COMFYUI.md#manifest-fields): a committed workflow, its bindings,
374
+ revisions, and atomic frame sets.
375
+ - [Scenario](./SCENARIO.md#manifest-fields): hosted model ids and Compute
376
+ Unit ceilings.
446
377
 
447
- Node IDs come from the exported workflow; they are not stable across unrelated
448
- workflows. Binding names beyond PixelKiln's built-ins are project-defined and
449
- an asset overrides one with a matching `providerInputs` value. A custom
450
- binding aimed at `LoadImage.image` or `LoadImageMask.image` treats its string as
451
- a manifest-relative PNG/JPEG, hashes it, and uploads it at submission. Other
452
- custom inputs accept a string, number, or boolean matching the workflow's
453
- placeholder type. Local paths never enter stable provenance.
454
-
455
- The current adapter supports `map` and ordered still-image `frames`, PNG output
456
- from one node, 1–16 `map` candidates, and dimensions from 16–4096px. A frame
457
- style uses `numImages: 1`; the varying input supplies 2–64 renders. It rejects
458
- manifest `styleImages` and `palette`;
459
- use custom image bindings for per-asset ControlNet or reference images, and keep
460
- shared model/LoRA/palette controls inside the workflow. See
461
- [Set up ComfyUI](COMFYUI.md) for the complete procedure, safe workflow, and
462
- quality limits. The 4096px adapter ceiling is not a recommended generation or
463
- native-art size.
464
-
465
- ## Experimental Scenario
466
-
467
- Scenario uses hosted model IDs and requires a conservative Compute Unit ceiling
468
- for every style:
378
+ Only the active provider's object is resolved and hashed, so one style can
379
+ carry settings for more than one provider and switch between them by
380
+ changing `provider` alone:
469
381
 
470
382
  ```jsonc
471
383
  {
472
- "provider": "scenario",
384
+ "provider": "pixellab",
473
385
  "styles": {
474
386
  "environment": {
475
387
  "generator": "map",
476
- "size": 512,
477
388
  "outDir": "assets/generated/environment",
478
389
  "providerOptions": {
479
- "scenario": {
480
- "modelId": "model_bfl-flux-2-dev",
481
- "projectId": "project_example",
482
- "numOutputs": 4,
483
- "maxComputeUnits": 60,
484
- "parameters": {
485
- "guidance": 4,
486
- "numInferenceSteps": 28
487
- }
488
- }
390
+ "retrodiffusion": { "promptStyle": "rd_plus__default", "numImages": 4 },
391
+ "comfyui": { "workflowFile": "workflows/still-api.json", "outputNodeId": "9" },
392
+ "scenario": { "modelId": "model_bfl-flux-2-dev", "maxComputeUnits": 60 }
489
393
  }
490
394
  }
491
395
  },
492
- "assets": {
493
- "mountain-town": { "prompt": "a fortified mountain town" }
494
- }
396
+ "assets": { "keep": { "prompt": "a mountain keep at dusk" } }
495
397
  }
496
398
  ```
497
399
 
498
- | Option | Meaning |
499
- |---|---|
500
- | `modelId` | Required Scenario endpoint model ID. |
501
- | `maxComputeUnits` | Required positive per-asset offline ceiling and maximum accepted live quote. |
502
- | `numOutputs` | One to four PNG candidates; defaults to one. |
503
- | `projectId` | Optional Scenario ownership/routing project. |
504
- | `parameters` | Additional model-specific JSON inputs. PixelKiln-owned request fields cannot be overridden. |
400
+ `providerInputs` on an asset is the same idea per asset, for providers that
401
+ declare bindings (ComfyUI); a provider without them rejects a non-empty
402
+ object rather than ignoring it.
505
403
 
506
- The current adapter supports `map`, dimensions from 128–2048px in multiples of
507
- 16, optional seed, and no style-image uploads. Every paid request is preceded
508
- by an identical `dryRun=true` request. See [Set up Scenario](SCENARIO.md) for
509
- credentials, cost semantics, recovery, and the paid live-test boundary.
510
-
511
- ## Asset fields
404
+ ## Characters and mirrors
512
405
 
513
- | Field | Type/default | Meaning |
514
- |---|---|---|
515
- | `prompt` | string, required unless `mirror` | Subject-specific prompt. It may be empty only during existing-art onboarding. |
516
- | `category` | string | Human grouping metadata. |
517
- | `width` | integer 16–8192 | Per-asset width override. Each provider applies its own ceiling. |
518
- | `height` | integer 16–8192 | Per-asset height override. Each provider applies its own ceiling. |
519
- | `size` | integer 32–256 | Per-asset square size override. |
520
- | `file` | string | Filename/path override beneath the style output root. |
521
- | `styles` | string array, `[]` | If non-empty, generate this asset only in the named styles. |
522
- | `promptByStyle` | object, `{}` | Replace the asset prompt for specific style ids. |
523
- | `tags` | string array, `[]` | Asset tags combined with style tags. |
524
- | `cell` | `[column,row]` | Non-negative stable grid cell used by `mount`. |
525
- | `source` | string | Manifest-relative post-processed/hand-drawn source placed by `mount`, `pack`, and `export` instead of lock output. For a set of PNG outputs, a path that is not a file is a stem: each member is placed from `<stem>-<role>.png`, the rule generated outputs follow. A file that exists there is one image placed for the whole set, as before. |
526
- | `sourceByStyle` | object | The same, for one style only. A hand edit belongs to one generation, so an asset shared by styles keeps one edit per style. |
527
- | `revision` | object | Generate a new asset from another asset's current bytes. `source` and `revision` are mutually exclusive. |
528
- | `outputRole` | string | Select one member of a structural output set for mounting. |
529
- | `mirror` | string | Another asset of this style, flipped left to right. No prompt, no provider, no cost. See [Mirrors](#mirrors). |
530
- | `proportions` | preset or object | This base's body proportions, over the style's. `standard` humanoid bases only. |
531
- | `reference` | string or object | The character's own sprite(s) for PixelLab to rotate. Bases only. |
532
- | `concept` | string | A concept image a `pro` base is designed from. Bases only. |
533
- | `styleCharacter` | string | The character in this style a `pro` base takes its look from, over the style's. |
534
-
535
- ## Characters
406
+ A `character` style holds a base drawn facing 4 or 8 directions, states, and
407
+ loops, with a `mirror` asset for the direction that faces the other way,
408
+ made locally at no cost. The shapes, engines, references, and costs have
409
+ their own page: [Characters](./CHARACTERS.md) and
410
+ [Mirrors](./CHARACTERS.md#mirrors).
536
411
 
537
- ```json
412
+ ```jsonc
538
413
  {
539
414
  "styles": {
540
415
  "cast": {
541
416
  "generator": "character",
542
417
  "outDir": "art/characters",
543
- "view": "side",
544
- "size": 64,
545
- "mode": "standard",
546
- "palette": ["#0f380f", "#306230", "#8bac0f", "#9bbc0f"],
547
- "enforcePalette": true
418
+ "mode": "pro-flash", // standard, v3, pro, or pro-flash
419
+ "template": "custom", // mannequin, a quadruped, or custom (pro-flash)
420
+ "size": 96
548
421
  }
549
422
  },
550
423
  "assets": {
551
- "mira": {
552
- "prompt": "small young woman, dark curly hair in a bun, oversized hoodie"
424
+ "bot": {
425
+ "prompt": "small round orange robot with one blue eye",
426
+ "reference": "refs/bot-south.png" // rotate this sprite instead of drawing from text
553
427
  },
554
- "mira.chair_spin": {
555
- "prompt": "one hand high on the pole, sitting in the air with knees up, spinning",
556
- "state": { "of": "mira", "paletteFromReference": true }
428
+ "bot.dented": {
429
+ "prompt": "shell dented, one arm hanging loose",
430
+ "state": { "of": "bot", "paletteFromReference": true }
557
431
  },
558
- "mira.fireman_spin": {
559
- "prompt": "spinning around the pole, knees crossed",
560
- "animation": { "of": "mira.chair_spin", "direction": "east", "frames": 8, "fps": 10 }
432
+ "bot.walk.west": {
433
+ "prompt": "walking in place, short legs stepping",
434
+ "animation": { "of": "bot", "direction": "west", "frames": 12, "fps": 12 }
561
435
  },
562
- "mira.walk": {
563
- "prompt": "",
564
- "animation": { "of": "mira", "template": "walk", "direction": "south" }
565
- }
436
+ "bot.walk.east": { "mirror": "bot.walk.west" } // flipped locally, 0 generations
566
437
  }
567
438
  }
568
439
  ```
569
440
 
570
- A `character` style holds three shapes of asset that share one PixelLab
571
- character.
572
-
573
- A base is a prompt, wrapped in the style's prefix and suffix like any other
574
- asset, that PixelLab draws facing 4 or 8 directions. `mode` picks the
575
- engine: `standard` (1 generation, the skeleton template, `outline`,
576
- `shading`, and `detail` as soft guidance, and `palette` sent as a colour
577
- reference), `v3` (2 to 9 by size, the highest quality, up to 256px),
578
- `pro` (20 to 40 by size), or `pro-flash` (6 to 17 by size: PixelLab's
579
- newest image model draws the south sprite and v3 rotates it; sizes are
580
- multiples of 4 up to 256, `template` may be `custom`, and a `reference`
581
- pays for the rotations only, 1 at 64px). `size` is the character's size;
582
- `view` is `low top-down` (the default), `high top-down`, or `side`;
583
- `template` picks the body. Each direction lands as
584
- `<asset>-<direction>.png`, south first.
585
- Standard mode draws on a canvas 28px larger than `size`, 14px of room on
586
- each side for animation (a 64px character comes back as 92px files, a
587
- 104px one as 132px); the lock records the size asked for, and the files
588
- are what PixelLab drew.
589
-
590
- Three more knobs shape a `standard` base. `proportions` is a preset
591
- (`chibi`, `heroic`, and so on) or multipliers on the mannequin's head,
592
- arms, legs, shoulders, and hips; it lives on the style and any base may
593
- override it, and only the mannequin template has proportions to set.
594
- `textGuidanceScale` (1 to 20, PixelLab's default 8) is how closely the text
595
- is followed, and applies to template loops as well. `isometric` draws the
596
- base and every loop in isometric view. The v3 and pro engines take none of
597
- the three for a base; a v3 style may still set the last two for its loops.
598
- A `v3` base takes `enhancePrompt` instead, which has PixelLab expand the
599
- prompt into a fuller one before drawing.
600
-
601
- A base can also start from your own sprite. `reference` names a
602
- manifest-relative PNG or JPEG of the character facing south, and PixelLab
603
- draws the other directions from it, with the prompt as guidance:
604
-
605
- ```json
606
- "mira": { "prompt": "small young woman, oversized hoodie", "reference": "refs/mira-south.png" }
607
- ```
608
-
609
- `standard` uses each image as it is, centred on its larger canvas, and
610
- generates the rest, so the image must be the style's `size` exactly; it
611
- accepts one image per direction
612
- (`{ "south": ..., "east": ... }`), and a quadruped template needs south and
613
- east. `v3` rotates one south image of up to 256px (its `template` must
614
- match the body in the image). `pro` rotates one south image of up to 168px
615
- through its `rotate_character` method. The reference's bytes are part of
616
- the base's identity, so a redrawn file makes the base stale, and the file
617
- is read again at submit time and refused if it changed since `plan`.
618
- States, animations, and mirrors take their look from their parent and
619
- cannot carry a reference.
620
-
621
- The pro engine has two more ways in. `concept` on a base names a concept
622
- image (a painting, a photo, a sketch, up to 1024px) that seeds the design
623
- in place of the prompt alone (PixelLab's `create_from_concept`); the
624
- prompt still guides it. `styleCharacter`, on the style or a base, names a
625
- generated 8-direction character in the same style whose look the base
626
- follows (`style_character_id`); its south sprite becomes the style image
627
- unless a style image is also given. The anchor is a dependency like a
628
- loop's character: the base is `blocked` until the anchor is downloaded and
629
- current, `gen` runs it in the wave after the anchor lands, and a
630
- regenerated anchor makes every base drawn in its style `stale`. The anchor
631
- itself ignores the setting, so a style can name its own first character.
632
- PixelLab wants the base at least as large as the anchor's visible sprite
633
- and fails the job fast otherwise. A base rotating its own `reference`
634
- takes neither: the rotate method has one image slot and it is the
635
- character.
636
-
637
- Style images on a `character` style are a pro input: one image of up to
638
- 168px that anchors the look of a `pro` base drawn from text or a concept
639
- (`create_with_style` and `create_from_concept` both take it), or one of
640
- up to 256px that a `pro-flash` base copies its look from, no larger than
641
- the character's `size` on either side (crop it to its subject), with
642
- `styleTraits` (`palette`, `outline`, `detail`, `shading`, each on by
643
- default) choosing which traits it lends. `standard` and `v3` have no such
644
- slot and refuse them; a base with a `reference` refuses them too, in
645
- either engine.
646
-
647
- A state is a text edit of an existing character, `state.of`, applied to
648
- every direction at once: a pose, an outfit, a held object. The prompt is
649
- the edit and goes to PixelLab as written, without the style's prefix and
650
- suffix, since the character already carries the look.
651
- `paletteFromReference` snaps the result to the parent's colours; `canvas`
652
- asks for a larger frame when the edit adds something big. A state costs 20
653
- to 40 by canvas and keeps the parent's directions. A state may be a state
654
- of another state.
655
-
656
- An animation is one loop of one character (`animation.of`, a base or a
657
- state) in one `direction`. With a `template` (PixelLab's `walk`,
658
- `breathing-idle`, `running-8-frames`, and so on) it costs 1 and the
659
- template decides the frame count; without one the prompt is the motion and
660
- PixelLab's v3 engine draws `frames` frames (4 to 16, even, default 8) for
661
- `ceil(size² × frames / 65536)` generations, one at 64px. `keepFirstFrame`
662
- (on by default) stores the resting pose as frame 0, so 8 frames land as 9
663
- files, `<asset>-frame-00.png` onwards. `fps` is recorded with the frames
664
- for the gallery, `pack`, and the Godot and Aseprite formats; PixelLab does
665
- not keep one. An animation lands in review as an ordered set: `pick` shows
666
- the loop and accepts or rejects it whole. One asset per direction; declare
667
- another asset for another direction, or a [mirror](#mirrors) of this one
668
- for the direction that faces the other way.
669
-
670
- A v3 loop can start and end where you say. `startFrame` is a
671
- manifest-relative image of the pose to begin from instead of the
672
- character's rotation; `endFrame` is a pose to reach, and with it the loop
673
- interpolates from the start frame to that image (both up to 256px, and the
674
- end frame the same size as the start frame or, without one, as the
675
- character's rotation, which `plan` checks once the parent is on disk; the
676
- frames can still come back on a taller canvas when the motion needs it, as
677
- a 92px crouch did at 92×104). `subject` replaces the character's own description for this loop when it
678
- would mislead the model (a state that took the armour off), and
679
- `enhancePrompt` lets PixelLab expand the action into a fuller motion
680
- description first. A template loop takes none of those; it takes
681
- `outline`, `shading`, and `detail` overrides instead, over the character's
682
- own. The style's `palette` goes to every loop as a colour reference, as it
683
- does to a `standard` base, and `enforcePalette` still snaps the frames
684
- afterwards. Pose images hash into the loop's identity and are read again
685
- at submit time, like a base's `reference`.
686
-
687
- ```json
688
- "mira.bow": {
689
- "prompt": "bowing deeply from the waist",
690
- "animation": { "of": "mira", "direction": "south", "frames": 6, "endFrame": "poses/mira-bowed.png" }
691
- }
692
- ```
693
-
694
- States and animations depend on their parent the way a revision does. The
695
- parent must be downloaded and current before the child is actionable;
696
- `plan` reports the child as `blocked` and names the parent until then, and
697
- one `gen` runs the waves in order: bases, then states, then animations,
698
- under one budget. The
699
- child's identity includes the parent's generated south-facing file, so
700
- regenerating the parent makes every state and animation of it `stale`. A
701
- hand edit of the parent does not, because PixelLab draws the child from the
702
- character it holds, not from local bytes.
703
-
704
- Regenerating an animation clears PixelKiln's own earlier take of that
705
- direction on the character first, since PixelLab skips a direction that
706
- already exists. The lock records the animation and group ids PixelLab
707
- assigned, and the delete goes by those (PixelLab keeps the name PixelKiln
708
- gives a loop only for text animations, not template ones). Nothing else on
709
- the character is touched, and a base or state is never deleted by
710
- PixelKiln.
711
-
712
- `enforcePalette` snaps every direction and every frame. `pack --style cast
713
- --format godot` writes a `SpriteFrames` with each direction of a base or
714
- state as a still and each animation as a looping set at its fps;
715
- `--format aseprite` does the same with `frameTags`.
716
-
717
- Characters that already exist on the account come under the manifest with
718
- `adopt`. Declare the asset with `remoteId` (the character id, or
719
- `<character id>#<animation group id>` for a loop; `get_character` in
720
- PixelLab's own tools shows both) and run `pixelkiln adopt`: it records the
721
- character, writes every direction and frame that is not on disk, and costs
722
- nothing. A base can also be matched by the bytes of its south-facing file.
723
- See [`adopt`](CLI.md#adopt).
724
-
725
- ## Mirrors
726
-
727
- Each direction of a loop is its own generation, and a sprite walking east
728
- is the sprite walking west flipped. A `mirror` asset is that flip, made
729
- locally from the source asset's downloaded files:
730
-
731
- ```json
732
- "hero.walk.west": { "prompt": "", "animation": { "of": "hero", "template": "walk", "direction": "west" } },
733
- "hero.walk.east": { "mirror": "hero.walk.west" }
734
- ```
735
-
736
- A full 8-direction set of one loop is then 5 generations (south, north,
737
- west, south-west, north-west) and 3 mirrors; a 4-direction set is 3 and 1.
738
- The mirror takes its shape from the source (a loop of the same character,
739
- facing the other way, at the same fps), needs no prompt, and costs
740
- nothing: `plan` lists it under the provider with a cost of 0 and `gen`
741
- flips it in the wave after the source lands. It has its own lock entry and
742
- files (`hero.walk.east-frame-00.png` onwards), so `pack`, the gallery, and
743
- an engine see an ordinary loop. Nothing is sent to the provider, and the
744
- account holds no east animation.
745
-
746
- The lock records a hash over the source's output hashes when the flip was
747
- made. A source that is regenerated, restored, or re-snapped makes its
748
- mirrors `stale`, and the next `gen` flips them again for free. A mirror
749
- whose files went missing is `stale` too; one that was hand-edited is
750
- `orphaned`, like generated art. A source that is not downloaded, current,
751
- and untouched blocks its mirrors.
752
-
753
- A loop facing south or north cannot be mirrored: the flip would be the same
754
- direction with its asymmetries swapped, not a new one. A single image or a
755
- frame set from any generator can be mirrored, and so can a base or state
756
- (every direction flipped and relabelled, so west becomes east). A tile set
757
- cannot; its edges carry meaning. Mirroring swaps handedness, so a character
758
- who holds a sword in the right hand holds it in the left when facing the
759
- mirrored way. Most games accept that; if yours does not, generate both
760
- sides. `adopt` skips mirrors, since there is nothing upstream to adopt.
761
-
762
- Engines that flip sprites at draw time (Godot's `flip_h`, Unity's
763
- `flipX`) do not need mirrored files at all. Declare only the directions
764
- you generate and flip in the engine; mirrors are for pipelines that want
765
- every direction on disk.
766
-
767
441
  ## Controlled revisions
768
442
 
769
443
  ```jsonc
@@ -808,7 +482,25 @@ reject the revision during offline resolution. See
808
482
 
809
483
  ## Prompt and override resolution
810
484
 
811
- For each participating style/asset pair:
485
+ ```jsonc
486
+ {
487
+ "styles": {
488
+ "base": { "generator": "map", "outDir": "art/base", "promptPrefix": "pixel-art prop", "promptSuffix": "transparent background" },
489
+ "mono": { "extends": "base", "outDir": "art/mono", "promptSuffix": "one-bit, transparent background" }
490
+ },
491
+ "assets": {
492
+ "star": {
493
+ "prompt": "a golden star",
494
+ "promptByStyle": { "mono": "a five-pointed star" } // colour words would fight the style
495
+ }
496
+ }
497
+ }
498
+ ```
499
+
500
+ Prefix, subject, and suffix are trimmed and joined with ", ": `base/star`
501
+ is sent as "pixel-art prop, a golden star, transparent background" and
502
+ `mono/star` as "pixel-art prop, a five-pointed star, one-bit, transparent
503
+ background". For each participating style/asset pair:
812
504
 
813
505
  1. Resolve the style provider, falling back to the top-level default.
814
506
  2. Choose `promptByStyle[styleId]` when present, otherwise `prompt`.
@@ -865,6 +557,21 @@ expand one asset into `outputs[]` with stable roles such as `tile-00` and
865
557
  filenames such as `terrain-tile-00.png`. Consumers should use roles rather than
866
558
  assuming array position. See [tiles](./TILES.md).
867
559
 
560
+ ```jsonc
561
+ {
562
+ "styles": { "base": { "generator": "map", "outDir": "art/base" } },
563
+ "assets": {
564
+ "anvil": { "prompt": "an anvil" }, // art/base/anvil.png
565
+ "hammer": { "prompt": "a hammer", "category": "tools" }, // art/base/tools/hammer.png
566
+ "sign": { "prompt": "a shop sign", "file": "ui/shop-sign.png" } // art/base/ui/shop-sign.png
567
+ }
568
+ }
569
+ ```
570
+
571
+ A set keeps the same stem and adds the role: a character base `bot` in a
572
+ style with `outDir: "art/cast"` lands as `art/cast/bot-south.png`,
573
+ `bot-west.png`, and so on; a loop as `bot.walk-frame-00.png` onwards.
574
+
868
575
  ## Validation and editor setup
869
576
 
870
577
  Regenerate the checked-in schema after changing the Zod manifest types: