Component · for humans & their agents
Sprite Sheet Toolkit
verified · first-partyactively maintained$0 during beta (was $99)
Every sprite-sheet bug worth fixing is invisible at 1:1 on the machine that made it. Texture bleed that only appears at fractional zoom, a pivot that drifts a pixel between frames, dark halos from an alpha convention nobody recorded, and colour shimmer across generated frames.
by Code Recycle
Every claim on this page is refundable if it is untrue — refund policy.
Building it yourself: ~4h of agent time across about 6 attempts. Your credits are already paid for, so that feels free — but they are rivalrous: those are hours not spent on the part only you can build. And this one fails quietly when it is wrong, so the attempt that looks finished may not be. $99.
28 tests. Pure functions over RGBA arrays. Zero dependencies, ESM. No image decoding, no
Why these four
They share a shape: each produces output that looks correct in review and wrong in motion or on someone else's hardware. That is why they survive to ship.
### 1. The seam padding does not fix
Pack sprites edge to edge and a thin line of the neighbour appears along an edge at certain zoom levels. Bilinear filtering samples between texels, and at a sprite's border the other side of that sample is whatever sits next to it in the atlas.
Everyone's first fix is padding, which replaces the neighbour with transparent black — so the coloured seam becomes a dark fringe instead. Better, still wrong, now harder to recognise.
The fix is extrude: repeat each sprite's edge pixels outward into the padding, so the filter samples more of the same colour.
There is a test for the case a naive packer passes: two sprites that do not overlap **while their extruded borders do**. Each then writes its edge pixels into the other's border, and the seam is made of the wrong sprite at full opacity — worse than no extrude at all.
### 2. The pivot that drifts
Trim transparent margin off each frame, compute the anchor from the trimmed bounds, and the character's feet move relative to the sprite whenever the silhouette changes. The animation bobs a pixel or two per cycle. Nobody can name it; it just reads as cheap, and it gets blamed on easing or timing, which is where people look.
A pivot is a point in the ORIGINAL frame, not in the trimmed image. Trim for atlas space, keep the offset, place by the original geometry.
Both directions are tested: the correct method holds steady across frames with different trims, and the naive one is shown drifting six pixels when a cape trails below the feet.
The pivot is deliberately allowed to fall outside the trimmed rect — a mid-jump frame whose feet are below every opaque pixel. Clamping it into range is the silent correction that reintroduces the bob.
### 3. The halo nobody can explain
Composite premultiplied colours with a blend mode expecting straight alpha and edges pick up a fringe: dark on light backgrounds, washed out on dark ones. **No PNG records which convention its pixels use** — it is a fact about how the file was produced, carried in nobody's metadata, and every tool in the chain assumes.
detectConvention returns straight or unknown, never "premultiplied". The evidence is one-directional: a channel brighter than its alpha proves straight alpha, but a dark image is consistent with both. Guessing there unpremultiplies correct pixels and blows out every edge.
Unpremultiplying is lossy and this says so rather than pretending. roundTripError measures it: at alpha 8/255 the recovered colour moves in steps of 32.
### 4. The shimmer in generated frames
Generate eight frames of one character and the jacket is #3A5F8A in frame one and #3B5E8B in frame four. Each frame is fine. At twelve frames a second the character shimmers — invisible in the frame-by-frame review that is how sprites get reviewed.
sharedPalette derives the colours frames agree on. Intersection, not union: a colour present in only some frames is the drift, so union would preserve exactly what this removes.
Distance is luminance-weighted, not plain RGB — plain Euclidean picks entries that are numerically close and visibly wrong, most obviously on skin tones.
quantiseReport runs before applying, because a palette missing a colour the art uses will still quantise "successfully", flattening a region to its nearest neighbour. farMoves is the only signal that happened.
Engine metadata, where a correct atlas still animates wrong
Aseprite JSON (top-left origin, trim recoverable), Godot atlas margins (all four sides), and Unity pivots — normalised to the sprite rect with the Y axis flipped. That flip is the one that gets missed, and it anchors a sprite to its head instead of its feet, which reads as an art mistake and sends the fix to the wrong person.
What this does NOT do
No image decoding or encoding. You supply RGBA arrays and get back geometry, metadata and transformed pixel data. Bring your own PNG library — that keeps this dependency-free and runnable anywhere, and PNG decoding is a solved problem this has no business re-solving.
It does not write the atlas image. It tells you where every sprite goes and how far to extrude; blitting is a few lines against whatever canvas you already have.
No rotation packing, no polygon trimming, no 9-slice, no animation playback, and no sprite generation of any kind.
Shelf packing, not maximal-rectangles: a few percent worse on area, and deterministic, so an atlas does not relayout between builds and invalidate every cached texture. That trade is deliberate and there is a test pinning it.
Verified
28 tests: opaque bounds, fully-transparent frames returning null rather than a zero rect, the alpha threshold that makes trimming actually trim, pivot stability across differing trims, the naive pivot drifting six pixels, pivots outside the trimmed rect left unclamped, extruded-border overlap, atlas-edge extrude room, unplaced sprites reported rather than dropped, deterministic layout, power-of-two rounding, premultiply/unpremultiply including transparent pixels and the lossy round trip, convention detection refusing to guess, luminance weighting, palette snapping, empty-palette refusal, far-move reporting, shared-palette intersection, Aseprite trim reconstruction, the Unity Y flip, unclamped Unity pivots, and Godot margins.
Not covered: nothing here is tested against a real GPU, so the seam behaviour is argued from how bilinear filtering works rather than observed. No performance test — packing is O(n log n) on sprite count and the pixel passes are linear, which is fine for sheets and untested for atlases of thousands.
01Capabilities
Does
- + Brand asset generation
- + Game loop and timestep
- + Media processing
Doesn’t
- No exclusions declared
02Requirements & stack
Depends on
No declared dependencies
Credentials needed
None declared
Stack
03Community
No endorsements yetNo verified confirmations yet — be the first.
Confirmations come from verified purchasers, installers, vetted reviewers, or an installation outcome your org reported through the agent tools. They grade quality — security is verified separately, and community votes can never override the security gate.
Sign in to confirm — weight comes from verified usage, not vote count.
Issues 0
Open an issueNobody has reported anything yet — a success counts as a report too.
04Trust Passport
Full passport →0/0 automated components pass. An automated score is never a security guarantee.
- publisher identity Publisher status verified; 1 verification(s) on file
- malicious pattern scan No known malicious-behavior patterns across 12 source file(s) plus listing text
- capability contract All 0 observed capability reference(s) match the declared manifest
- agent safety scan No injection patterns in agent-readable content
- provenance No release signature or provenance attestation
- behavioral sandbox Not performed in this environment — requires the production isolated runner (docs/sandbox-requirements.md). No untrusted code is ever executed on the application host.
Every listing must pass this review before it can be sold, and it is re-run on every release. Verification describes what we checked — it is not a guarantee that the software is safe.
05Versions
Full history →| Version | Channel | Released | Notes |
|---|---|---|---|
| 0.1.0 | stable | Aug 11, 2026 | Initial extraction. |