Guardian Paper Doll Sprite Editor Design Document
A contract-first specification for a “Guardian” Paper Doll sprite editor that consumes Ink outputs and provides layer-based composition, selection tools, and pose-aware editing.
Core Principles
Ink is authoritative. The editor must consume Ink outputs and must not re-implement Ink’s conversion rules in JavaScript or PHP.
- Ink owns conversion: CSV→JSON, planar→chunky, and BSV→PNG/JSON.
- The editor owns intent: layering semantics, selection/editing tools, and composition state.
- No semantic guessing: “Looks identical” does not imply “is identical.”
- Preserve authored structure: do not collapse frames, auto-infer missing poses, or deduplicate layers.
This spec intentionally draws hard lines around common implementation foot-guns: re-implementing Ink conversions, confusing pose frames with palette animation frames, and auto-deduplicating semantically distinct slots.
Sprite Sources & Formats
Guardian assets are sourced from MEM/MOM tables and associated sprite files. The editor must treat MOM/MEM as authoritative metadata and treat Ink as the authoritative transformer/renderer for supported formats.
Supported Sprite Formats
.08v– character/creature frames referenced by MOM entries (source of pose frames)..08x/.08y– planar sprites; Ink converts planar → chunky..bsv– binary sprites; Ink can output PNG or JSON per request.
Important Naming/Parsing Rules
- Underscore (
_) and dash (-) suffixes are not animation frames. -
Instead, they denote layer mask semantics used in paper-doll composition:
_→ OR (color overwrite)-→ AND (transparency mask)
Pose Frames vs Palette Frames Critical Distinction
Pose Frames (Character Animation)
Pose frames are the logical character/creature pose slots. Pose indices range from 0..3.
- Pose indices are semantic slots. They are not guaranteed unique image data.
- Pose frames may duplicate. Any pose index may reference the same underlying frame data as another.
- Duplication is valid and must be preserved. The editor must not collapse or infer poses based on pixel equality.
Example: pose 1 and pose 3 may resolve to the same underlying sprite data. This is
intentional and must round-trip correctly.
Playback Semantics
Playback rules are example behavior, not an enforced limitation. A typical Guardian playback may be:
0 → 1 → 2 → 1. Pose 3 remains a valid addressable slot regardless of whether it is
used in the default playback.
Palette Frames (Color Animation)
Palette animation is controlled by Ink’s &frame=. Palette frames range from 0..3 and
represent animated color states. Palette frames are visual-only and independent of pose frames.
Do not conflate pose frames with palette frames. Pose is “which pose slot is active.” Palette frame is “which palette-cycle frame is being previewed.”
Naming Contract (Mandatory)
| Identifier | Range | Meaning |
|---|---|---|
poseIndex |
0..3 |
Character pose slot / animation frame index |
palFrame |
0..3 |
Palette animation frame (Ink &frame=) |
BSV JSON Contract Editable Canonical Form
When Ink emits BSV as JSON, it must be interpreted as the canonical editable representation for pixel tools, selection masks, and composition operations.
- Row
0:[width, height] - Rows
1..: pixel rows - Pixel values:
0..255→ palette index-1→ transparent
{
"rows": [
[64, 64],
[ -1, -1, 12, 12, ... ],
[ -1, 34, 34, 12, ... ],
...
]
}
Transparency is represented by -1. This value is authoritative for masking, selection, and composition rules.
Palette Model
Palettes are treated as 256-entry tables with 4 animated frames per entry. Each palette entry contains multiple representations (RGB + hex string).
Expected Palette JSON Shape
- 256 entries, indexed
0..255 - 4 palette frames per entry, indexed
0..3 - Each frame provides:
[r, g, b, "#RRGGBB"]
{
"pal": [
[
[ 0, 0, 0, "#000000" ],
[ 0, 0, 0, "#000000" ],
[ 0, 0, 0, "#000000" ],
[ 0, 0, 0, "#000000" ]
],
...
]
}
Palette animation (palFrame) is purely visual and must not modify underlying pixel indices. Pixel indices remain
0..255 and reference palette entries; only the displayed RGB changes across palette frames.
Layer Masks & Composition
Paper-doll composition is defined by an explicit ordered stack of layers. Layers can include “mask semantics”
conveyed by filename suffixes _ and -.
Layer Operations
_(OR / color overwrite): overwrite color indices wherever the layer is non-transparent.-(AND / transparency mask): apply transparency masking to the composite using the layer’s transparency.
Composition Rules
- Explicit order matters. Evaluate layers in stack order (top-to-bottom or bottom-to-top as specified by the editor UI).
- Clipping: out-of-bounds pixels are clipped; do not expand canvas implicitly.
- Transparency is authoritative:
-1means transparent and participates in AND masking semantics.
This document intentionally separates “how Ink renders a single sprite” from “how the editor composes multiple sprites.” Ink renders inputs; the editor applies stacking, offsets, and mask semantics.
Selection Tools
The editor must include selection tooling suitable for sprite editing and paper-doll layer authoring.
Required Tools
- Pixel selection (single pixel / brush selection)
- Flood fill selection
- Selection push / nudge (translate selected pixels)
- Select mode and deselect mode (e.g. “Add” vs “Subtract”)
Default Matching Rules
- Exact palette index match for flood fill (discrete, not tolerant).
- Optional mode: transparent-only fill (
-1). - No tolerance / fuzzy matching unless explicitly added later.
Scope Rules (Important)
- Selection operations apply to the active layer.
- Selection operations apply to the current poseIndex.
- Selection must not be derived from the fully composited result unless the editor explicitly offers “composite select” as a separate mode.
Composition Definition Schema Round-trip Safe
Even if server persistence is optional, the editor must have a stable internal JSON definition for a paper-doll composition. This supports undo/redo, exporting/sharing later, and prevents redesign.
{
"base": {
"file": "Guardian/char/hero.08v",
"poseIndex": 0
},
"layers": [
{
"file": "Guardian/gear/helmet.bsv",
"op": "_",
"offset": { "x": 0, "y": 0 },
"visible": true,
"lock": false,
"name": "Helmet"
},
{
"file": "Guardian/gear/visor-mask-.bsv",
"op": "-",
"offset": { "x": 0, "y": 0 },
"visible": true,
"lock": false,
"name": "Visor Mask"
}
],
"palette": {
"set": "Guardian",
"palFrame": 0
},
"view": {
"zoom": 8,
"grid": true,
"checker": true
}
}
Pose Duplication Requirement
The schema must preserve poseIndex as a semantic slot. If poses are duplicated in the underlying asset,
the editor must still allow editing and addressing any pose 0..3 without deduplication.
Rendering & Performance Expectations
When to Request PNG vs JSON
- Request Ink-rendered PNG for thumbnails and fast previews.
- Request Ink-rendered JSON (BSV JSON contract) for editing, selection, and composition operations.
Caching Guidance
- Cache palette JSON (256×4 entries) aggressively.
- Cache MEM/MOM tables; do not refetch per tick.
- Avoid per-frame network activity during playback; prefetch pose frames used for preview.
The editor should remain “sprite-editor responsive” even when Ink is remote. This implies prefetching/caching, minimizing conversions during interaction, and rendering locally once JSON is loaded.
Backend Expectations (Optional)
Server persistence is optional. However, if implemented, the backend should accept and return the composition schema as compact JSON, potentially with additional metadata for authoring, sharing, and versioning.
Optional Endpoints
POST /paperdolls/save– save schema JSON, return an id / slugGET /paperdolls/load?id=...– load schema JSON by idGET /paperdolls/export?id=...&format=png– export a composed PNG (either server-side or client-driven)
Even with persistence, the backend should not “reinterpret” layering semantics. It should store and return the editor’s schema as authored. Ink remains the transformer; the editor remains the compositor.