Piskel project guide
Move a supported Piskel project into Aseprite
Convert the supported Piskel model-version-2 project subset while preserving supported frame pixels, layers, opacity, and FPS timing.
A .piskel project can carry editable information that a GIF or PNG export has already flattened. The importer accepts a documented model-version-2 JSON subset and maps its supported layers and visible frames into SpriteProject.
Project file versus rendered export
The project contains a global canvas size, FPS, a list of layer records, chunk layouts, and embedded PNG sheets. A rendered PNG contains only one composited frame; a rendered GIF contains composited animation frames. Choose the project file when layer names, order, opacity, or visibility matter.
{
"modelVersion": 2,
"piskel": {
"name": "Synthetic walk",
"fps": 12,
"width": 16,
"height": 16,
"layers": ["{...string-encoded layer JSON...}"],
"hiddenFrames": []
}
}
How chunks become cels
Every layer entry is itself a JSON string. Its chunks contain rectangular layout[column][row] arrays and exact data:image/png;base64, images. Layout cells identify zero-based frame indexes. Across all chunks in one layer, every frame index must appear exactly once—no gaps, duplicates, negative values, or ragged columns.
After local PNG decoding, the sheet dimensions must equal the project frame size multiplied by the chunk layout. The importer slices each cell into a full-canvas RGBA cel at (0, 0). The supported legacy form with one top-level base64PNG is normalized as a horizontal sheet, but a layer containing both legacy and chunk forms is rejected as ambiguous.
Layers and hidden frames
Layer array order is preserved. Names are required, opacity defaults to 1 and maps to 0–255, and an explicit boolean visibility value is preserved. Generated IDs such as piskel-layer-0 provide stable internal identity; source layers are not merged or synthesized.
Supported hiddenFrames indexes are omitted from every layer, and remaining frames are reindexed contiguously. The importer accepts the known empty-string sentinels written by some Piskel exports but rejects ambiguous non-empty strings, duplicate indexes, out-of-range indexes, or a project in which every frame is hidden.
Timing behavior
This subset uses one finite positive FPS value for the whole project. It does not accept arbitrary per-frame durations. Every visible output frame receives:
durationMs = clamp(round(1000 / fps), 1, 65535)
For example, 12 FPS becomes approximately 83 ms per frame after whole-millisecond rounding. Project name and description are validated metadata but are not represented in the generated Aseprite file.
Compatibility is intentionally narrow
The importer rejects unsupported model versions, unknown fields outside the documented harmless expanded state, invalid layer JSON, inconsistent frame counts, external image URLs, malformed PNG data, incomplete frame coverage, or decoded sheet-size mismatches. This prevents a plausible-looking output from silently omitting source structure.
| Piskel input | Aseprite output |
|---|---|
| Canvas width and height | RGBA document canvas |
| Visible frame indexes | Contiguous timeline frames |
| Global FPS | Same normalized duration on every frame |
| Layer array | Same order, names, opacity, and supported visibility |
| Chunk frame pixels | Full-canvas cels on the matching layer |
| Project name and description | Not exported |
Prepare and verify
- Save a fresh model-version-2 project rather than renaming a flattened export.
- Check that important frames are not marked hidden and that every layer covers the same timeline.
- Convert with Piskel project mode and inspect the preview.
- In Aseprite, compare timing, visible frame count, layer order, names, opacity, transparency, and representative pixels.
- Keep the Piskel original; the conversion is editable but not a universal round trip.
Compare with the Pixilart guide only when you actually have a Pixilart file—the two JSON containers and layer reconstruction rules are substantially different.