pixelkiln 0.87.0 → 0.89.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
@@ -64,13 +64,15 @@ not merely a label edit.
64
64
  | `source` | string | Manifest-relative committed art used instead of generation. For a set of PNG outputs (a `frames` animation, a `tiles` set) a path that is not a file is the stem of a hand edit laid beside the generated members (`<stem>-<role>.png` each); the set still generates. Mutually exclusive with `revision`. |
65
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. |
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
- | `revision` | object | Controlled dependency on another asset: `image-to-image`, `inpaint`, or `outpaint`; on PixelLab also `reduce-colors`, `correct-pixelart`, `remove-background`, 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). |
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
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). |
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). |
73
+ | `vfx` | object | `vfx` styles, PixelLab only: the effect to render. `{ "preset": "explosion", "parts": [{ "name": "scorch_mark" }], "sprite": "art/chars/hero.png", "fps": 12 }`; start from one `preset` or one `recipe` (a recipe JSON or a `.vfx.json` record), with optional `parts`, `sprite`, `fps`, and `name`. Or give a `description` (with optional `artist`: `lite` or `pro`, a local PNG `sprite`, and `name`) for PixelLab's paid VFX artist instead; it excludes `preset`, `recipe`, `parts`, and `fps`. Replaces `prompt` as the required field. See [`vfx`](./GENERATORS.md#vfx) and [Described effects](./GENERATORS.md#described-effects-the-paid-vfx-artist). |
73
74
  | `scene` | object | `map` styles, PixelLab only: draw this object into a scene image, in its style. `{ "image": "scene.png", "placement": { "oval": 0.3 } }`; `placement` is `oval` or `rectangle` (a fraction, 0.05–0.95) or `mask` (a PNG the size of the scene). See [Drawing into a scene](./GENERATORS.md#drawing-into-a-scene). |
75
+ | `connect` | object | `terrain` styles, PixelLab only: share a terrain with another set in the same style, keyed by this set's own side. `{ "lower": { "of": "coast", "terrain": "upper" } }` makes this set's lower terrain `coast`'s upper. The other set generates first; regenerating it makes this one stale. A sidescroller set connects only `lower`. Cycles are rejected. |
74
76
  | `batch` | object | `1dir` styles: rides along on another `1dir` asset's `item_descriptions` submission instead of generating on its own — `{ "of": "<leader asset id>", "index": <1-based slot> }`. See [`1dir`](./GENERATORS.md#1dir). |
75
77
  | `pieces` | array | `uiAsset` styles: exact shape regions (`rounded_rect`/`circle`/`polygon`) the panel is composited from, each with a unique `id`. See [`uiAsset`](./GENERATORS.md#uiasset). |
76
78
  | `elements` | string array | `uiAsset` styles: named, auto-positioned UI scaffolds (`button`, `health_bar`, `window`, ...). Combine with `pieces`; omit both for a plain full-canvas panel. See [`uiAsset`](./GENERATORS.md#uiasset). |
@@ -79,7 +81,7 @@ not merely a label edit.
79
81
  | `concept` | string | `character` styles, `pro` bases: a manifest-relative concept image (up to 1024px) the design is seeded from. |
80
82
  | `styleCharacter` | string | `character` styles, `pro` bases: this base's style anchor, over the style's. |
81
83
  | `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. |
82
- | `styles` | string array, `[]` | Restrict the asset to named styles; empty means every style. |
84
+ | `styles` | string array, `[]` | Restrict the asset to named styles. Empty means every style of the asset's kind: `vfx` styles for an asset with a `vfx` block, every other style otherwise. |
83
85
  | `promptByStyle` | string map, `{}` | Replace only the asset prompt for a named style. |
84
86
  | `cell` | `[column,row]` | The grid cell this asset owns in a mounted sheet; see [stable-cell mounting](#stable-cell-mounting). |
85
87
  | `outputRole` | string | Select one structural output when a generator returns a set. |
@@ -119,7 +121,7 @@ One asset using the fields most projects reach for:
119
121
  |---|---|---|
120
122
  | `extends` | style id | Optional parent style. The child inherits resolved settings but must declare its own `outDir`. |
121
123
  | `provider` | top-level default | Provider registry id for this style. Assets cannot override it. |
122
- | `generator` | `map` | `map`, `1dir`, `pixflux`, `tiles`, `terrain`, `isometricTile`, `imagePro`, `character`, `objectPro`, `uiAsset`, `uiElement`, `imageProFlash`, or provider-specific `animation`/`frames`. See [GENERATORS.md](./GENERATORS.md). |
124
+ | `generator` | `map` | `map`, `1dir`, `pixflux`, `tiles`, `terrain`, `isometricTile`, `imagePro`, `character`, `objectPro`, `uiAsset`, `uiElement`, `imageProFlash`, `vfx`, or provider-specific `animation`/`frames`. See [GENERATORS.md](./GENERATORS.md). |
123
125
  | `outDir` | string, required | Output directory relative to the manifest. |
124
126
  | `promptPrefix` | `""` | Prepended to every participating asset prompt. |
125
127
  | `promptSuffix` | `""` | Appended to every participating asset prompt. |
@@ -130,7 +132,9 @@ One asset using the fields most projects reach for:
130
132
  | `shading` | string | PixelLab `map`: `flat shading`, `basic shading`, `medium shading`, or `detailed shading`. |
131
133
  | `detail` | string | PixelLab `map`: `low detail`, `medium detail`, or `high detail`. |
132
134
  | `seed` | integer | Deterministic provider seed where supported. |
133
- | `palette` | hex array, `[]` | The style's palette; `#` is optional. Sent to providers that take one (`pixflux`, Retro Diffusion, ComfyUI bindings). |
135
+ | `palette` | hex array, `[]` | The style's palette; `#` is optional. Sent to providers that take one (`pixflux`, `imagePro` as its target palette, Retro Diffusion, ComfyUI bindings). |
136
+ | `paletteName` | string ≤100 | `imagePro` only, needs `palette`: the palette's name (`Game Boy`, `PICO-8`), which PixelLab uses to help the look. |
137
+ | `maxColors` | integer 2–64 | `imagePro` only: at most this many colours in each candidate, drawn from `palette` when one is set. |
134
138
  | `enforcePalette` | boolean, `false` | Snap every downloaded PNG to the nearest `palette` colour, no dithering. Needs at least two colours. See [Palette enforcement](#palette-enforcement). |
135
139
  | `noBackground` | boolean, `true` | `pixflux` background removal. Set false for scenes/backdrops. |
136
140
  | `providerOptions` | object, `{}` | Options grouped by provider id. Only the active provider's object is resolved and hashed. |
@@ -139,6 +143,19 @@ One asset using the fields most projects reach for:
139
143
  | `tileView` | enum | `top-down`, `high top-down`, `low top-down`, or `side`. |
140
144
  | `tileFeature` | enum | Connectable `roads`, `tileset`, or `building` structural set. |
141
145
  | `outlineMode` | enum | `outline` or `segmentation`; segmentation avoids quilted ground seams. |
146
+ | `terrainOrientation` | `top-down` | `terrain` only. `sidescroller` makes a platform set: prompt `1). <platform body> 2). <top layer>`, 16 or 32px, no mode, view, or shape controls. |
147
+ | `terrainMode` | `standard` | `terrain` only. `standard` or `pro`. |
148
+ | `terrainTileSize` | `16` | `terrain` only. 16, 32, or 64 (`pro` only). |
149
+ | `terrainTransitionSize` | `0` | `terrain` only. 0, 0.25, 0.5, or 1 (any 0–0.5 with `terrainShapeStyle`). Pro reads it as wall height; above 0.4 a Pro set has 25 tiles, standard only at 1. |
150
+ | `terrainView` | `high top-down` | `terrain` only. `low top-down` or `high top-down`. |
151
+ | `terrainShapeStyle` | | `terrain`, `standard` only. `square` or `round` procedural boundary. |
152
+ | `terrainRaggedness` | `0` | `terrain`, `pro` only. Boundary noise, 0–1. |
153
+ | `terrainRoundness` | 0.85 pro, 0.6 shape style | `terrain`, `pro` or `terrainShapeStyle`. Border corners: 0 square, 1 round, 2 cut at 45° (above 1 `pro` only). |
154
+ | `terrainBoundaryAmplitude` | `0` | `terrain`, `pro` or `terrainShapeStyle`. How far the border meanders, 0–0.3. |
155
+ | `terrainBoundarySeed` | `0` | `terrain`, with `terrainBoundaryAmplitude`. Which meander, an integer. |
156
+ | `terrainStepSlope` | `0` | `terrain`, `pro` at `terrainTransitionSize` 0.25 only. 0 a cliff to 2 a gentle slope. |
157
+ | `terrainConnectDiagonals` | `false` | `terrain`, `pro` only. Join the diagonal tiles' upper corners into one area. |
158
+ | `terrainSpreadX` / `terrainSlopeSize` | | Deprecated: PixelLab ignores both. Still accepted. |
142
159
  | `mode` | `standard` | `character` only. `standard`, `v3`, `pro`, or `pro-flash`; see [Characters](./CHARACTERS.md). |
143
160
  | `directions` | `8` | `character` only. `4` or `8`; every engine but `standard` draws 8. |
144
161
  | `template` | `mannequin` | `character` only. Body template: `mannequin`, or `bear`, `cat`, `dog`, `horse`, `lion`; `custom` for `pro-flash`. |
@@ -242,6 +259,10 @@ Generator-specific fields are validated before planning. Important constraints:
242
259
  or style images. `enforcePalette` still holds its output to the palette.
243
260
  - `pixflux` accepts a forced `palette` and returns inline output; it has no
244
261
  style-image support.
262
+ - `imagePro` sends `palette` (2 to 256 colours) as PixelLab's beta target
263
+ palette: every pixel of every candidate is one of those colours. Add
264
+ `paletteName` to name it and `maxColors` to cap each candidate's colours.
265
+ The cost is unchanged.
245
266
  - `tiles` cannot combine `tileFeature` with `styleImages` because the provider
246
267
  rejects connectable features in style-tile mode.
247
268
  - ComfyUI `frames` derives its count from one 2–64 item `providerInputs`
@@ -471,13 +492,14 @@ their own page: [Characters](./CHARACTERS.md) and
471
492
 
472
493
  | Revision field | Type | Meaning |
473
494
  |---|---|---|
474
- | `mode` | enum, required | `image-to-image`, `inpaint`, `outpaint`, `reduce-colors`, `correct-pixelart`, `remove-background`, `animate`, `animate-pixminimax`, `animate-skeleton`, `interpolate`, or `edit-animation`. The selected provider must opt into the mode. |
475
- | `from` | asset id, required | Parent asset, in this asset's style unless `fromStyle` names another. Self-references, unknown ids, and cycles are rejected. A parent written as a set (a character's directions, an animation's frames) is read as every member: `reduce-colors`, `correct-pixelart`, and `edit-animation` take the whole set in one call; the other modes refuse it. See [Revising a whole set at once](./REVISIONS.md#revising-a-whole-set-at-once). |
495
+ | `mode` | enum, required | `image-to-image`, `inpaint`, `outpaint`, `reduce-colors`, `correct-pixelart`, `remove-background`, `normal-map`, `animate`, `animate-pixminimax`, `animate-skeleton`, `interpolate`, or `edit-animation`. The selected provider must opt into the mode. |
496
+ | `from` | asset id, required | Parent asset, in this asset's style unless `fromStyle` names another. Self-references, unknown ids, and cycles are rejected. A parent written as a set (a character's directions, an animation's frames) is read as every member: `reduce-colors`, `correct-pixelart`, and `edit-animation` take the whole set in one call, and `normal-map` takes an animation's frames as one strip but refuses a set of directions; the other modes refuse it. See [Revising a whole set at once](./REVISIONS.md#revising-a-whole-set-at-once). |
476
497
  | `fromStyle` | style id | The style the parent lives in, when it is not this asset's own. The parent must be an asset in that style, and styles may depend on one another but not in a cycle. See [Across styles](./REVISIONS.md#across-styles). |
477
498
  | `mask` | string | Manifest-relative PNG required for `inpaint`; rejected for the other modes. Its dimensions must match an available source. |
478
- | `strength` | number 0–1 | Workflow edit/denoise strength (`image-to-image`, `correct-pixelart`); rejected for `reduce-colors`, `remove-background`, `animate`, `animate-pixminimax`, `interpolate`, and `edit-animation`. Interpretation is provider- and model-specific. |
499
+ | `strength` | number 0–1 | Workflow edit/denoise strength (`image-to-image`, `correct-pixelart`); rejected for `reduce-colors`, `remove-background`, `normal-map`, `animate`, `animate-pixminimax`, `interpolate`, and `edit-animation`. Interpretation is provider- and model-specific. |
479
500
  | `numColors` | integer 2–256 | `reduce-colors` only. Target palette size; mutually exclusive with `paletteImage`. |
480
501
  | `removalTask` | `simple` or `complex` | `remove-background` only. `simple` (PixelLab's default) is faster for flat backgrounds; `complex` handles detailed edges. |
502
+ | `convention` | `opengl` or `directx` | `normal-map` only. `opengl` (PixelLab's default) is green up, for Godot, Unity, and Phaser; `directx` is green down, for Unreal. A `normal-map` revision is refused in a style with `enforcePalette` or `quality`; see [Normal maps](./REVISIONS.md#normal-maps). |
481
503
  | `paletteImage` | string | `reduce-colors` only. Manifest-relative image whose colors become the palette; unlike `mask`, no size relationship to the source is required. |
482
504
  | `dithering` | enum | `reduce-colors` only. `none` (default), `2x2`, `4x4`, or `8x8`. |
483
505
  | `ditheringStrength` | number 0–10 | `reduce-colors` only. Ignored when `dithering` is `none`. |
package/docs/PIXELLAB.md CHANGED
@@ -71,10 +71,11 @@ unapproved files. See [Manifest quality profiles](./MANIFEST.md#quality-profiles
71
71
  | `pixflux` | You need a closed palette or a full-bleed background | 1 generation |
72
72
  | `1dir` | You need references or several candidates for human review | 10 to 25 generations |
73
73
  | `tiles` | You need ground variations or a connected structural set | 10 to 25 generations |
74
- | `terrain` | You need a two-terrain Wang tileset (grass to water, floor to cliff) | 10 to 25 generations (unmeasured) |
74
+ | `terrain` | You need a two-terrain Wang tileset (grass to water, floor to cliff), sets that share a terrain, or a sidescroller platform set | sidescroller 3 (measured); top-down Pro 15 or 25, standard up to 4 (PixelLab's quote) |
75
75
  | `isometricTile` | You need one standalone isometric tile, 16 to 64px | 1 generation (measured once) |
76
- | `imagePro` | You need a larger or non-square scene, up to 792 wide and 688 tall | 10 to 25 generations |
76
+ | `imagePro` | You need a larger or non-square scene, up to 792 wide and 688 tall, optionally held to the style's `palette` | 10 to 25 generations |
77
77
  | `imageProFlash` | You need a still on the Pro Flash model, optionally styled from one image, 16 to 256px | 5 to 9 generations (provisional quote) |
78
+ | `vfx` | You need an animated effect (spell, hit, explosion, aura), on its own or around a sprite | free from presets and parts; 1 to 28 when described to PixelLab's artist (lite measured at 1) |
78
79
  | `character` | You need a character in 4 or 8 directions, with states, loops, portraits, or outfits | See [Characters](./CHARACTERS.md) |
79
80
  | `objectPro` | You need a prop or creature with rotations, states, or loops but no character rig | about 6 at 64px (unmeasured) |
80
81
  | `uiAsset` | You need a UI panel composited from pieces and named elements, 192px and up | 20 generations (measured once) |
@@ -99,6 +100,91 @@ beside the submit-time `cost` estimate a wave budget spends against;
99
100
  `pixelkiln history`, `plan --json`, and the gallery show both when they
100
101
  differ (docs/ENDPOINTS.md, "Limits and billing").
101
102
 
103
+ ## What changed in PixelLab 0.4.130
104
+
105
+ PixelLab 0.4.130 (October 2026) added four things. PixelKiln covers each:
106
+
107
+ - **Tileset Pro.** Top-down Pro sets take roundness, connected diagonals, an
108
+ uneven boundary, and edge slope, and sets can share a terrain. These are
109
+ `terrain` style fields plus an asset's `connect`; see the
110
+ [manifest reference](./MANIFEST.md). Sidescroller sets use
111
+ `terrainOrientation: "sidescroller"`. The public API has no Pro mode for
112
+ sidescrollers yet.
113
+ - **A target palette for Create Image Pro.** An `imagePro` style's `palette`
114
+ now reaches PixelLab, which draws every pixel from it. `maxColors` caps
115
+ each image and `paletteName` names the palette.
116
+ - **VFX in the API.** A `vfx` block with a `description` asks PixelLab's
117
+ effects artist for the effect, at 1 generation and up. Presets, parts, and
118
+ recipes still render free on the workbench. See
119
+ [Generator selection](./GENERATORS.md#described-effects-the-paid-vfx-artist).
120
+ - **Normal maps.** A `normal-map` revision makes one for a sprite or a whole
121
+ loop at 0.5 generations, and `pixelkiln normal-map` does it for a loose
122
+ file. See [Controlled asset revisions](./REVISIONS.md#normal-maps).
123
+
124
+ Three have been billed live once each: a sidescroller set (3 generations),
125
+ a lite described effect (1, its quote), and an 8-frame normal map strip
126
+ (0.5). Top-down Pro prices and the palette use PixelLab's published
127
+ prices.
128
+
129
+ ## Learn from PixelLab's tutorials
130
+
131
+ PixelLab's YouTube channel teaches its web app, not PixelKiln, but most of
132
+ its advice carries over. Two recent videos changed what we recommend.
133
+
134
+ ### Pro tilesets
135
+
136
+ [PixelLab Pro Tilesets: Full Guide](https://www.youtube.com/watch?v=MbkqpDUFkEg)
137
+ covers the `terrain` generator's endpoint.
138
+
139
+ - End each terrain on the word "texture": `1). deep ocean water texture 2).
140
+ plain smooth sand texture`. Without it, Pro sometimes scatters rocks,
141
+ shells, or plants over the sand.
142
+ - Keep terrain prompts short and put style words in the style's
143
+ `promptPrefix` or `promptSuffix`. Color, season, and outline words
144
+ ("autumn", "frosty", "bold black outlines") change a set more than mood
145
+ words do.
146
+ - On Pro, `terrainTransitionSize` is wall height. 0 is flat ground, 0.25 a
147
+ low bank, and 0.5 or 1 a full wall in a 25-tile set.
148
+ - `terrainRaggedness` roughens the border; 0.8 gives a marsh edge.
149
+ `terrainSpreadX` and `terrainSlopeSize` no longer change anything, because
150
+ PixelLab's Pro pipeline stopped reading them.
151
+ - For clean 45° paths and coastlines, set `terrainConnectDiagonals: true`
152
+ and `terrainRoundness: 2`. That is the video's "Connect diagonal corners"
153
+ checkbox with roundness at 45°.
154
+ - To paint ocean, sand, and grass on one map, chain sets with `connect`:
155
+ the sand-to-grass set's lower terrain is the ocean-to-sand set's upper.
156
+ Every pair of terrains that touch needs its own set, or they meet with a
157
+ hard edge.
158
+ - `terrainOrientation: "sidescroller"` makes platform sets: `1). mossy stone
159
+ bricks texture 2). green grass texture`. The video's Pro sidescroller, at
160
+ 64px with a taller top layer, is only in PixelLab's web app.
161
+
162
+ ### Animated effects
163
+
164
+ [How to Make Animated Pixel Art VFX in PixelLab](https://www.youtube.com/watch?v=t8FIMv-8QpI)
165
+ generates effects instead of rendering them. The `vfx` generator is free and
166
+ renders the same pixels every time, so try it first. Generate when the
167
+ effect is a drawn thing or a character's own motion, like a water hammer
168
+ that bursts into a splash or a sword that turns into lightning.
169
+
170
+ 1. Draw a sheet of effects with one `imagePro` call. List one effect per
171
+ frame in the prompt; a 64×64 canvas returns 16 candidates to pick from.
172
+ 2. Animate the pick with an `animate-pixminimax` revision. Describe the
173
+ motion, not the look, and ask a loop to return to its first state.
174
+ 3. Pin an ending with `lastFrame` when the motion should land somewhere. An
175
+ ice crystal can grow from a few blue pixels, or a `"engine": "pro-flash"`
176
+ edit of the same sprite can be the ending it transforms into.
177
+ 4. Clean up. `driftThreshold` or a `reduce-colors` revision fixes
178
+ frame-to-frame color flicker, and `correct-pixelart` or `pixelkiln edit`
179
+ removes stray pixels. Check whether a loop's last frame repeats its
180
+ first.
181
+
182
+ Pad a growing effect onto a larger canvas before animating it, since a
183
+ revision animates its parent at the parent's own size. Long PixMiniMax clips
184
+ can also cost more than `plan` says: PixelLab prices a 40-frame 64×64 clip
185
+ at 6 generations, and PixelKiln estimates 3. Set `--budget` with that gap in
186
+ mind.
187
+
102
188
  ## Account workflows
103
189
 
104
190
  The PixelLab adapter supports `balance`, `adopt`, `salvage`, `tag`, and the
@@ -182,14 +268,15 @@ inpainting, whole-image editing, and skeleton-driven animation (posing a
182
268
  reference image frame-by-frame from a supplied keypoint skeleton, not a text
183
269
  description) are the `revision` asset shape, not the `character` generator;
184
270
  see [Controlled asset revisions](./REVISIONS.md). The same revision shape
185
- animates, interpolates, recolors, and cleans up any asset, a whole character
186
- or loop at once where the endpoint takes a set; `pixelkiln estimate-skeleton`
271
+ animates, interpolates, recolors, cleans up, and makes normal maps for any
272
+ asset, a whole character or loop at once where the endpoint takes a set; `pixelkiln estimate-skeleton`
187
273
  starts a skeleton file from an image. Pro Flash stills are the
188
274
  `imageProFlash` generator, and Pro Flash edit and inpaint are a revision's
189
275
  `"engine": "pro-flash"`. Object Creator is the
190
276
  `objectPro` generator, UI panels and elements are `uiAsset` and `uiElement`,
191
- and fonts and unzooming reference art are the standalone `pixelkiln font` and
192
- `pixelkiln unzoom` commands. Map Workshop
277
+ and fonts, unzooming reference art, and normal maps for loose files are the
278
+ standalone `pixelkiln font`, `pixelkiln unzoom`, and `pixelkiln normal-map`
279
+ commands. Map Workshop
193
280
  scene composition (placing objects and characters into a map, in-scene
194
281
  inpainting, export) is a separate PixelLab product surface this adapter does
195
282
  not model at all — including PixelKiln's own `map` generator, which is
package/docs/REVISIONS.md CHANGED
@@ -7,8 +7,9 @@ dependencies before submission, and records the lineage in the lockfile.
7
7
 
8
8
  The manifest and pipeline are provider-neutral. ComfyUI, PixelLab and Scenario implement
9
9
  it; PixelLab covers `image-to-image`, `inpaint`, `reduce-colors`,
10
- `correct-pixelart`, `remove-background`, `animate`, `animate-pixminimax`,
11
- `animate-skeleton`, `interpolate`, and `edit-animation`, not `outpaint` (its API has no
10
+ `correct-pixelart`, `remove-background`, `normal-map`, `animate`,
11
+ `animate-pixminimax`, `animate-skeleton`, `interpolate`, and `edit-animation`,
12
+ not `outpaint` (its API has no
12
13
  canvas-expansion endpoint). ComfyUI covers every mode generically except the
13
14
  five frame-set modes (`animate`, `animate-pixminimax`, `animate-skeleton`,
14
15
  `interpolate`, `edit-animation`), which need an ordered frame set its
@@ -57,9 +58,11 @@ input to the provider workflow, not a cross-model quality guarantee.
57
58
  committed asset's drawer and use "+ New revision" under "Revisions from this
58
59
  asset" to create a child of it. On a PixelLab sprite the mode picker offers
59
60
  `image-to-image`, `inpaint`, `animate`, `animate-pixminimax`, `interpolate`,
60
- `reduce-colors`, `correct-pixelart`, and `remove-background`; on a PixelLab
61
- frame set (or a character's directions) it offers `edit-animation`,
62
- `reduce-colors`, and `correct-pixelart`, which take the whole set. ComfyUI
61
+ `reduce-colors`, `correct-pixelart`, `remove-background`, and `normal-map`;
62
+ on a PixelLab frame set (or a character's directions) it offers
63
+ `edit-animation`, `reduce-colors`, and `correct-pixelart`, which take the whole
64
+ set, plus `normal-map` on an animation loop. `normal-map` is hidden in a style
65
+ that sets `enforcePalette` or `quality`. ComfyUI
63
66
  sprites get `image-to-image` and `inpaint`; other providers get
64
67
  `image-to-image`. The form asks only for the new asset id, the instruction or
65
68
  motion, and the mode's own options (strength, frames, fps, colours,
@@ -233,6 +236,59 @@ generation.
233
236
  or `complex` (slower, for detailed edges). `description` is an optional hint
234
237
  naming the foreground. Both are rejected on other modes, as is `strength`.
235
238
 
239
+ ### Normal maps
240
+
241
+ `normal-map` calls PixelLab's `/normal-map` and returns the parent's normal
242
+ map, so a game engine can light the sprite. The map is the parent's size: RGB
243
+ is the way each pixel faces, and alpha is 255 where the sprite shows and 0
244
+ elsewhere. It is a background job, and costs 0.5 generation per call,
245
+ charged only when the map is delivered (PixelLab's documented price; not yet
246
+ measured here).
247
+
248
+ ```jsonc
249
+ {
250
+ "styles": {
251
+ "lit": { "generator": "map", "outDir": "art/normals" }
252
+ },
253
+ "assets": {
254
+ "hero-walk-east-normals": {
255
+ "prompt": "normal map",
256
+ "styles": ["lit"],
257
+ "revision": { "mode": "normal-map", "from": "hero-walk-east", "fromStyle": "chars", "convention": "opengl" }
258
+ }
259
+ }
260
+ }
261
+ ```
262
+
263
+ - `convention` is `opengl` (PixelLab's default: green up, for Godot, Unity,
264
+ and Phaser) or `directx` (green down, for Unreal). It is rejected on other
265
+ modes and enters the spec hash only when set. `strength` is refused.
266
+ - The parent must be a PNG, 8 to 512 pixels per side; both are checked at
267
+ plan time.
268
+ - **A loop goes as one strip.** When the parent is an animation (its
269
+ `-frame-NN.png` members), every frame is laid left to right in one image
270
+ and sent with `frames: N`, up to 32. PixelLab shapes the first frame and
271
+ carries it to the rest, so the light holds steady across the loop instead
272
+ of flickering. The returned strip is cut back into `-frame-NN.png` files
273
+ under the parent's roles, keeping its playback rate. A whole loop is one
274
+ call, so it costs 0.5 too.
275
+ - **A character's directions are refused.** They are separate views, not
276
+ frames of one motion: sent as a strip, each would be shaped from the first
277
+ direction, and one call per direction would put several paid jobs behind
278
+ one lock entry with no way to recover half of them. Make one `normal-map`
279
+ revision per direction, each from an asset whose `source` is that
280
+ direction's file.
281
+ - **A normal map is lighting data, not art.** Its colours are directions, so
282
+ palette snapping or the quality gate's grid repair would wreck it. A
283
+ `normal-map` revision in a style that sets `enforcePalette` or `quality` is
284
+ refused at plan time; put it in a style without either (as `lit` above) and
285
+ name the parent's style with `fromStyle`. A `mirror` of a normal map is
286
+ refused too: flipping the pixels leaves every red channel pointing the old
287
+ way.
288
+
289
+ For a loose file outside the manifest, `pixelkiln normal-map` does the same
290
+ job (see [CLI](./CLI.md#normal-map)).
291
+
236
292
  ### Revising a whole set at once
237
293
 
238
294
  Both modes also take a parent that is a **set** rather than one image: a
@@ -272,6 +328,8 @@ its playback rate.
272
328
  rather than being sent as if it belonged.
273
329
  - Members must all be the same size. `reduce-colors`' 512×512 limit is for the
274
330
  whole call: sixteen 64×64 frames fit, five 256×256 do not.
331
+ - `normal-map` takes an animation loop as one strip but refuses a set of
332
+ directions (see [Normal maps](#normal-maps)).
275
333
  - Every other mode except `edit-animation` (below) reads exactly one image and
276
334
  refuses a set parent at plan time, naming the modes that take one.
277
335
 
@@ -660,6 +718,13 @@ further — there is nothing to ask.
660
718
  CLI-only-and-never-in-the-submission-path decision. Same unverified-live
661
719
  caveat as `animate`/`animate-pixminimax`, sourced from the live
662
720
  `animate_with_skeleton_v3` MCP tool schema rather than an observed call.
721
+ - `normal-map` calls `/normal-map` (PixelLab 0.4.130), a background job whose
722
+ map is `last_response.image`; a loop's frames go and come back as one
723
+ horizontal strip, cut into per-frame files on poll. See
724
+ [Normal maps](#normal-maps). Measured once through `pixelkiln normal-map`:
725
+ an 8-frame strip billed exactly 0.5 generations and came back the same
726
+ size with matching alpha (docs/ENDPOINTS.md). The revision mode shares
727
+ that request but has not run live itself.
663
728
 
664
729
  - The mask convention is fixed, not graph-defined like ComfyUI's: white marks
665
730
  the area to generate, black the area to preserve. There is no way to flip it.
package/docs/TILES.md CHANGED
@@ -37,9 +37,10 @@ renumbered by array position.
37
37
 
38
38
  ### The `terrain` generator's shape
39
39
 
40
- `terrain` (`/create-tileset`, [`docs/GENERATORS.md`](GENERATORS.md)) is a
41
- different PixelLab endpoint from `tiles`' `tileFeature: "tileset"` above, and
42
- its response carries no `tile_rules` bitmask at all. Instead each tile names
40
+ `terrain` (`/create-tileset`, or `/create-tileset-sidescroller` for a
41
+ sidescroller set; [`docs/GENERATORS.md`](GENERATORS.md)) is a different
42
+ PixelLab endpoint from `tiles`' `tileFeature: "tileset"` above, and its
43
+ response carries no `tile_rules` bitmask at all. Instead each tile names
43
44
  which terrain occupies its four corners directly:
44
45
 
45
46
  ```jsonc
@@ -50,12 +51,21 @@ which terrain occupies its four corners directly:
50
51
  "terrainTiles": [
51
52
  { "role": "tile-00-none", "corners": { "NW": "upper", "NE": "upper", "SW": "upper", "SE": "upper" } },
52
53
  { "role": "tile-01-nw-ne-sw-se", "corners": { "NW": "lower", "NE": "lower", "SW": "lower", "SE": "lower" } }
53
- ]
54
+ ],
55
+ // Each terrain's base tile id; a connected set sends one of these.
56
+ "terrainIds": { "lower": "<uuid>", "upper": "<uuid>" }
54
57
  }
55
58
  }
56
59
  }
57
60
  ```
58
61
 
62
+ A sidescroller set names its terrains `["central", "background"]` while its
63
+ corners still say `lower` (the platform) and `upper` (empty air; the
64
+ all-`upper` tile is blank). PixelKiln records the corner vocabulary,
65
+ `["lower", "upper"]`, as `terrainTypes` so masks match, and keeps
66
+ PixelLab's names as `providerTerrainTypes` beside `view: "side"`. In Tiled
67
+ and Godot the platform is terrain 0.
68
+
59
69
  `normalizeTerrainTiles()` (`src/pipeline/tileset-export.ts`) turns this into
60
70
  the same `NormalizedTileRules` shape `tileRules` would have produced directly:
61
71
  `ruleType: "corner"`, `arity: 4`, `terrains` from `terrainTypes`, and a mask
@@ -70,9 +80,15 @@ role — correct here because `terrainTiles` and the output list are pushed in
70
80
  the same loop, but worth knowing if either is ever reordered independently.
71
81
 
72
82
  A tile whose corner is `"transition"` (the extra ring PixelLab adds at
73
- `terrainTransitionSize: 1`) has no bit of its own in a two-terrain corner
83
+ `terrainTransitionSize: 1`, or 0.5 on Pro) has no bit of its own in a two-terrain corner
74
84
  mask; it is folded into whichever of `terrains[0]`/`terrains[1]` it isn't.
75
85
 
86
+ That 25-tile layout covers 21 distinct corner combinations. Four
87
+ wall-continuation tiles share their corners with a twin and differ only in
88
+ `pattern_4x4`, which PixelLab says is the field to match on. PixelKiln
89
+ stores each tile's `pattern4x4` but builds masks from `corners`, so each
90
+ twin pair gets the same mask.
91
+
76
92
  ## Command
77
93
 
78
94
  ```bash
@@ -205,7 +221,15 @@ isometric-corner-names bug; the counts alone did not.
205
221
  placement semantics would have to be invented.
206
222
  - A `terrain` asset's `"transition"` corners collapse into a two-color mask
207
223
  (see above); the cliff-tier 25-tile set exports, but its middle transition
208
- ring is not distinguished from the two named terrains in Tiled/Godot.
224
+ ring is not distinguished from the two named terrains in Tiled/Godot, and
225
+ its four wall-continuation twins export with duplicate masks.
226
+ - A `terrain` set exports alone. A connected set (`connect` in the
227
+ manifest, `lower_base_tile_id`/`upper_base_tile_id` in the API) shares a
228
+ terrain with another set, the way PixelLab's
229
+ [Pro tilesets guide](https://www.youtube.com/watch?v=MbkqpDUFkEg) paints
230
+ ocean, sand, grass, path, and cliffs on one map, but each set still
231
+ exports as its own two-terrain Tiled Wang set or Godot terrain set.
232
+ PixelKiln does not merge connected sets into one engine terrain set yet.
209
233
  - Godot edge/side masks on an isometric or oblique tile remain generic- or
210
234
  Tiled-only; see Godot 4 above.
211
235
  - Export never changes source PNGs or the lockfile.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixelkiln",
3
- "version": "0.87.0",
3
+ "version": "0.89.0",
4
4
  "description": "Manifest-driven pixel-art generation, review, recovery, and packaging with deterministic provenance.",
5
5
  "type": "module",
6
6
  "sideEffects": false,