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.
- package/CONTRIBUTING.md +92 -0
- package/LICENSE +21 -0
- package/NAMING.md +87 -0
- package/PROVIDERS.md +96 -0
- package/README.md +333 -0
- package/SECURITY.md +38 -0
- package/bin/pixelkiln.js +2 -0
- package/dist/cli.d.ts +47 -0
- package/dist/cli.js +5889 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +5129 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2679 -0
- package/dist/index.d.ts +2679 -0
- package/dist/index.js +4990 -0
- package/dist/index.js.map +1 -0
- package/docs/AGENTS.md +64 -0
- package/docs/ARCHITECTURE.md +120 -0
- package/docs/ARTIFACTS.md +162 -0
- package/docs/CLI.md +248 -0
- package/docs/ENDPOINTS.md +344 -0
- package/docs/GENERATORS.md +170 -0
- package/docs/GETTING_STARTED.md +170 -0
- package/docs/LIBRARY.md +157 -0
- package/docs/MANIFEST.md +162 -0
- package/docs/QUALITY.md +107 -0
- package/docs/README.md +44 -0
- package/docs/RECOVERY.md +124 -0
- package/docs/TILES.md +118 -0
- package/examples/minimal/README.md +26 -0
- package/examples/minimal/pixelkiln.manifest.json +21 -0
- package/package.json +100 -0
- package/schema/manifest.schema.json +255 -0
- package/skills/pixelkiln/SKILL.md +51 -0
- package/skills/pixelkiln/agents/openai.yaml +5 -0
|
@@ -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
|
+

|
|
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.
|