Start here
Open the sprite sheet maker, choose the original eight 16×16 PNGs and set their intended order. Use a four-column Grid with equal frame dimensions, then select Build sprite sheet. The result is 64×32; no resizing is needed.
One PNG sheet, a ZIP containing the same PNG and a manifest mapping each placed rectangle to its source. Recorded manual-order and padding exports match independently checked browser-decoded inputs.
What you need
Use assets/frames/frame-00.png through frame-07.png from the original CC 0 pack. Select only those eight for this exercise. The deliberate filename sequence is 3,0,7,1,6,2,5,4.
Choose 1–32 static PNGs, up to 10 MiB each and 64 MiB combined. Output geometry and a conservative memory budget also apply. One invalid source fails the complete assembly; animated PNGs are rejected.
Tested with: Chrome 154/macOS actual Next 16.3.8 manual-order and padding exports; separate bounded Cloudflare dependency replay. Node 24.22.0 references and independent Pillow 12.3.0 checks.
1. Put filenames in the intended sequence
Natural filename order sorts numeric runs, so frame-2 precedes frame-10. For this exercise choose Manual order and use Move up/Move down until the visible filenames read 03,00,07,01,06,02,05,04. The visible list numbers are positions, not source IDs.
Source indices come from the original selection. The saved run used byte-identical copies of originals 00–07 in that order under aliases such as frame10.png and 帧2.png. Its manual operation.order.sourceIndices is [3,0,7,1,6,2,5,4]. Manifest names can gain collision suffixes. If your chooser supplies a different selection order, resolve each index through sources[sourceIndex].name rather than equating it with a filename number.
2. Build four columns without changing frame size
Choose Grid, Columns 4 and Require equal frame dimensions. All eight originals are 16×16, so four columns and two rows produce 64×32. Select Build sprite sheet; Results offer the sheet PNG, ZIP and manifest.

A Horizontal strip uses one row; a Vertical column uses one column. Neither rescales the frames. A partially filled last row leaves transparent cells. The result preview is physically bounded to 128 px per edge; download the full PNG for import.
3. Make unequal-size handling explicit
The three inputs in assets/unequal/ are 12×13,16×16 and 11×14. Equal-size mode rejects them. Choose Pad to largest frame, Grid with three columns, then Top left or Center as the padding anchor. Each allocated cell is 16×16.
Centering uses floor((cell − source)/2) per axis, leaving an odd spare pixel on the right/bottom. It centers the full source rectangle, without trimming invisible borders or locating the visible artwork.

4. Read source identity and placed rectangles separately
Inspect the actual manual-order manifest. Its first output frame has frameIndex:0, sourceIndex:3 and sourceFrameIndex:0. The source is the fourth selected file; its frame index is 0 because each input PNG is one image. Do not read sourceFrameIndex as the numbered filename.
In the centered manifest, compare contentRect with allocatedRect. The 12×13 input occupies content(2,1,12,13) inside cell(0,0,16,16). The added padding is transparent black; it is not original artwork.
Rectangles use a top-left origin and half-open right/bottom edges. Compare every decoded content crop with its corresponding decoded input. Overall dimensions or a correct first frame alone would miss a swapped middle frame.
5. Keep actual exports separate from reference generation
The recorded actual PNG/ZIP/manifest outputs were checked against separate browser readbacks with independent Pillow crops, order and padding comparisons. Browser decoding can normalize invisible RGB and round partial-alpha channels; this does not establish source-file byte identity or preserve PNG metadata.
The original pack also reproduces independent file-level reference images and generic geometry. From its extracted folder run:
node --test fixture.test.mjs
node generate.mjs
node generate.mjs --checkThose nine Node cases verify reference ordering, strict rejection, padding, transparent trailing cells and CLI guards. They do not execute the browser worker or validate an arbitrary downloaded PNG. Keep originals and both manifests when moving frames between tools; neither PNG nor this geometry JSON supplies FPS, tags or engine import resources.
Use the splitter when frames begin inside a regular sheet. Use the batch upscaler before assembly if larger frame pixels are required. Changing the maker's column count only rearranges them.