pixelkiln 0.1.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.
@@ -0,0 +1,344 @@
1
+ # PixelLab API — measured reference
2
+
3
+ Every cost here was **measured against a live Tier 2 account**, not read from
4
+ documentation. Where a figure came from docs rather than a meter, it says so.
5
+
6
+ Source of truth for shapes: `https://api.pixellab.ai/v2/openapi.json`
7
+ (79 paths, 56 of them POST). A concise index lives at `GET /v2/llms.txt`.
8
+
9
+ Auth is one long-term key for everything: `Authorization: Bearer $PIXELLAB_API_KEY`.
10
+
11
+ ---
12
+
13
+ ## The API surface
14
+
15
+ | Family | Paths | What it covers |
16
+ |---|---|---|
17
+ | characters | 23 | 4/8-direction characters, portraits, outfit transfer, lip-sync |
18
+ | single images | 14 | one-off images, inpaint, resize, rotate, background removal |
19
+ | tiles / tilesets | 12 | top-down and sidescroller tilesets, isometric tiles |
20
+ | objects | 10 | 1- and 8-direction objects, states, animations |
21
+ | animation | 9 | skeleton and text-driven animation, interpolation |
22
+ | ui / fonts | 6 | UI panels, pixel fonts |
23
+ | account | 5 | balance, listing, tags, background jobs |
24
+
25
+ pixelkiln uses the **single images** and **objects** families. The rest are
26
+ listed so it is obvious what exists.
27
+
28
+ ---
29
+
30
+ ## Single-image generators — measured
31
+
32
+ The endpoints that produce one standalone image. This is the decision that
33
+ matters most for icon and prop work.
34
+
35
+ | Endpoint | Cost | Sync? | Palette | Style ref | Verdict |
36
+ |---|---|---|---|---|---|
37
+ | `create-image-pixflux` | **1** | sync (~10s) | **yes** | no | **Default for a fixed palette** |
38
+ | `create-image-pixen` | **1** | sync (~40s) | no | no | Best raw detail at 1 generation |
39
+ | `create-image-bitforge` | **1** | sync (~20s) | yes | yes | Best on paper, poor in practice |
40
+ | `map-objects` | **1** | job (~30s) | rejected | no | Fine when the prompt carries the style |
41
+ | `create-1-direction-object` | **20–40** | job | no | yes | Only when you need rotations |
42
+ | `generate-with-style-v2` (Pro) | **20** | job (~150s) | no | yes | Real style transfer |
43
+ | `generate-image-v2` (Pro) | **40** | job (~150s) | no | yes | Real style transfer |
44
+
45
+ `1dir` prices by canvas area: ≤1024 px = 20, ≤2048 px = 25, larger = 40. A
46
+ 64×64 icon therefore costs 40. Every other generator above is a flat 1–40
47
+ regardless of size.
48
+
49
+ ### What the measurements actually showed
50
+
51
+ **`pixflux` is the workhorse.** 1 generation, returns the PNG inline, and its
52
+ `color_image` parameter is a genuine constraint. A two-colour palette produced
53
+ 130 badges containing **only** `#000000` and `#ffffff` — verified 65/65 in each
54
+ of two sets. Prose asking for the same thing gave a yellow star and a brown
55
+ chocolate bar.
56
+
57
+ **`bitforge` disappoints.** It is the only single-image endpoint with palette
58
+ *and* style reference *and* synchronous delivery, all for 1 generation — which
59
+ should make it ideal. In practice it returned unrecognisable blobs for a simple
60
+ "blacksmith anvil" prompt at `style_strength` 0, 10, 25 and 50. The first
61
+ failure looked like the style reference overwhelming the prompt; sweeping the
62
+ strength proved otherwise. Possibly tuned for a different kind of subject (docs
63
+ call it "Create S-M image", max area 200×200). **Do not reach for it on this
64
+ evidence alone.**
65
+
66
+ **`pixen` gave the best unconstrained detail** of anything at 1 generation — a
67
+ properly rendered anvil with sparks where others produced flat shapes. It has no
68
+ palette parameter, so it suits styles carried entirely by the prompt.
69
+
70
+ **The Pro v2 endpoints do real style transfer**, taking labelled reference
71
+ images and matching them convincingly. At 20–40 generations and ~150s they are
72
+ for cases where that matters more than throughput.
73
+
74
+ **`color_image` is not universal.** `map-objects` accepts the parameter in its
75
+ schema but returns `500 cannot identify image file` for every payload shape
76
+ tried. The palette lock works on `pixflux` and `bitforge` only.
77
+
78
+ ### Payload shapes differ between generations of endpoint
79
+
80
+ Three incompatible conventions, which is a common source of 422s:
81
+
82
+ ```jsonc
83
+ // pixflux / bitforge / map-objects
84
+ { "color_image": { "type": "base64", "base64": "...", "format": "png" } }
85
+
86
+ // generate-image-v2 (Pro)
87
+ { "style_image": { "image": {...}, "size": {"width":64,"height":64},
88
+ "usage_description": "art style to match" } }
89
+
90
+ // generate-with-style-v2 (Pro)
91
+ { "style_images": [ { "image": {...}, "width": 64, "height": 64 } ] }
92
+ ```
93
+
94
+ A palette swatch must also be **chunky**: a 4×1 image was rejected outright with
95
+ "cannot identify image file", a 64×64 block swatch was accepted.
96
+
97
+ ---
98
+
99
+ ## Post-processing utilities — measured
100
+
101
+ Five endpoints take an image in and give one back. All are **synchronous**, all
102
+ return `{usage, image}` inline, and all cost **1 generation** — including the
103
+ ones that sound like pure image manipulation.
104
+
105
+ | Endpoint | Cost | Palette survives? | What it actually does |
106
+ |---|---|---|---|
107
+ | `remove-background` | 1 | **yes** | Genuine cleanup — the only safe one |
108
+ | `rotate` | 1 | no | Re-renders from a new angle |
109
+ | `resize` | 1 | no | **Re-generates**, does not resample |
110
+ | `image-to-pixelart` | 1 | no | Destroys alpha too |
111
+ | `inpaint` / `edit-image` | 1 | untested | Targeted edits |
112
+
113
+ Measured on one 64×64 riso badge with an exact 4-colour palette
114
+ (`#f4ecd8 #1c1a17 #c1553a #6b7f5e`, 57% transparent):
115
+
116
+ | | colours out | transparency | result |
117
+ |---|---|---|---|
118
+ | source | 4 | 0.57 | — |
119
+ | `remove-background` | **3** | 0.60 | dropped a stray fringe, kept the rest |
120
+ | `rotate` | 30 | 0.58 | anti-aliased new angle |
121
+ | `resize` → 32px | 38 | 0.57 | cream+rust came back **gold** |
122
+ | `image-to-pixelart` → 32px | **455** | **0.00** | opaque grey background |
123
+
124
+ **`remove-background` is a de-fringe pass, not just a matte.** It removed the
125
+ scattered sage-green speckles around the badge outline and left the three real
126
+ inks untouched — colour count went *down*, transparency went *up*. It is the one
127
+ utility safe to run on palette-locked art.
128
+
129
+ **`resize` is generative, not a resampler.** The name is misleading: it takes a
130
+ `description` as a *required* field, and it re-renders. A 4-colour cream-and-rust
131
+ badge came back as 38 colours of gold.
132
+
133
+ **`color_image` does not rescue it.** `resize` accepts the parameter in its
134
+ schema; passing the exact same swatch that `pixflux` honours changed nothing —
135
+ 56 colours, still gold. The forced palette works on **`pixflux` and `bitforge`
136
+ only**, and being in another endpoint's schema is not evidence it is wired up.
137
+
138
+ > **Never round-trip palette-locked art through `resize` or `rotate`.**
139
+ > Regenerating at the target size with `pixflux` costs the same 1 generation and
140
+ > the palette holds exactly.
141
+
142
+ **`image-to-pixelart` is for photographs and 3-D renders**, not for reprocessing
143
+ pixel art. It returned 455 colours on a 4-colour input and flattened the alpha
144
+ channel to an opaque grey field. There is no `no_background` parameter to
145
+ prevent that.
146
+
147
+ ---
148
+
149
+ ## Tilesets — schema only, not yet measured
150
+
151
+ Called out separately because these are the most capable endpoints for level art
152
+ and they are relevant to the disc-golf game. **Costs below are unmeasured.**
153
+
154
+ `POST /tilesets` (top-down) and `/tilesets-sidescroller` are the current
155
+ versions; `/create-tileset*` are the older aliases with identical schemas.
156
+
157
+ They take `lower_description` + `upper_description` (+ optional
158
+ `transition_description`) — you describe two terrains and the transition between
159
+ them, and get a tileset that blends them. Distinctively, they accept **both**
160
+ `color_image` *and* per-layer reference images (`lower_reference_image`,
161
+ `upper_reference_image`, `transition_reference_image`) — the only family that
162
+ combines palette forcing with style anchoring.
163
+
164
+ Useful knobs: `tile_size`, `tileset_adherence` / `tileset_adherence_freedom`
165
+ (how strictly tiles must fit together), `raggedness` and `slope_size` for edge
166
+ character, plus the usual `outline` / `shading` / `detail`.
167
+
168
+ `create-tiles-pro` is a different shape — a single `description` plus
169
+ `style_images`, `tile_view`, `building_*` fields for structures.
170
+
171
+ Its `style_images` is a **fourth** convention, and not the one two lines up:
172
+ `TilesProStyleImage` is flat, with all three fields required.
173
+
174
+ ```jsonc
175
+ // create-tiles-pro
176
+ { "style_images": [ { "base64": "...", "width": 32, "height": 32 } ] }
177
+ ```
178
+
179
+ Sending `generate-with-style-v2`'s nested `{image: {...}}` here is rejected as
180
+ an extra field. Passing style images at all makes the endpoint ignore
181
+ `tile_type` and `tile_view` and copy the reference's tile geometry instead —
182
+ which is the only way to land new art on an existing sheet's ground plane.
183
+
184
+ `GET /tiles-pro/{id}` carries no `status` field. It answers **423 while the set
185
+ is still drawing** and 200 with `storage_urls` when it is done, so the HTTP code
186
+ is the status. Cost is reported at submit time and lands on the same 20/25/40
187
+ canvas tiers as `1dir`, but the canvas is tile size x variation count, not one
188
+ sprite — a small tile in a large set still reaches the top tier.
189
+
190
+ All response shapes used by the client are validated at runtime. The TypeScript
191
+ interfaces alone are not trusted at the HTTP boundary: missing ids, invalid
192
+ object geometry, malformed tile URL maps, or balance fields with changed types
193
+ produce an `Invalid PixelLab response` error before pipeline state is updated.
194
+
195
+ `outline_mode` defaults to `outline`, which draws a dark border around every
196
+ tile. For ground tiles that is wrong: laid on a grid the borders read as
197
+ quilting, with a seam at every cell edge. `segmentation` omits them and the
198
+ same set tiles seamlessly — measured on a fairway-to-rough terrain set, where
199
+ it was the difference between usable and unusable.
200
+
201
+ `tile_feature` and `style_images` are **mutually exclusive** — "Connectable
202
+ features (roads/tileset/building) cannot be combined with style tiles". So the
203
+ geometry-anchoring trick above is unavailable for a connectable set, and its
204
+ tiles land on whatever ground plane the view angle implies (measured: 32x24
205
+ with the diamond midline at y=7, against a 32x32 sheet wanting y=15).
206
+
207
+ `tile_feature` turns it from independent variations into a connectable set:
208
+ `roads` (18-configuration path set), `tileset` (16-tile Wang corner set for a
209
+ terrain transition — describe it as the transition, not one terrain), and
210
+ `building` (floor/wall/doorway kit). These are sliced by index, so the returned
211
+ order is load-bearing. They are structural multi-output results, not candidates
212
+ to choose between: PixelKiln persists every URL in numeric order and labels the
213
+ downloaded outputs `tile-00`, `tile-01`, … .
214
+
215
+ The completed response also carries `tile_rules` for connectable sets. Its
216
+ placement contract is `rule_type` (`edge`, `corner`, or `outline`), `arity` (4
217
+ for square/isometric edges or corners, 6 for hex edges), `connectivity`
218
+ (`same`/`other`), two terrain labels, and a `tiles` map of provider tile key to
219
+ bitmask. Four-edge masks use N/E/S/W as bits 0/1/2/3. Four-corner masks use
220
+ NW/NE/SW/SE as bits 3/2/1/0. Tiles absent from that map are stamp-only. PixelKiln
221
+ stores the complete object in `providerMetadata.pixellab.tileRules`; exporters
222
+ normalize the documented subset but retain the raw object in the generic file.
223
+
224
+ Given that `color_image` is honoured on `pixflux` but silently ignored on
225
+ `resize`, **verify the palette actually holds on a single tileset before
226
+ committing to a set**.
227
+
228
+ ---
229
+
230
+ ## Recipes
231
+
232
+ **Lock a palette.** Use `pixflux` with a `color_image` swatch. Build the swatch
233
+ with `paletteSwatch()` — 64×64 blocks, one band per colour. It is a real
234
+ constraint, not a hint: 130 badges across two sets came back containing *only*
235
+ the requested colours.
236
+
237
+ **Re-roll one bad asset.** 1 generation. `gen --only <id> --force`. Cheaper than
238
+ asking for more candidates — a `1dir` call that yields 16 candidates costs 20–40.
239
+
240
+ **Clean up fringing.** `remove-background` at 1 generation, and it will not
241
+ disturb the palette.
242
+
243
+ **Change the size of an existing asset.** Regenerate with `pixflux` at the new
244
+ size. Do not use `resize`.
245
+
246
+ **Anchor to an existing look rather than a palette.** `generate-with-style-v2`
247
+ (20) or `generate-image-v2` (40). `bitforge` claims to do this for 1, but see
248
+ above. `map` has no style anchoring at all — a 1-bit set generated through it
249
+ came back with a yellow star and a brown chocolate bar.
250
+
251
+ **Write prompts for a monochrome style.** Strip colour words from the prompt
252
+ itself. "a golden trophy" fights a two-tone palette; the neutral noun does not.
253
+ pixelkiln's `promptByStyle` exists for exactly this.
254
+
255
+ **Never let a device or medium name lead a prompt.** `"Original Game Boy DMG
256
+ handheld sprite:"` as a prefix produced drawings of handheld consoles across a
257
+ whole set. Put the medium in the *suffix*, after the subject.
258
+
259
+ **Probe an unfamiliar parameter freely.** Failed generations are not billed.
260
+
261
+ ---
262
+
263
+ ## Candidates
264
+
265
+ Only `create-1-direction-object` returns multiple candidates, and the count is
266
+ derived from size — it cannot be requested:
267
+
268
+ | size | candidates |
269
+ |---|---|
270
+ | ≤42 | 64 |
271
+ | ≤85 | 16 |
272
+ | ≤170 | 4 |
273
+ | >170 | 1 |
274
+
275
+ Cost is per call, so the candidates are free. Counterintuitively, generating
276
+ *smaller* yields *more* candidates for *less*. But "pick from 16" is poor value
277
+ once a 1-generation re-roll exists: forty re-rolls cost one `1dir` call.
278
+
279
+ ---
280
+
281
+ ## Object lifecycle
282
+
283
+ - `1dir` and `8dir` objects **persist** with stable public URLs.
284
+ - `map-objects` job records are documented to auto-delete after 8 hours. The
285
+ **image survives**: a March 2026 sprite still resolved from `/v2/objects` four
286
+ months on while `/v2/map-objects/{id}` returned 404. pixelkiln's `poll` checks
287
+ the objects collection on a 404 rather than writing the work off.
288
+ - `pixflux`/`pixen`/`bitforge` results are **not account objects** at all —
289
+ bytes come back inline and are never listed. `adopt` and `salvage` cannot see
290
+ them, and they cannot be tagged.
291
+
292
+ ---
293
+
294
+ ## Limits and billing
295
+
296
+ - **Submissions must be >2s apart.** Concurrent background jobs: Tier 1 = 8,
297
+ Tier 2 = 10, Tier 3 = 20.
298
+ - **Failed generations are not billed.** Two failed forced-palette attempts moved
299
+ the balance by zero, so probing an unfamiliar parameter is free.
300
+ - **Billing lags slightly.** A bitforge call read as cost 0 immediately after and
301
+ 1 a few seconds later. Trust the `usage` field in the response over an
302
+ immediate balance diff.
303
+ - `usage` is returned inline by the synchronous endpoints:
304
+ `{"type":"generations","generations":1}`.
305
+
306
+ ---
307
+
308
+ ## Not yet explored
309
+
310
+ Listed so the gaps are known rather than assumed away:
311
+
312
+ - `inpaint`, `inpaint-v3`, `edit-image`, `edit-images-v2` — targeted edits.
313
+ Both `inpaint` and `edit-image` accept `color_image`, but given that `resize`
314
+ accepts and ignores it, assume nothing until measured.
315
+ - `image-to-pixelart-pro` — takes only `image` + `description`, no size fields.
316
+ The non-Pro version is characterised above.
317
+ - the tileset family — schema documented above, costs unmeasured
318
+ - `create-isometric-tile`, `create-ui-asset`, `generate-font-pro` — job-based,
319
+ response shapes not in the simple `{usage, image}` form
320
+ - the character family (23 paths) — out of pixelkiln's scope by design
321
+
322
+ ---
323
+
324
+ ## Reproducing these numbers
325
+
326
+ ```bash
327
+ curl -s -H "Authorization: Bearer $PIXELLAB_API_KEY" \
328
+ https://api.pixellab.ai/v2/balance
329
+ ```
330
+
331
+ The count lives at **`subscription.generations`**, not at the top level — a
332
+ probe reading `.generations` gets `undefined` and silently reports every cost as
333
+ `NaN` rather than failing:
334
+
335
+ ```jsonc
336
+ { "credits": { "type": "usd", "usd": 0.0 },
337
+ "subscription": { "type": "generations", "status": "active",
338
+ "plan": "Tier 2: Pixel Artisan",
339
+ "generations": 1281.0, "total": 5000.0 } }
340
+ ```
341
+
342
+ Take a balance reading, make one call, wait a few seconds, read again. Prefer
343
+ the response's `usage` field where present — it is inline, exact, and immune to
344
+ the billing lag.
@@ -0,0 +1,170 @@
1
+ # Generator selection
2
+
3
+ Choosing the generator is the largest cost and capability decision in a
4
+ PixelKiln manifest. The figures here were measured against a live PixelLab
5
+ account; [ENDPOINTS.md](./ENDPOINTS.md) contains the detailed experiments.
6
+
7
+ ## Quick choice
8
+
9
+ | You need | Generator | Measured cost | Candidates/output |
10
+ |---|---|---:|---|
11
+ | Standalone prop or icon with arbitrary dimensions | `map` (default) | 1 generation | 1 |
12
+ | Exact fixed palette | `pixflux` | 1 generation | 1 inline image |
13
+ | Candidate variety, richer rendering, future rotation/animation | `1dir` | 20–40 generations | 4–64 by size |
14
+ | Ground tiles or connectable structural sets | `tiles` | 20–40 generations | variations or complete set |
15
+
16
+ Start with `map` unless a required capability points elsewhere. Forty `map`
17
+ re-rolls cost the same as one 64×64 `1dir` call.
18
+
19
+ ## `map`
20
+
21
+ `map` calls PixelLab's map-object endpoint. It is purpose-built for isolated
22
+ props, badges, and icons, supports arbitrary width/height, and costs a flat one
23
+ generation at every supported size.
24
+
25
+ Use it when:
26
+
27
+ - one result per prompt is acceptable;
28
+ - prompt prose can carry the style;
29
+ - exact palette conformance and reference-image transfer are not required.
30
+
31
+ It does not accept style images or a forced palette. On a measured 1-bit restyle,
32
+ switching from reference-anchored generation to `map` changed median palette
33
+ distance from 6.6 to 41.9; cheap output is not cheap when unusable.
34
+
35
+ ## `1dir`
36
+
37
+ `1dir` is the single-facing sibling of PixelLab's rotatable/animated object
38
+ pipeline. It is square and priced by canvas area:
39
+
40
+ | Pixel area | Cost |
41
+ |---:|---:|
42
+ | ≤1024 | 20 |
43
+ | ≤2048 | 25 |
44
+ | larger | 40 |
45
+
46
+ Candidate count is also size-derived and does not reduce price:
47
+
48
+ | Square size | Candidates |
49
+ |---:|---:|
50
+ | ≤42 | 64 |
51
+ | ≤85 | 16 |
52
+ | ≤170 | 4 |
53
+ | >170 | 1 |
54
+
55
+ Use `1dir` when candidate comparison, rendering detail, rotations, or animation
56
+ justify the price. Style reference images are its strongest consistency tool.
57
+ The largest reference determines output size, so references should already be
58
+ at the target resolution. Keep style references separate from live assets:
59
+ editing a reference intentionally invalidates every spec using it.
60
+
61
+ Candidate scoring is deterministic RGB palette-distance arithmetic. No LLM
62
+ selects the artwork; the local contact sheet exists for human judgment.
63
+
64
+ ## `pixflux`
65
+
66
+ `pixflux` costs one generation, returns inline without polling, and supports a
67
+ hard `palette` constraint:
68
+
69
+ ```jsonc
70
+ "gameboy": {
71
+ "generator": "pixflux",
72
+ "palette": ["#0f380f", "#306230", "#8bac0f", "#9bbc0f"],
73
+ "outDir": "art/gameboy"
74
+ }
75
+ ```
76
+
77
+ A palette prompt is a request; `pixflux`'s swatch is a constraint. Measured
78
+ two- and four-color outputs contained exactly the supplied colors.
79
+
80
+ Trade-offs:
81
+
82
+ - rendering is flatter than `1dir`;
83
+ - style reference images are unavailable;
84
+ - the same `color_image` payload does not work on `map`;
85
+ - inline results are temporarily cached between submit and fetch;
86
+ - `noBackground` defaults to true in PixelKiln, which is correct for sprites
87
+ but wrong for scenes and banners.
88
+
89
+ Set `noBackground: false` for full-bleed artwork. Prompt prose cannot undo a
90
+ post-generation background-removal step.
91
+
92
+ ## `tiles`
93
+
94
+ `tiles` calls the tile-pro endpoint and understands tile geometry and
95
+ connectable output sets.
96
+
97
+ ```jsonc
98
+ "ground": {
99
+ "generator": "tiles",
100
+ "tileSize": 32,
101
+ "tileType": "isometric",
102
+ "tileView": "low top-down",
103
+ "outlineMode": "segmentation",
104
+ "outDir": "assets/tiles/src"
105
+ }
106
+ ```
107
+
108
+ Independent variations enter the normal review-and-pick flow. A `tileFeature`
109
+ instead creates a structural set in provider order:
110
+
111
+ | Feature | Meaning |
112
+ |---|---|
113
+ | `roads` | 18-configuration connected path/road set. |
114
+ | `tileset` | 16-tile Wang-corner terrain transition. |
115
+ | `building` | Floor/wall/doorway/pillar/stair construction kit. |
116
+
117
+ Describe a `tileset` asset as the transition—“fairway grass to rough meadow”—
118
+ rather than one terrain. Every returned member is required, so PixelKiln
119
+ records numerical roles such as `tile-00` rather than treating them as choices.
120
+
121
+ Important constraints:
122
+
123
+ - Style mode copies geometry from `styleImages` and ignores `tileType` and
124
+ `tileView`. It is useful when new independent tiles must match an existing
125
+ ground plane.
126
+ - `tileFeature` and `styleImages` cannot be combined; PixelLab rejects it and
127
+ the manifest catches it before spending.
128
+ - Use `outlineMode: "segmentation"` for continuous ground. The provider default
129
+ outlines every tile, which produces dark seams when repeated.
130
+ - Six-edge and unknown adjacency families remain lossless in generic export but
131
+ are rejected by engine exporters that cannot map them safely.
132
+
133
+ See [tiles and engine exports](./TILES.md).
134
+
135
+ ## Style variants
136
+
137
+ Styles are namespaces. Add another style to re-derive the same asset ids into a
138
+ separate output directory and separate lock keys:
139
+
140
+ ```jsonc
141
+ "styles": {
142
+ "base": { "generator": "map", "outDir": "art/base" },
143
+ "neon": {
144
+ "generator": "1dir",
145
+ "size": 64,
146
+ "promptPrefix": "Neon-noir game icon: ",
147
+ "promptSuffix": ", magenta/cyan rim light, transparent background",
148
+ "styleImages": [{ "path": "art/style-refs/neon.png" }],
149
+ "outDir": "art/neon"
150
+ }
151
+ }
152
+ ```
153
+
154
+ Seed a prose-only variant with two or three assets, commit the best results as
155
+ dedicated references, then generate the remainder. Reference images control
156
+ inherent subject colors more reliably than prose alone.
157
+
158
+ ## Limits and post-processing
159
+
160
+ PixelLab submissions must be spaced more than two seconds apart and concurrent
161
+ background jobs are tier-limited. PixelKiln enforces both globally.
162
+
163
+ Image-in/image-out resize, rotate, and pixel-art conversion endpoints are
164
+ generative and can change a closed palette or alpha channel. They cost the same
165
+ as a fresh one-generation image. Regenerate at the target dimensions when
166
+ palette fidelity matters; background removal is the measured exception that
167
+ acts as a genuine de-fringe pass.
168
+
169
+ Animated eight-direction characters and their ZIP/engine-resource export are
170
+ outside the current library scope.
@@ -0,0 +1,170 @@
1
+ # Getting started
2
+
3
+ Pixelkiln turns a committed asset manifest into generated files with a
4
+ committed provenance lockfile. Planning, auditing, packing, mounting, and
5
+ exporting are local operations. Only generation, provider polling, candidate
6
+ selection, downloads, tagging, account adoption, salvage, purge, and balance
7
+ checks need provider access.
8
+
9
+ ## Requirements
10
+
11
+ - Node.js 20 or newer
12
+ - A PixelLab API key for provider-backed commands
13
+ - A repository checkout until the first npm release in
14
+ [issue #1](https://github.com/gfargo/pixelkiln/issues/1) is complete
15
+
16
+ From a checkout:
17
+
18
+ ```bash
19
+ npm ci
20
+ npm run pixelkiln -- help
21
+ ```
22
+
23
+ The rest of this guide uses `pixelkiln` for readability. In the checkout,
24
+ replace it with `npm run pixelkiln --`.
25
+
26
+ ## Using an agent
27
+
28
+ Install the repository's PixelKiln skill when an agent will help operate the
29
+ workflow:
30
+
31
+ ```bash
32
+ npx skills add gfargo/pixelkiln@pixelkiln
33
+ ```
34
+
35
+ The skill teaches compatible agents to plan first, preserve provenance, and
36
+ use explicit budgets. It does not grant permission to spend provider credits;
37
+ you still approve the generation command and its hard `--budget` ceiling. See
38
+ [Agent workflows](AGENTS.md) for the full safety contract and example prompts.
39
+
40
+ ## Start a new project
41
+
42
+ Copy the minimal manifest into the project root:
43
+
44
+ ```bash
45
+ cp examples/minimal/pixelkiln.manifest.json pixelkiln.manifest.json
46
+ ```
47
+
48
+ Set each style's generator, dimensions, prompt prefix/suffix, reference images,
49
+ and output directory. Then add one manifest entry per asset. Put the credential
50
+ in `.env.local` beside the manifest:
51
+
52
+ ```dotenv
53
+ PIXELLAB_API_KEY=...
54
+ ```
55
+
56
+ Before spending anything, validate and price the selected work:
57
+
58
+ ```bash
59
+ pixelkiln doctor
60
+ pixelkiln plan
61
+ pixelkiln plan --style base --only anvil,hammer
62
+ ```
63
+
64
+ Generate with a hard cap copied from the plan:
65
+
66
+ ```bash
67
+ pixelkiln gen --style base --budget 120
68
+ ```
69
+
70
+ The command submits jobs, polls them, opens the local review sheet when a
71
+ generator returns alternatives, downloads the selected images, and updates the
72
+ lockfile. In review, use Left/Right to browse every candidate, Enter to select,
73
+ 1–9 for direct choices, 0 to leave a row unresolved, and Up/Down to change
74
+ rows. Closing the sheet without applying discards no provider objects.
75
+
76
+ ![PixelKiln local candidate review UI](../website/public/review-ui-showcase.jpg)
77
+
78
+ The sheet is served only on localhost and selection remains human-controlled.
79
+ Only rows submitted with **Apply selections** are recorded; unresolved rows can
80
+ be reopened later with [`pixelkiln pick`](CLI.md#pick).
81
+
82
+ ## Start from existing art
83
+
84
+ Scaffold the manifest from PNGs, then reconcile exact file hashes with the
85
+ provider account:
86
+
87
+ ```bash
88
+ pixelkiln init --from assets/sprites --exclude characters,gifs --generator map
89
+ pixelkiln adopt --write-prompts
90
+ pixelkiln plan
91
+ ```
92
+
93
+ `init` leaves prompts empty rather than inventing provenance. `adopt` recovers
94
+ the original prompts for exact byte matches. Locally retouched files remain
95
+ `untracked`; they are not silently overwritten or scheduled for paid
96
+ regeneration.
97
+
98
+ ## Everyday workflow
99
+
100
+ ```bash
101
+ # Free: inspect drift, missing files, recovery, and estimated spend.
102
+ pixelkiln plan
103
+
104
+ # Optional safety and quality checks.
105
+ pixelkiln doctor --dry-run
106
+ pixelkiln audit --check --max-distance 35 --min-transparency 0.1
107
+ pixelkiln cache --check
108
+
109
+ # Generate only the intended slice.
110
+ pixelkiln gen --style neon --only anvil,hammer --budget 80
111
+
112
+ # Rebuild missing files from provider URLs or the local content cache.
113
+ pixelkiln restore
114
+ ```
115
+
116
+ Repeated `--style`, `--only`, `--claims`, and `--output-role` flags accumulate;
117
+ comma-separated values also work. Unknown flags are errors, so a misspelled
118
+ filter cannot accidentally widen a paid run.
119
+
120
+ ## Automation
121
+
122
+ Use machine-readable planning and auditing as build gates:
123
+
124
+ ```bash
125
+ pixelkiln plan --json --check
126
+ pixelkiln audit --json --check --max-distance 35 --max-colors 128
127
+ ```
128
+
129
+ Both commands exit nonzero when the selected state is unsafe. Provider-backed
130
+ pipeline stages also exit nonzero after partial failures or timeouts.
131
+
132
+ ## Recovery
133
+
134
+ - `restore` repairs missing generated outputs without buying new generations.
135
+ - `.pixelkiln/cache/` stores downloaded PNG bytes by SHA-256, so restoration can
136
+ still work after a temporary provider URL expires.
137
+ - `cache --check` verifies hashes and fully decodes cached PNG structure before
138
+ trusting recovery bytes, then validates the account object-hash cache.
139
+ `cache --prune` removes corrupt, partial, and unreferenced project content
140
+ plus invalid hash entries. It does not mistake
141
+ objects belonging to another project for disposable account data.
142
+ - `adopt` maps existing local files to exact remote objects.
143
+ - `salvage --claims <every-other-lockfile>` reviews remote objects no project
144
+ currently claims. `salvage` never deletes; `purge` is a separate confirmed
145
+ operation.
146
+ - Pixelkiln refuses to overwrite a file whose bytes differ from its recorded
147
+ hash. Resolve intentional hand edits explicitly.
148
+
149
+ ## What belongs in Git
150
+
151
+ Commit:
152
+
153
+ - `pixelkiln.manifest.json`
154
+ - `pixelkiln.lock.json`
155
+ - generated art and any derived sheets/export metadata your application uses
156
+ - `.pixelkiln.json` provenance companions written beside managed sheets,
157
+ mounted trees, and tileset exports
158
+
159
+ Do not commit:
160
+
161
+ - `.env` or `.env.local`
162
+ - `.pixelkiln/` caches, transaction journals, staging trees, and backups
163
+ - `pixelkiln.cache.json`
164
+
165
+ The caches are disposable performance and recovery aids. The manifest,
166
+ lockfile, output files, and provenance companions are the durable project
167
+ record. Lock output paths are manifest-relative, so that committed record
168
+ survives moving or cloning the project instead of pointing back to an old
169
+ machine's checkout. See [Artifacts and provenance](ARTIFACTS.md) for the
170
+ ownership and transaction model.