<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[FrameSprite Workflow Notes]]></title><description><![CDATA[FrameSprite Workflow Notes]]></description><link>https://framesprite-workflow.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Tue, 03 Nov 2026 15:32:40 GMT</lastBuildDate><atom:link href="https://framesprite-workflow.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Trim a Sprite Atlas Without Moving the Character's Anchor]]></title><description><![CDATA[Map an anchor from the original canvas into a trimmed sprite, then check atlas metadata and placement with a worked coordinate example.]]></description><link>https://framesprite-workflow.hashnode.dev/atlas-trim-pivot-handoff</link><guid isPermaLink="true">https://framesprite-workflow.hashnode.dev/atlas-trim-pivot-handoff</guid><category><![CDATA[Game Development]]></category><dc:creator><![CDATA[FrameSprite Workflow Team]]></dc:creator><pubDate>Wed, 28 Oct 2026 07:50:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6add6fc04b032073c0397857/be126b01-fcfb-4e15-b8c6-b85b32b30f18.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Removing transparent borders changes a sprite's stored rectangle. It need not change where the character stands, but that requires preserving the removed offset and interpreting it correctly. Before adjusting animation frames by eye, separate three coordinate spaces: the original canvas, the trimmed image, and the packed atlas.</p>
<p><em>Disclosure: This article was prepared with AI assistance by the FrameSprite Workflow Team. It explains a general atlas handoff, not a claim that our tools export trimming or pivot metadata. All dimensions and placement results below are hypothetical calculations, not a product or engine test.</em></p>
<p><strong>Key takeaways</strong></p>
<ul>
<li>Keep the original dimensions and the trimmed region's original offset.</li>
<li>State whether an anchor is measured against the original or cropped rectangle.</li>
<li>Let a compatible importer restore trimming, or compensate manually; do not apply both.</li>
</ul>
<h2>Keep the three rectangles separate</h2>
<p>The atlas rectangle answers where to sample pixels. The original size and trim offset answer where those pixels belong. PixiJS's <a href="https://pixijs.com/8.x/guides/components/textures">texture guide</a> distinguishes <code>frame</code>, <code>orig</code>, and <code>trim</code>; they are not interchangeable dimensions.</p>
<table>
<thead>
<tr>
<th>Space</th>
<th>Question it answers</th>
<th>Example below</th>
</tr>
</thead>
<tbody><tr>
<td>Original canvas</td>
<td>Where did the artist place the character and anchor?</td>
<td>128 × 128 pixels</td>
</tr>
<tr>
<td>Trimmed region</td>
<td>Which part remains, and where was it inside that canvas?</td>
<td>Offset (24, 20), size 80 × 96</td>
</tr>
<tr>
<td>Atlas</td>
<td>Where is that retained rectangle packed?</td>
<td>Offset (256, 64), size 80 × 96</td>
</tr>
</tbody></table>
<p>TexturePacker's <a href="https://www.codeandweb.com/texturepacker/documentation/custom-exporter">exporter reference</a> separately describes <code>cornerOffset</code>, <code>untrimmedSize</code>, and the packed <code>frameRect</code>. Its definitions also distinguish rotation and extrusion. Check the chosen export format rather than assuming these internal names appear verbatim in every JSON file.</p>
<h2>Read one frame before building the animation</h2>
<p>Here is an illustrative frame entry using fields present in the <a href="https://pixijs.download/v8.2.3/docs/spritesheet_Spritesheet.ts.html">PixiJS 8.2.3 spritesheet parser</a>. This is an excerpt, not a complete atlas file or an observed FrameSprite export. Assume no packing rotation, no resolution scaling, and a rectangular crop:</p>
<pre><code class="language-json">{
  "frame": { "x": 256, "y": 64, "w": 80, "h": 96 },
  "rotated": false,
  "trimmed": true,
  "sourceSize": { "w": 128, "h": 128 },
  "spriteSourceSize": { "x": 24, "y": 20, "w": 80, "h": 96 }
}
</code></pre>
<p>The retained region fits the original canvas: its right edge is <code>24 + 80 = 104</code>, and its bottom edge is <code>20 + 96 = 116</code>. Both lie within 128. Those are geometric edges, not inclusive pixel indices. The atlas position <code>(256, 64)</code> does not tell you how far the character stood from the original canvas edge.</p>
<pre><code class="language-text">Original canvas: 128 x 128
  origin (0,0)
      |
      +-- trim starts at (24,20)
          retained region: 80 x 96
          anchor within it: (40,92)

Same retained pixels are packed at atlas (256,64).
Packing location does not replace the trim offset.
</code></pre>
<h2>Convert the anchor once</h2>
<p>Choose a foot reference point at original-canvas coordinate <code>(64, 112)</code>. Here “anchor” means the image point that must land on a specified scene position. Use top-left origins, x increasing right, y increasing down, unit scale, and no rotation throughout this calculation.</p>
<p>Subtract the trim offset to express the same point inside the crop:</p>
<pre><code class="language-text">crop anchor x = 64 - 24 = 40
crop anchor y = 112 - 20 = 92
</code></pre>
<p>Suppose that point must land at scene position <code>(300, 240)</code>. The cropped rectangle's scene top-left must then be:</p>
<pre><code class="language-text">left = 300 - 40 = 260
top  = 240 - 92 = 148
</code></pre>
<p>Check the same result through the original canvas. Its top-left would be <code>(300 − 64, 240 − 112) = (236, 128)</code>. Adding the retained region's offset gives <code>(236 + 24, 128 + 20) = (260, 148)</code>. The two routes agree.</p>
<p>If you instead place the crop at <code>(236, 128)</code> without restoring the removed offset, the retained art moves 24 pixels left and 20 pixels up. Changing the atlas packing position cannot repair that placement error.</p>
<p>These equations describe translation only. Rotation, flips, parent transforms, resolution scaling, and a different destination coordinate system need their own explicit handling.</p>
<h2>Ask what a normalized anchor is relative to</h2>
<p>On the original canvas, the example point is <code>(64/128, 112/128) = (0.5, 0.875)</code>. On the crop, it is <code>(40/80, 92/96) = (0.5, 0.95833…)</code>. These describe the same point in different rectangles.</p>
<p>Reusing original-normalized y <code>0.875</code> against the crop's height gives <code>84</code>, not <code>92</code>: an eight-pixel mismatch. This is a useful diagnostic calculation, not an instruction to rewrite every imported anchor. A trim-aware loader may already retain the original dimensions and compensate correctly.</p>
<p>PixiJS's <a href="https://pixijs.download/v8.14.0/docs/rendering.TextureOptions.html">TextureOptions reference</a> includes original and trimmed rectangles alongside a default placement/rotation anchor. Verify how the actual sprite and loader use them. Do not add manual trim compensation to a pipeline that already restores it.</p>
<h2>Diagnose placement before repainting frames</h2>
<p>Use an untrimmed reference and a fixed scene marker. Keep the sprite's transform unchanged while switching frames, then compare the same intended body reference point. Natural movement in the drawing still exists; preserving canvas placement does not freeze an animated foot or create collision data.</p>
<table>
<thead>
<tr>
<th>Observation</th>
<th>Check first</th>
<th>Recovery</th>
</tr>
</thead>
<tbody><tr>
<td>Correct pixels, but one frame shifts</td>
<td>That frame's original size and trim offset</td>
<td>Restore matching metadata; compare with its untrimmed reference</td>
</tr>
<tr>
<td>Every frame is offset after a manual fix</td>
<td>Whether the loader already applies trimming</td>
<td>Remove duplicate compensation and retest the marker</td>
</tr>
<tr>
<td>The anchor changes when crop size changes</td>
<td>Which rectangle normalized values refer to</td>
<td>Convert against the declared rectangle, or retain the loader's original-space model</td>
</tr>
<tr>
<td>The atlas preview looks correct but import differs</td>
<td>Export mode, rotation flag, resolution and loader support</td>
<td>Select a supported contract; rebuild and reimport a small example</td>
</tr>
</tbody></table>
<p>TexturePacker's <a href="https://www.codeandweb.com/texturepacker/documentation/texture-settings">trim settings</a> distinguish preserving original placement from crop modes that change size or discard position. Similar-looking packed images can therefore carry different placement contracts. Inspect the data and importer together.</p>
<p>If the untrimmed reference already drifts, start with the <a href="https://framesprite-workflow.hashnode.dev/sprite-alignment-fixed-canvas-acceptance-test">fixed-canvas alignment review</a>. If the crop itself selects the wrong pixels, the <a href="https://framesprite-workflow.hashnode.dev/unity-sprite-slicing-cell-pivot-qa">Unity slicing and pivot review</a> addresses a separate rectangle-selection problem.</p>
<h2>Hand off enough information to repeat the check</h2>
<p>Keep the atlas and matching metadata together with the original source frames. Record the packer/export format, trim mode, rotation and scale settings, loader/version, anchor space, and a reference placement. Repacking may change atlas coordinates; changing the crop may change local coordinates. Neither should silently redefine the intended reference point.</p>
<p>An atlas handoff is usable when the receiving workflow samples the intended pixels and places them against the agreed reference. A smaller rectangle alone proves neither condition.</p>
<p><em>About the author: The FrameSprite Workflow Team documents game-asset workflows. <a href="https://www.framesprite.com/about#workflow-team">About the team</a> · <a href="https://www.framesprite.com/contact">Contact and corrections</a>. The cover is an AI-generated concept illustration, not a product screenshot or a diagram of this example.</em></p>
]]></content:encoded></item><item><title><![CDATA[Tile Atlas Margin, Spacing, and Extrusion: A Practical Import Check]]></title><description><![CDATA[Measure atlas cells, margins and spacing, then distinguish empty gaps from copied edge pixels. Diagnose progressive grid drift with a worked example.]]></description><link>https://framesprite-workflow.hashnode.dev/tile-atlas-margin-spacing-extrusion</link><guid isPermaLink="true">https://framesprite-workflow.hashnode.dev/tile-atlas-margin-spacing-extrusion</guid><category><![CDATA[Game Development]]></category><dc:creator><![CDATA[FrameSprite Workflow Team]]></dc:creator><pubDate>Wed, 28 Oct 2026 07:45:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6add6fc04b032073c0397857/256bdcf1-a86a-40da-a816-eae88e8a9361.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A tileset can look correct as an image and still produce cut-off tiles or colored seams in a game. The first problem is often an incorrect grid. The second can be texture sampling. Treat them as separate checks: <strong>margin and spacing describe where cells are; extrusion duplicates edge pixels to reduce sampling artifacts</strong>.</p>
<p><em>Disclosure: This article was prepared with AI assistance by the FrameSprite team, which operates the linked product. The examples are illustrative, not results from a benchmark.</em></p>
<h2>Key takeaways</h2>
<ul>
<li>Measure the source grid before changing filtering.</li>
<li>Extrusion duplicates border pixels; spacing identifies the distance between cells.</li>
<li>Test the atlas in the actual engine and camera.</li>
</ul>
<h2>Start with the dimensions you actually have</h2>
<p>For a regular tileset with equal outer margins on each side, image width is:</p>
<pre><code class="language-text">width = 2 × margin + columns × tile width + (columns − 1) × spacing
</code></pre>
<p>For example, four 32-pixel tiles, a two-pixel margin and four pixels between tiles require <code>2×2 + 4×32 + 3×4 = 144</code> pixels. If the first tile starts at x=2, the following tiles start at x=38, x=74 and x=110. The final tile ends immediately before x=142, leaving two pixels at the right edge. Coordinates here are zero-based.</p>
<p>Use the equivalent formula for height. It assumes a complete rectangular grid, uniform spacing, and symmetrical margins. A partially filled final row or irregular packing needs separate metadata; do not force it into this formula.</p>
<p><a href="https://doc.mapeditor.org/en/stable/manual/editing-tilesets/">Tiled's tileset documentation</a> describes fixed-size image tilesets with margin and spacing. Its <a href="https://doc.mapeditor.org/en/latest/reference/json-map-format/">format reference</a> defines margin as the buffer between the image edge and the first tile and spacing as the gap between adjacent tiles. An image collection is different: individual tiles refer to separate files, so one atlas grid may not apply.</p>
<h2>Why a gap and an extrusion are different</h2>
<p>Spacing is a measured region between cells; it may contain transparent pixels, duplicated edge colors, or other pixels. Extrusion copies a tile's border into a surrounding region. <a href="https://doc.mapeditor.org/en/stable/manual/editing-tilesets/">Tiled describes extruded border pixels as a way to avoid color bleeding</a>. It can help prevent a texture filter from sampling unrelated neighbors, but it does not repair a wrong tile size or incorrect grid offset.</p>
<p>Consider a horizontal slice of two adjacent cells in the example above. Each cell is 32 pixels wide; this picture is schematic and not drawn to scale:</p>
<pre><code class="language-text">image left                                                image right
|-- margin: 2 --|-- cell A: 32 --|-- gap: 4 --|-- cell B: 32 --| ...
                                  A  free  B
                                  e  .  .  e
                                  &lt;- spacing -&gt;
</code></pre>
<p>Here <code>e</code> beside cell A duplicates A's right edge; <code>e</code> beside cell B duplicates B's left edge. The two <code>.</code> pixels are unused gap in this hypothetical packing. All four pixels belong to the spacing measurement, but only the two <code>e</code> pixels are extruded. Outer margins can also contain border extrusion or other pixels; measure their total width independently. Confirm the actual export's layout before applying this convention. The example labels the visible 32-pixel cells separately from their extruded border pixels.</p>
<p>Do not assume a universal number of padding pixels. Filtering, scaling, mipmaps, packing and engine settings affect the result. A correct choice for one pipeline is not a guarantee for another.</p>
<p>For example, Godot 4.2 documents a <strong>Use Texture Padding</strong> option that adds one transparent pixel around tiles to help prevent bleeding; this is a specific engine behavior, not proof that every atlas needs the same authored padding. See the <a href="https://docs.godotengine.org/en/4.2/tutorials/2d/using_tilesets.html">Godot TileSet guide</a>.</p>
<h2>A short test that separates the causes</h2>
<ol>
<li>Inspect the raw atlas at native size. Measure image dimensions, tile dimensions, first tile offset and gap.</li>
<li>Enter those values in the importer. Check the first tile, an interior tile and the last tile in each row. If edges are cropped or the grid drifts, correct the geometry before testing filtering.</li>
<li>Place neighboring tiles with very different colors in a test scene. View them under the actual game's scaling, camera motion and filtering settings.</li>
<li>If the grid is correct but seams appear, compare the engine's filtering and padding options with an appropriately extruded atlas. Change one variable at a time.</li>
<li>Reopen the project and verify the selected tile regions and scene. Godot warns that changing a TileSet atlas's margins, separation or region size can cause tiles to be lost; save a copy before changing an existing setup.</li>
</ol>
<p>Blank lines can also result from camera or tile placement, not just atlas bleeding. Capture the settings and a reproducible test scene before declaring a cause.</p>
<h2>Example: the grid drifts at each column</h2>
<p>Suppose the 144-pixel-wide example is imported with the correct 32-pixel tile width and two-pixel margin, but spacing is mistakenly set to two instead of four. The importer would place the first two starts at x=2 and x=36, while the intended second start is x=38. By the fourth column the importer would start at x=104 instead of x=110, a six-pixel drift. This is an illustrative calculation, not a test result.</p>
<p>First compare those positions with the raw atlas and restore spacing to four. Check that the image actually follows the assumed geometry. Only after the cells line up should you investigate any remaining seams using the real engine's sampling and camera settings. Adjusting filtering while the grid itself is wrong cannot establish whether filtering caused the original defect.</p>
<h2>Keep a compact handoff note</h2>
<p>Record the image filename, pixel dimensions, tile width and height, margin, spacing, and whether edges were extruded. Also note the engine version and filtering settings used for the test. Do not label a transparent gap as extrusion merely because the atlas has spacing.</p>
<p>The FrameSprite team builds tools for game assets, but an exported image alone cannot tell a downstream editor every intended setting. Verify the actual file and the target engine instead of assuming that an image's appearance proves its geometry.</p>
<h2>Related reading</h2>
<p><em>About the author: The FrameSprite Workflow Team documents workflows for its own game-asset tools. The cover is an AI-generated concept illustration, not a product screenshot. <a href="https://www.framesprite.com/about#workflow-team">About the team</a> · <a href="https://www.framesprite.com/contact">Contact and corrections</a>.</em></p>
<p>For a complementary sprite import example, see <a href="https://framesprite-workflow.hashnode.dev/unity-sprite-slicing-cell-pivot-qa">Unity sprite slicing and pivot QA</a>.</p>
<p>For a separate check of image transparency, see our published <a href="https://framesprite-workflow.hashnode.dev/transparent-png-light-dark-edge-check">transparent PNG edge review</a>; edge appearance and atlas grid geometry require different tests.</p>
]]></content:encoded></item><item><title><![CDATA[Naming Animation Frames So a Game Engine Can Import Them Reliably]]></title><description><![CDATA[Use zero-padded frame names and an import checklist to catch swapped frames, duplicate images and ZIP extraction conflicts before playback.]]></description><link>https://framesprite-workflow.hashnode.dev/animation-frame-naming-import-order</link><guid isPermaLink="true">https://framesprite-workflow.hashnode.dev/animation-frame-naming-import-order</guid><category><![CDATA[Game Development]]></category><dc:creator><![CDATA[FrameSprite Workflow Team]]></dc:creator><pubDate>Wed, 28 Oct 2026 07:41:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6add6fc04b032073c0397857/445931ce-336e-4613-a35a-3e343940d79d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>An animation can contain every required image and still play in the wrong order. A common cause is an implicit ordering assumption: a file browser, archive utility, or import script may sort <code>frame_10.png</code> before <code>frame_2.png</code> when comparing names as text. Make the sequence explicit and test the importer you actually use.</p>
<p><em>Disclosure: This article was prepared with AI assistance by the FrameSprite team, which operates a game-asset creation product. The examples illustrate a proposed handoff convention; they are not a claim about any particular exporter's default filenames.</em></p>
<h2>Key takeaways</h2>
<ul>
<li>Use a documented index convention and fixed-width numbers.</li>
<li>Preserve namespaces to avoid overwriting similarly named files.</li>
<li>Inspect the actual imported order and timing.</li>
</ul>
<h2>Use names that preserve the intended order</h2>
<p>For a twelve-image attack, a consistent set might be:</p>
<pre><code class="language-text">guard_attack_000.png
guard_attack_001.png
...
guard_attack_011.png
</code></pre>
<p>Here the first index is zero. A team can instead choose one-based numbering; what matters is documenting the choice and keeping it consistent. Fixed-width numbers make ordinary character-by-character sorting produce the expected order within that range. If a sequence can reach 1,000 images, use enough digits from the start.</p>
<p>Names alone do not make a correct animation. With zero-based indices, <code>000</code> is the first frame, <code>004</code> is the fifth, <code>005</code> is the sixth, and <code>011</code> is the twelfth. A filename does not prove that <code>005</code> depicts the intended sixth moment, or that an engine imported every image. Check both the content and the import result.</p>
<h2>Give each action its own namespace</h2>
<p>Suppose two characters both contain <code>idle_000.png</code>. If a collaborator extracts both into the same folder, an operating system or archive tool may ask to replace a file, rename it, or behave differently. Keep a character identifier in the filename or preserve the directory structure:</p>
<pre><code class="language-text">guard/idle/guard_idle_000.png
merchant/idle/merchant_idle_000.png
</code></pre>
<p>Agree whether folder names are part of the contract. ZIP files can even contain members with duplicate names; <a href="https://docs.python.org/3/library/zipfile.html">Python's ZIP documentation</a> specifically notes that duplicate archive names exist. Inspect the archive before flattening it, and never accept an overwrite prompt without checking the destination.</p>
<h2>Provide a small manifest</h2>
<p>A useful handoff can be a plain-text note alongside the frames:</p>
<pre><code class="language-text">character: guard
action: attack
index: zero-based, inclusive 000–011
frame count: 12
ordering: ascending numeric index
playback: once
approved source: guard_attack_master_v03
</code></pre>
<p>This example describes a naming convention, not a standardized metadata schema. Add the actual canvas size, intended frame timing, and revision as needed. If timings differ per image, list them separately; a file name does not encode duration. Make sure the declared total agrees with the files on disk.</p>
<h2>Import and verify rather than trust the folder view</h2>
<ol>
<li>Count the files and check for missing or repeated indices.</li>
<li>Inspect the first, middle and final frames, then the entire sequence if movement is complex.</li>
<li>Import into the destination engine and inspect the resulting frame order. A natural-number sort in one tool does not prove another tool uses the same rule.</li>
<li>Verify whether the action should loop and apply the intended timing.</li>
<li>Reopen the imported project and play the animation in the actual scene.</li>
</ol>
<p>For example, <a href="https://docs.godotengine.org/en/stable/tutorials/2d/2d_sprite_animation.html">Godot's 2D sprite animation guide</a> describes using separate image files or a sprite sheet with <code>AnimatedSprite2D</code>. Follow the instructions for the engine version you use and check the resulting resource rather than assuming its ordering policy.</p>
<p>If a frame is missing, do not silently renumber the remaining images and declare the sequence complete. Ask whether it was omitted, intentionally duplicated or removed. Keep the received archive intact and create a revised package under a new version name.</p>
<h2>Diagnose an order mismatch</h2>
<ul>
<li><strong>A frame appears too early:</strong> compare the actual imported order with the manifest. If names such as <code>frame_2</code> and <code>frame_10</code> are sorted as text, create a new consistently zero-padded package and import it again. Do not assume the importer uses the same sort as your file browser.</li>
<li><strong>The count is correct but a pose is missing:</strong> inspect the images themselves for a duplicated or mislabeled pose. Twelve filenames do not guarantee twelve distinct moments; an intentional hold may also reuse an image, so compare the source and timing notes before replacing anything.</li>
<li><strong>A frame disappears after extraction:</strong> inspect the original archive for duplicate member names and compare the full paths with the extracted files. Restore from the intact archive into separate folders if flattening caused a collision; do not overwrite a recipient's existing files.</li>
<li><strong>The order is right but motion feels wrong:</strong> check frame durations, loop settings and playback speed separately. A filename sequence determines neither the duration of an individual frame nor the transition back to idle.</li>
</ul>
<p>Record the failing revision, expected and observed order, the suspected cause and the result of the next import. These are suggested checks; they do not report a test performed for this article.</p>
<p>Clear names are inexpensive insurance, but they are only one part of delivery. The actual visual sequence, playback timing and in-game behavior still need a human review.</p>
<h2>Related reading</h2>
<p><em>About the author: The FrameSprite Workflow Team documents workflows for its own game-asset tools. This article was prepared with AI writing assistance. The cover is an AI-generated concept illustration, not a product screenshot. <a href="https://www.framesprite.com/about#workflow-team">About the team</a> · <a href="https://www.framesprite.com/contact">Contact and corrections</a>.</em></p>
<p>Our <a href="https://framesprite-workflow.hashnode.dev/sprite-sheet-export-qa-12-frame-png-audit">12-frame sprite export QA example</a> checks image and export integrity. This article instead addresses sequence naming, archive collisions and downstream import order; both checks are needed.</p>
<p>For the next handoff step, the published <a href="https://framesprite-workflow.hashnode.dev/godot-spriteframes-import-acceptance">Godot SpriteFrames import acceptance guide</a> covers the engine-side sequence, timing and loop review.</p>
]]></content:encoded></item><item><title><![CDATA[Editing a Character Outfit: How to Choose a Mask and Review the Result]]></title><description><![CDATA[Plan old clothing, added silhouette space and protected areas. Use a hypothetical coat edit to diagnose mask conflicts.]]></description><link>https://framesprite-workflow.hashnode.dev/character-outfit-edit-mask-review</link><guid isPermaLink="true">https://framesprite-workflow.hashnode.dev/character-outfit-edit-mask-review</guid><category><![CDATA[Game Development]]></category><dc:creator><![CDATA[FrameSprite Workflow Team]]></dc:creator><pubDate>Wed, 28 Oct 2026 07:36:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6add6fc04b032073c0397857/930dd6f0-20e0-4725-8a81-3d96ac722842.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>An outfit variant can preserve a useful character pose while changing armor, a coat or colors. The difficult part is drawing a mask large enough for the new garment without unnecessarily exposing the face or protected equipment to edits. Plan the intended silhouette before painting the selection.</p>
<p><em>Disclosure: This article was prepared with AI assistance by the FrameSprite team, which operates the product discussed below. Product behavior described here is limited to the documented local static edit; inspect your actual result.</em></p>
<p><em>Cover: AI-generated concept illustration of a game-art workspace. It does not show an actual outfit edit or the selection described below.</em></p>
<h2>Key takeaways</h2>
<ul>
<li>Include the space needed by the new outfit silhouette.</li>
<li>Protect and compare the face, hair and equipment.</li>
<li>Treat later generated animation as a separate review.</li>
</ul>
<h2>Prepare a complete master</h2>
<p>Choose an image you have rights to use and save a copy before editing. Check that the full character, feet, headwear and any important equipment are visible. If a weapon is already cropped, an outfit edit is not a reliable repair method.</p>
<p>Write a concise change request: “Replace the short tunic with a long blue coat; retain the face, hair, brown boots and sword.” This establishes both the change and the protected details. A prompt can express intent, but it does not substitute for a precise mask and visual review.</p>
<p>If the request starts from an automatically inferred description, <a href="https://framesprite-workflow.hashnode.dev/review-image-to-prompt-before-reuse">review the image-to-prompt result</a> first. A mistaken description of the sword or coat can become an unwanted instruction.</p>
<h2>Include the space the new garment needs</h2>
<p>If the new coat extends below the old shirt, selecting only the original shirt leaves no room for the longer hem. Include the clothing and adjacent empty space that the new silhouette must occupy. Exclude the face, hair, weapon and other protected details when possible.</p>
<p>For a cape that must pass behind a sword or occupy already crowded space, a mask alone may not resolve the overlap. A different pose, a separate layer, or manual cleanup may be needed. Painting over the sword creates a different risk: the model may alter it along with the outfit.</p>
<p>A local-edit workflow may preserve pixels outside its mask, depending on the tool and mode. Check the current documentation and compare the actual result. A static edit does not guarantee protection for a later generated animation: the <a href="https://www.framesprite.com/guides/recover-ai-animation-submission-after-timeout">FrameSprite animation recovery guide</a> makes that distinction explicit. A mask does not guarantee that the requested outfit will appear correctly inside it.</p>
<h2>A worked mask plan for the blue coat</h2>
<p>This is a hypothetical planning example, not a generated result or a test of a particular model. Assume a guard whose short tunic ends at the waist. The proposed blue coat should end above the boots. The sword sits beside the body, and one hand crosses the waist. The face, hair, hands, boots and sword must remain unchanged in the approved still.</p>
<p>The diagram describes regions rather than exact pixel locations. <code>M</code> is the existing garment to select, <code>E</code> is extra space the longer coat needs, and <code>P</code> marks details to protect and compare.</p>
<pre><code class="language-text">                 [P: face and hair]
 [P: sword]      [M: short tunic]    [P: hand]
                 [E: coat space]
                 [P: brown boots]

 Proposed selection: M + E, excluding every P region
</code></pre>
<p>Start by identifying the intended coat outline on a copy of the master. Select the tunic plus the space down to that outline. Leave the protected hand as an excluded island if the editor supports that selection. If the coat must cover the hand, the requirements conflict: choose whether the hand may be redrawn, change the coat shape, or use a layered/manual workflow before generating.</p>
<p>Use a small review record so a failed candidate leads to one specific change:</p>
<table>
<thead>
<tr>
<th>Region or condition</th>
<th>Acceptance target</th>
<th>Possible failure</th>
<th>One change to try next</th>
</tr>
</thead>
<tbody><tr>
<td>Coat below the old waistline</td>
<td>Hem reaches the planned outline above the boots</td>
<td>Coat stops at the old tunic edge</td>
<td>Expand only the lower selection into the required space</td>
</tr>
<tr>
<td>Hand crossing the waist</td>
<td>Same hand and grip remain recognizable</td>
<td>Selected fingers are redrawn or merged into the coat</td>
<td>Exclude the hand from the selection, if the editor permits it</td>
</tr>
<tr>
<td>Sword beside the body</td>
<td>Blade, grip and placement match the master</td>
<td>Sword changes where it overlaps the selection</td>
<td>Remove the sword overlap from the mask; reconsider the coat outline if that blocks it</td>
</tr>
<tr>
<td>Boot boundary</td>
<td>Coat ends cleanly without repainting the boots</td>
<td>A strip of old tunic or an awkward seam remains</td>
<td>Adjust only the boundary responsible for the seam</td>
</tr>
</tbody></table>
<p>These are possible outcomes and proposed corrections, not observations. Keep the first mask and candidate, then save the revised mask as a new version. If changing the mask also requires a new prompt, record both changes; that comparison no longer isolates the mask alone.</p>
<h2>Review the output in three passes</h2>
<p><strong>1. Protected regions.</strong> Compare the face, hair, boots and weapon with the master. A local boundary can protect outside pixels, but an important object partly inside the mask still needs inspection.</p>
<p><strong>2. Garment construction.</strong> Look at the neckline, sleeves, belt and hem. Does the new coat attach plausibly to the body? Is the intended color consistent? Is an edge awkward where the edited area meets unchanged pixels?</p>
<p><strong>3. Practical use.</strong> View the character at the intended game size. An elaborate buckle may disappear at that size, while a high-contrast coat might make the silhouette more readable. The acceptable answer depends on the game's visual style.</p>
<p>Check the cutout on both a light and a dark background using the <a href="https://framesprite-workflow.hashnode.dev/transparent-png-light-dark-edge-check">transparent PNG edge review</a>. A garment can have the intended shape while still carrying an unwanted fringe at its new edge.</p>
<p>If the result fails, adjust one thing at a time. Expand the mask if the coat is visibly cut short; reduce it if the tool altered a protected object. Keep the previous candidate rather than overwriting it, and record which mask produced each version.</p>
<h2>Treat animation as a separate review</h2>
<p>Keep the original master, the approved outfit still, and each action output as separate files. A name such as <code>guard_bluecoat_attack_v02</code> helps a team identify a variant, but does not create a runtime equipment slot or a skeletal rig.</p>
<p>When animating the variant, recheck the coat, hands and equipment throughout the movement. The static protection described above does not carry forward as a guarantee that every generated frame preserves those details. Import the approved results into the target engine and test against the actual scene and interaction requirements.</p>
<p>For a reusable animation system with multiple interchangeable outfits, consider whether layered art or a rig is the better production method. A set of generated stills and frames can be useful, but it is not automatically a dynamic wardrobe system.</p>
<h2>Related reading</h2>
<p><em>About the author: The FrameSprite Workflow Team documents workflows for its own game-asset tools. <a href="https://www.framesprite.com/about#workflow-team">About the team</a> · <a href="https://www.framesprite.com/contact">Contact and corrections</a>.</em></p>
<p>For a separate check of identity across views, see <a href="https://framesprite-workflow.hashnode.dev/eight-direction-facing-identity-mirror-qa">eight-direction sprite QA</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Nine-Slice UI Panels: A Practical Test Before You Ship]]></title><description><![CDATA[Check corners, edges, stretch modes and target sizes with a concrete nine-slice matrix. Keep generated art separate from engine UI behavior.]]></description><link>https://framesprite-workflow.hashnode.dev/nine-slice-ui-review</link><guid isPermaLink="true">https://framesprite-workflow.hashnode.dev/nine-slice-ui-review</guid><category><![CDATA[Game Development]]></category><dc:creator><![CDATA[FrameSprite Workflow Team]]></dc:creator><pubDate>Wed, 28 Oct 2026 07:31:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6add6fc04b032073c0397857/404a0610-6430-438f-ba33-d572bcbd6d58.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>To reuse a small panel at several sizes, split its texture into corners, edges, and a center, then define how each region should respond to resizing. The result is only acceptable after you inspect it at the smallest and largest intended panel sizes. A correct-looking source image alone does not establish that the margins or edge modes are correct in the game.</p>
<p>We are the FrameSprite team. This article is a general review procedure for 2D game interfaces, not an assertion that a particular project or engine version has passed these tests. The dimensions used below are a worked example, not measurements of a production asset.</p>
<p>This article was drafted with AI assistance. Learn about the team on the <a href="https://www.framesprite.com/about#workflow-team">FrameSprite About page</a>; send corrections through the <a href="https://www.framesprite.com/contact">contact page</a>.</p>
<p><strong>Key points</strong></p>
<ul>
<li>Measure borders before choosing panel sizes.</li>
<li>Check corners, edge behavior and content space at each intended size.</li>
<li>Inspect the source image and the engine settings as separate artifacts.</li>
</ul>
<p>Our published <a href="https://framesprite-workflow.hashnode.dev/transparent-png-light-dark-edge-check">transparent PNG edge review</a> covers a related image-level check before the UI component is assembled.</p>
<h2>Start with the border, not the target size</h2>
<p>Imagine a 96 × 96 pixel panel with a decorative frame. If the non-stretchable left and right border regions each occupy 16 pixels, and the top and bottom borders also occupy 16 pixels, the remaining center is 64 × 64 pixels. The corners are the four 16 × 16 regions. Check the actual drawing before adopting those numbers: a protruding ornament or shadow may extend beyond the apparent border.</p>
<p>The nine regions in this example are arranged as follows:</p>
<pre><code class="language-text">+-------------+-------------+-------------+
| top-left    | top edge    | top-right   |
| corner      |             | corner      |
+-------------+-------------+-------------+
| left edge   | center      | right edge  |
+-------------+-------------+-------------+
| bottom-left | bottom edge | bottom-right|
| corner      |             | corner      |
+-------------+-------------+-------------+
</code></pre>
<p>The corners preserve their shape in the intended nine-slice setup; the edges and center fill the remaining space according to the selected engine modes. This diagram describes the regions, not an actual exported panel or a measured engine result.</p>
<p>The dividing lines must include the full detail that should remain stable. Put a margin through the middle of a corner flourish and the resizing operation can stretch that flourish as part of an edge or center. Put it too far inward and a large fixed region may leave little usable center space.</p>
<p>The four sides do not have to use equal values. Godot's <a href="https://docs.godotengine.org/en/stable/classes/class_ninepatchrect.html">NinePatchRect documentation</a> provides separate margins for each side and a region rectangle for selecting a texture area. Confirm the behavior and property names in the version actually used by your project.</p>
<h2>Choose what the edges should do</h2>
<p>Stretching scales an edge segment to fill the available space. Tiling repeats a segment instead. Both can be useful: a smooth gradient can stretch well, while a stitched pattern may need repetition. Neither is automatically correct.</p>
<p>Here is a small teaching example. Treat each character as one abstract unit in a four-unit edge segment. Doubling its width by nearest-neighbor scaling enlarges the marks; repeating it preserves their width and adds another repeat:</p>
<pre><code class="language-text">source:       AB--
stretch x2:   AABB----
tile twice:   AB--AB--
</code></pre>
<p>This is a symbolic comparison, not an engine screenshot. A real panel's filtering, fractional size, and selected mode can produce a different final join. Godot also offers <code>AXIS_STRETCH_MODE_TILE_FIT</code>, which adjusts repeats to fit whole tiles; its <a href="https://docs.godotengine.org/en/stable/classes/class_ninepatchrect.html#enum-ninepatchrect-axisstretchmode">mode definitions</a> distinguish that behavior from ordinary tiling.</p>
<p>Look for a repeated pattern that does not join cleanly at the seam. With stretching, look for features that become elongated or blurred. With tiling, inspect the final partial repeat and the junction with the corners. If a decorative line has an obvious direction or asymmetric end, it may require a redesigned source rather than a different checkbox.</p>
<p>Keep the center decision separate. Some panels need an opaque interior; others are frames with a transparent center. In Godot, <code>draw_center</code> controls whether the center is drawn. If the game uses the center as a background for text, verify the text's contrast over the actual center rather than over an editor checkerboard.</p>
<h2>Test three shapes and one failure case</h2>
<p>Make a small test matrix:</p>
<ol>
<li>The minimum useful panel: inspect whether the fixed borders collide or obscure content.</li>
<li>A wide, short panel: inspect horizontal edge stretching and repeated ornaments.</li>
<li>A narrow, tall panel: inspect vertical edges and padding around content.</li>
<li>One intentionally undersized panel: note what breaks and set a minimum size or a fallback.</li>
</ol>
<p>Use a representative label, icon, and multiline text rather than an empty box. A panel can preserve its corners perfectly while its content is clipped. Verify the content area and the decorative border as separate concerns: nine-slice margins describe how image regions resize, while layout padding determines where text or child controls can go.</p>
<p>Use the following planning matrix with hypothetical 16-pixel borders on all four sides. Assume one source pixel maps to one target layout unit, with no additional border scaling. The width between borders is <code>target width − 16 − 16</code>; height uses the same calculation. These are geometric expectations, not engine measurements or usable text bounds.</p>
<table>
<thead>
<tr>
<th>Target size</th>
<th>Region between borders</th>
<th>Main inspection</th>
<th>If it fails</th>
</tr>
</thead>
<tbody><tr>
<td>96 × 96</td>
<td>64 × 64</td>
<td>Compare with the original division</td>
<td>Recheck crop and margins</td>
</tr>
<tr>
<td>160 × 64</td>
<td>128 × 32</td>
<td>Horizontal ornament width and joins</td>
<td>Compare stretch and tile modes</td>
</tr>
<tr>
<td>64 × 160</td>
<td>32 × 128</td>
<td>Vertical repeats and text fit</td>
<td>Adjust edge mode or content padding</td>
</tr>
<tr>
<td>24 × 64</td>
<td>No valid width; borders need 32</td>
<td>Deliberately exceed the size limit</td>
<td>Enforce a minimum or use smaller borders</td>
</tr>
</tbody></table>
<p>The last target is eight units narrower than the two fixed side borders together. Inspect how the chosen engine handles it rather than assuming all engines squeeze or overlap the regions in the same way. The other rows can still fail a content test: padding and the text itself need space inside the geometric center.</p>
<p>At the game's target scale, check rounded corners, one-pixel lines, translucent shadows, and edges over both light and dark scenes. If the design is pixel art, test integer display scaling and the project's texture filtering settings. A correctly divided source can still look soft if it is later sampled or displayed at an unsuitable scale.</p>
<h2>Check the engine's actual import</h2>
<p>Unity's <a href="https://docs.unity3d.com/2018.4/Documentation/Manual/UIVisualComponents.html">UI component documentation</a> describes the Image component's Sliced mode and the sprite's 3×3 division. In Unity's UI workflow, verify the sprite border and the component's sliced image configuration in the target project; do not assume the metadata in a source PNG carries those settings by itself.</p>
<p>If the panel comes from an atlas, first verify the selected sprite rectangle. Our <a href="https://framesprite-workflow.hashnode.dev/unity-sprite-slicing-cell-pivot-qa">Unity slicing and pivot review</a> provides image-region checks before you judge the panel's resizing behavior. A wrong crop can move the apparent border even when the margin values have not changed.</p>
<p>For Godot, configure the texture, margins, and axis stretch modes on the chosen node, then resize that node in a test scene. A shared atlas may need an explicit <code>region_rect</code>; otherwise the wrong portion of the texture can be divided. Check the chosen region and margin coordinates together.</p>
<p>If a supplied panel is generated, record which version was reviewed and keep the original image. FrameSprite's <a href="https://www.framesprite.com/guides/ai-game-ui-generator">game UI asset guidance</a> covers preparing an art kit and rebuilding interface components; this article complements it with a specific nine-slice size matrix and handoff checks. Generated artwork still needs the same border, readability, and engine checks as any other image. This guide does not claim that a generated panel includes engine-specific nine-slice settings.</p>
<h2>Leave a useful handoff</h2>
<p>Deliver the source image, an exact crop or atlas region, four margin values, stretch or tile choices for each axis, minimum supported size, content padding, and screenshots of the test matrix. Include the engine and version if a specific project was checked. If none was checked, state that plainly.</p>
<p>A panel is ready when the target project preserves the intended corner detail, handles edge joins, and keeps content readable across the actual sizes it uses. If the design fails at the smallest size, revising the art or setting a minimum can be more reliable than forcing a universal panel to fit.</p>
<p><em>Cover image: AI-generated conceptual illustration, not a screenshot of the product.</em></p>
]]></content:encoded></item><item><title><![CDATA[A Tilemap Handoff: Keep the Picture, Grid, and Game Rules Separate]]></title><description><![CDATA[Package dependencies, declare coordinate units and verify game rules. Follow a 32-pixel orthogonal example and a practical failure checklist.]]></description><link>https://framesprite-workflow.hashnode.dev/tilemap-data-handoff</link><guid isPermaLink="true">https://framesprite-workflow.hashnode.dev/tilemap-data-handoff</guid><category><![CDATA[Game Development]]></category><dc:creator><![CDATA[FrameSprite Workflow Team]]></dc:creator><pubDate>Wed, 28 Oct 2026 07:15:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6add6fc04b032073c0397857/29c41d03-46c3-4faf-bcd0-0b1a2f20dbb3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A map image can show a convincing dungeon and still tell a game engine nothing about where a player can walk. Before delivery, distinguish the visual art, the tile placement grid, and the rules that interpret collision, objects, and spawn positions. Test each layer at the receiving end instead of treating an attractive preview as proof that the level works.</p>
<p>We are the FrameSprite team and operate a browser-based Tilemap Builder. This article describes a general handoff and uses an illustrative map rather than claiming a new engine test or production result. Our tool produces starter data; the receiving project must still implement and verify gameplay.</p>
<p>This article was drafted with AI assistance. Learn about the team on the <a href="https://www.framesprite.com/about#workflow-team">FrameSprite About page</a>; send corrections through the <a href="https://www.framesprite.com/contact">contact page</a>.</p>
<p><strong>Key points</strong></p>
<ul>
<li>Hand off artwork, tile placement and gameplay metadata as distinct artifacts.</li>
<li>Record external tileset dependencies and coordinate conventions.</li>
<li>Verify collision and object interpretation in the receiving project.</li>
</ul>
<p>For the separate task of checking grid-based sprite imports, see our published <a href="https://framesprite-workflow.hashnode.dev/unity-sprite-slicing-cell-pivot-qa">Unity sprite slicing review</a>.</p>
<h2>Decide what each file means</h2>
<p>Three artifacts are often called a “map,” but they serve different purposes:</p>
<table>
<thead>
<tr>
<th>Artifact</th>
<th>What it contains</th>
<th>What it does not establish</th>
</tr>
</thead>
<tbody><tr>
<td>Baked map art</td>
<td>A single image showing the scene</td>
<td>Walkable cells, editable objects, or collision behavior</td>
</tr>
<tr>
<td>Tileset</td>
<td>Source graphics arranged for reuse</td>
<td>A finished level layout</td>
</tr>
<tr>
<td>Tilemap</td>
<td>Data placing tiles in a grid and possibly other layers or metadata</td>
<td>That the engine interprets those fields as intended</td>
</tr>
</tbody></table>
<p>A wall painted on a single image might visually suggest an obstacle. It has no collision by itself. Conversely, a tilemap can include collision metadata, but that metadata only matters if the receiving code or importer consumes it correctly.</p>
<p>The <a href="https://doc.mapeditor.org/en/stable/manual/layers/">Tiled layers documentation</a> describes tile, object, image, and group layers as distinct structures. Choose the representation according to what needs to change: a repeating ground pattern fits a tile layer; a named spawn or an irregular trigger may need an object or another explicit contract.</p>
<h2>Make a small contract before exporting</h2>
<p>Suppose a prototype uses a top-down orthogonal grid with 32-pixel cells. The handoff should say whether that number describes the source artwork, the grid in Tiled, the display scale, or all three. A 32-pixel source tile displayed at twice its source size does not magically become a 64-pixel source tile.</p>
<p>Record at least:</p>
<ul>
<li>Camera and map orientation: for example, orthogonal top-down, rather than an ambiguous “top view.”</li>
<li>Grid size, map width and height, and any outer margin or spacing in the atlas.</li>
<li>Tileset image path and tile ordering; include external files if the map references them.</li>
<li>Layer names and meaning, including whether visibility affects gameplay or only the editor.</li>
<li>Coordinates and units for spawn points, collision objects, and level exits.</li>
<li>The intended importer and version, plus a known test scene or manual verification steps.</li>
</ul>
<p>These are handoff requirements, not claims that every exporter writes every field. Open the produced file and inspect what actually exists.</p>
<h2>Follow the dependencies in a small package</h2>
<p>This hypothetical package uses a Tiled JSON map and an external JSON tileset. It is a teaching example, not an observed export from our tool. The separate notes file is a team convention, not a standard Tiled requirement.</p>
<pre><code class="language-text">handoff/
  maps/room.tmj
  tilesets/terrain.tsj
  images/terrain.png
  handoff-notes.json
</code></pre>
<p>The dependency chain is <code>room.tmj → terrain.tsj → terrain.png</code>. In this example, the map's tileset <code>source</code> is <code>../tilesets/terrain.tsj</code>; the tileset's <code>image</code> is <code>../images/terrain.png</code>. The <a href="https://doc.mapeditor.org/en/stable/reference/json-map-format/">Tiled JSON reference</a> documents the external tileset <code>source</code> and tileset <code>image</code> fields. Inspect those values in the actual files rather than relying on the preview.</p>
<table>
<thead>
<tr>
<th>File</th>
<th>What the recipient checks</th>
</tr>
</thead>
<tbody><tr>
<td><code>maps/room.tmj</code></td>
<td>The tileset reference resolves; expected visual and object layers exist</td>
</tr>
<tr>
<td><code>tilesets/terrain.tsj</code></td>
<td>The atlas path resolves; tile dimensions and IDs match the map</td>
</tr>
<tr>
<td><code>images/terrain.png</code></td>
<td>The expected atlas is included, not an older image with the same filename</td>
</tr>
<tr>
<td><code>handoff-notes.json</code></td>
<td>Units, origin, intended importer/version, and agreed movement test are recorded</td>
</tr>
</tbody></table>
<p>Copy the whole <code>handoff</code> directory to a new location and open the copied map. A missing tileset prevents the chain from reaching the atlas; a missing PNG leaves the tileset without its source art. Restore the missing dependency or correct the reference, then repeat the relocated-copy check. Renaming an image without updating its reference is not a complete fix.</p>
<h2>Separate visual and collision checks</h2>
<p>Start by turning visual layers on and off in the editor. Is the ground continuous? Are wall corners assigned to the expected cells? Check any decorative overlay separately so it does not conceal a wrong base tile.</p>
<p>Then inspect the collision representation without the art, if your editor or engine allows it. If a layer named <code>collision</code> contains tile IDs, document how the importer maps those IDs to blocked or walkable space. If the data instead uses object shapes, verify their units and alignment. A label called <code>collision</code> is only a name until the game code interprets it.</p>
<p>Tiled supports <a href="https://doc.mapeditor.org/en/stable/manual/custom-properties/">custom properties</a> on its basic data structures. Properties can carry useful information, but the receiving application decides how to use it. A property named <code>blocked</code> with a boolean value is not automatically a physics body in an unrelated engine.</p>
<p>Finally, place a test actor at a declared spawn and walk the perimeter, each doorway, and any one-tile passage. This is a suggested test, not a claim that the particular map has been tested. If an entrance is visually open but the player cannot pass, check the grid, collision layer, scale, and origin before editing the artwork.</p>
<h2>Check dependencies and coordinate assumptions</h2>
<p>The <a href="https://doc.mapeditor.org/en/stable/reference/json-map-format/">Tiled JSON map reference</a> distinguishes <code>tilelayer</code>, <code>objectgroup</code>, <code>imagelayer</code>, and <code>group</code> entries. Tile layers refer to tiles through global tile IDs. An importer must interpret these IDs and associated tilesets correctly; a JSON file opening in a text editor is not an import test.</p>
<p>Copy the map and all referenced image or tileset files into a clean directory, then open the copy using the intended workflow. This catches a dependency that accidentally points to a creator's local path. If a link breaks, fix the package and repeat the test rather than telling the recipient to guess where the atlas lives.</p>
<p>For object positions, explicitly note whether a coordinate is measured in pixels or cells and which point it represents. A spawn placed at a cell corner may need an offset to put a character's feet inside the tile. Different engine coordinate conventions and scaling can change the result, so validate a known marker in the receiving scene.</p>
<h3>Work one coordinate through the handoff</h3>
<p>Assume a finite orthogonal map with 32 × 32 pixel cells, zero-based column and row indices, and the map origin at the top-left corner of cell <code>(0, 0)</code>. For this example, positive x points right and positive y points down. There are no layer offsets or transforms. These are the example's declared conventions, not a conversion rule for every map orientation or engine.</p>
<p>Cell <code>(3, 2)</code> starts at pixel <code>(96, 64)</code>. Its center is:</p>
<pre><code class="language-text">center x = (3 + 0.5) × 32 = 112 pixels
center y = (2 + 0.5) × 32 = 80 pixels
</code></pre>
<p>If the contract places the character's foot anchor at the cell center, a point object marking that spawn should be at pixel <code>(112, 80)</code> in this map space. Tiled's <a href="https://doc.mapeditor.org/en/stable/manual/objects/">object documentation</a> describes point objects as locations, and its JSON reference gives object x/y coordinates in pixels. Do not multiply an already-pixel-valued point by 32 again. A tile object's alignment is a separate setting and should not be treated as a point object's anchor.</p>
<p>In the receiving scene, draw a marker at the imported spawn and compare it with the intended cell center. A <code>(96, 64)</code> marker would expose a corner-versus-center mismatch in this example. If the destination uses different axes, origin or world units, document and check that conversion first. Displaying the map at twice its source size also needs one consistent transform for art, collision and the marker; it does not change the source coordinate recorded above.</p>
<h3>Diagnose the first failed check</h3>
<table>
<thead>
<tr>
<th>Observation</th>
<th>Check first</th>
<th>Recovery and repeat check</th>
</tr>
</thead>
<tbody><tr>
<td>The map opens but tiles are missing</td>
<td>Map-to-tileset and tileset-to-image paths</td>
<td>Restore dependencies; reopen the relocated copy</td>
</tr>
<tr>
<td>Tiles exist but the wrong graphics appear</td>
<td>Atlas version, tileset mapping and global tile IDs</td>
<td>Restore the matching atlas and mapping; inspect known cells</td>
</tr>
<tr>
<td>The scene looks right but an open doorway blocks movement</td>
<td>Collision data and the receiving importer's interpretation</td>
<td>Inspect collision alone; rerun the agreed doorway test</td>
</tr>
<tr>
<td>The spawn is offset by half a cell</td>
<td>Pixel/cell units, origin and anchor point</td>
<td>Apply the declared conversion; compare the marker with the known center</td>
</tr>
</tbody></table>
<p>For image-level crop and edge problems, use the separate <a href="https://framesprite-workflow.hashnode.dev/transparent-png-light-dark-edge-check">transparent PNG edge review</a>. Changing collision metadata cannot fix damaged source pixels, just as repainting a doorway cannot fix an importer that marks it solid.</p>
<h2>Use automation as a starting point</h2>
<p>Our <a href="https://www.framesprite.com/tools/tilemap-builder">Tilemap Builder</a> can produce starter layouts from a compatible source tile or terrain atlas. A generated layout is not a completed level: inspect the actual exported package, then implement movement, collisions, navigation, and game-specific interactions in the destination project. A fixed seed can make a particular generation reproducible only when the relevant inputs and generator behavior remain the same.</p>
<p>The handoff is complete when another person can identify the source assets, recreate the expected layout, and run the agreed movement test. Keep a copy of the input atlas and the export settings alongside the map so a visual fix does not silently invalidate the data.</p>
<p><em>Cover image: AI-generated conceptual illustration, not a screenshot of the product.</em></p>
]]></content:encoded></item><item><title><![CDATA[Scaling Pixel Art: Check the Sampling Path, Not Just the Export Size]]></title><description><![CDATA[Compare integer and fractional pixel-art scaling, separate file changes from display sampling, and test your actual browser or engine configuration.]]></description><link>https://framesprite-workflow.hashnode.dev/pixel-art-integer-scaling</link><guid isPermaLink="true">https://framesprite-workflow.hashnode.dev/pixel-art-integer-scaling</guid><category><![CDATA[Game Development]]></category><dc:creator><![CDATA[FrameSprite Workflow Team]]></dc:creator><pubDate>Wed, 28 Oct 2026 06:35:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6add6fc04b032073c0397857/fc2f7255-946e-4ca9-8c78-0ec480fa3371.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>For hard-edged pixel art, start by enlarging the source by an integer factor without smoothing, then inspect the final image in the actual display path. A clean PNG can still look uneven after an engine rescales it, a camera moves between pixels, or a browser displays it at a fractional size.</p>
<p>We are the FrameSprite team and operate tools that include sprite preparation. This guide explains a general test; its examples are calculations, not measurements of a newly produced asset or a guarantee of how your engine will render it.</p>
<p>This article was drafted with AI assistance. Learn about the team on the <a href="https://www.framesprite.com/about#workflow-team">FrameSprite About page</a>; send corrections through the <a href="https://www.framesprite.com/contact">contact page</a>.</p>
<p><strong>Key points</strong></p>
<ul>
<li>Start with an integer enlargement and unsmoothed sampling.</li>
<li>Check the entire display path, including engine and browser scaling.</li>
<li>Record the actual source and output dimensions before accepting an export.</li>
</ul>
<p>Our published <a href="https://framesprite-workflow.hashnode.dev/sprite-sheet-export-qa-12-frame-png-audit">sprite-sheet PNG export audit</a> provides related file-level checks before display scaling.</p>
<h2>Start with the original grid</h2>
<p>Suppose a source image measures 48 × 32 pixels. A 4× enlargement should measure 192 × 128 pixels: each dimension is multiplied by four. Under nearest-neighbor enlargement, one source pixel becomes a block of sampled output pixels rather than being blended with its neighbors. The enlargement preserves the existing grid; it does not invent new artwork or improve a silhouette that was already unclear.</p>
<p>Keep the original small file. Enlarging an already enlarged image again can make it difficult to tell whether a defect came from the source, a prior processing step, or the current display. Compare the original and export at matching proportions before changing the art itself.</p>
<p>Non-integer scaling is different. At 3.5×, a source pixel cannot occupy exactly the same whole number of destination pixels everywhere. Depending on the sampling path and alignment, neighboring columns may appear different widths. That is not automatically a file corruption, but it is a reason to inspect the image at the actual size in which players see it.</p>
<h2>Separate file processing from display processing</h2>
<p>There are at least three places an image can be scaled:</p>
<ol>
<li>An export tool writes a larger PNG.</li>
<li>An engine imports and samples the texture.</li>
<li>The game window, browser, operating system, or display scales the final output.</li>
</ol>
<p>Changing only the first step does not control the other two. The <a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/imageSmoothingEnabled">Canvas API's <code>imageSmoothingEnabled</code> property</a> controls whether a canvas context smooths a scaled image. That property applies to that drawing context; it is not a universal setting for every image on the page or every engine renderer.</p>
<p>Similarly, CSS's <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/image-rendering"><code>image-rendering</code></a> controls a browser's image scaling behavior where supported. Browser implementation and layout still matter. A CSS declaration cannot correct a blurry image that was already interpolated when it was exported.</p>
<h2>Make the width difference visible</h2>
<p>The diagram below is a teaching example, not a captured export. Each letter represents one source column with a different color. At 4×, four source columns fit into sixteen output columns evenly. At 3.5×, those four columns must fit into fourteen output columns: equal whole-pixel widths are impossible.</p>
<pre><code class="language-text">Source:       A B C D             4 columns
4x:           AAAABBBBCCCCDDDD   16 columns
3.5x example: AAAABBBCCCCDDD     14 columns
Widths:       4 + 3 + 4 + 3
</code></pre>
<p>The last row shows one possible nearest-neighbor allocation. Which colors receive three or four columns depends on alignment and the sampler. The point is the uneven widths, not that every browser must produce that exact sequence. Blended sampling may soften boundaries instead.</p>
<p>To reproduce the comparison without a game project, draw a 4×4 PNG in a pixel editor. Fill each column with a different solid color, with no transparency. Duplicate the original and resize one copy to 16×16 and the other to 14×14 using nearest-neighbor sampling. Open both saved files at 1:1, then count each color's columns with the editor's pixel grid. The 16×16 copy should assign four columns to each source color. Record the actual widths in the 14×14 copy; do not substitute this diagram for an observed result.</p>
<p>If a file has more than the original four colors, inspect whether smoothing, a color conversion, or another processing step was applied. If its file grid is correct but the displayed grid looks uneven, move the investigation to browser, engine or display scaling. Keep the original as the reference for both checks.</p>
<h2>Test the same pattern in the display path</h2>
<p>Create a small test image with one-pixel vertical and horizontal lines, a diagonal staircase, and a few isolated pixels in contrasting colors. Keep it alongside the actual sprite, rather than using the test pattern as a substitute for reviewing the art.</p>
<p>Compare these cases:</p>
<table>
<thead>
<tr>
<th>Test</th>
<th>What to inspect</th>
</tr>
</thead>
<tbody><tr>
<td>Original at a whole-number zoom</td>
<td>The intended source pattern and color boundaries</td>
</tr>
<tr>
<td>Integer enlarged file at 1:1 display</td>
<td>Equal-sized blocks and unchanged edge colors</td>
</tr>
<tr>
<td>The asset in the target engine</td>
<td>Texture filtering, camera alignment, and animated movement</td>
</tr>
<tr>
<td>A resizable game window</td>
<td>Whether fractional viewport scaling creates uneven widths</td>
</tr>
</tbody></table>
<p>At 4×, a one-pixel source line should become four output pixels wide in the file when the enlargement is an exact nearest-neighbor operation with no other processing. That statement concerns the generated file. It does not establish how many physical screen pixels the line occupies after window or display scaling.</p>
<p>Inspect the diagonal while the sprite and camera move, and record any shimmer or apparent jitter rather than accepting a static screenshot alone. Separate this from asset alignment: the <a href="https://framesprite-workflow.hashnode.dev/sprite-alignment-fixed-canvas-acceptance-test">fixed-canvas acceptance test</a> checks whether the source frames themselves shift.</p>
<p>For a specific engine example, <a href="https://docs.godotengine.org/en/4.4/tutorials/rendering/multiple_resolutions.html#stretch-scale-mode">Godot 4.4's multiple-resolution guide</a> explains that integer Stretch Scale Mode rounds the scale down to a whole number. This can keep pixel widths even while leaving unused window space. Its pixel-art guidance uses a low-resolution viewport; it also describes a different choice for projects that need subpixel movement. Record the chosen mode and test that configuration rather than transferring one engine's settings to another.</p>
<h2>Transparency and color still need their own review</h2>
<p>Nearest-neighbor scaling does not repair an existing alpha fringe. Use the <a href="https://framesprite-workflow.hashnode.dev/transparent-png-light-dark-edge-check">light and dark background edge check</a>, including corners and isolated semi-transparent pixels. If a pixel art style intentionally uses semi-transparency, do not delete it merely to make the image look binary.</p>
<p>If the asset is inside a sprite sheet, check that adjacent cells do not bleed at runtime. Padding, extrusion, filtering, and texture-atlas settings interact differently across engines. An enlarged standalone PNG can pass its file check while an atlas import introduces a seam; reproduce the actual packaging and camera setup before declaring the pipeline correct.</p>
<h2>Use a tool, then verify its output</h2>
<p>FrameSprite's <a href="https://www.framesprite.com/tools/sprite-sheet">Sprite Sheet Tools</a> include a pixel-art resizing workflow. Select the source, choose the intended factor, download the result, and inspect the actual file dimensions and edges. Availability and limits should be checked on the live tool; this article does not promise a particular output for every input or claim that a new download was tested.</p>
<p>For a handoff, include the original file, exported file, enlargement factor, intended engine filtering, and target viewport assumptions. Record any deliberate deviation such as a fractional UI scale. The final acceptance test is the rendered game at the sizes and motions the project actually uses, not the number printed in the export dialog.</p>
<p><em>Cover image: AI-generated conceptual illustration, not a screenshot of the product.</em></p>
]]></content:encoded></item><item><title><![CDATA[How to Review a Generated 2D Attack Animation Before It Reaches Your Game]]></title><description><![CDATA[Review anticipation, contact, recovery and proposed damage windows. Use a 12-frame example to separate visual motion from verified game behavior.]]></description><link>https://framesprite-workflow.hashnode.dev/generated-2d-attack-animation-review</link><guid isPermaLink="true">https://framesprite-workflow.hashnode.dev/generated-2d-attack-animation-review</guid><category><![CDATA[Game Development]]></category><dc:creator><![CDATA[FrameSprite Workflow Team]]></dc:creator><pubDate>Wed, 28 Oct 2026 06:30:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6add6fc04b032073c0397857/bcca6270-9fa2-464a-8d67-a222322da8c5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A generated attack can look lively in a preview yet fail as a game action: the weapon may pass through the character, the strike can occur before the wind-up, or the recovery pose may leave no clean transition back to idle. Review the motion as a sequence of decisions, not just as a moving picture.</p>
<p><em>Disclosure: This article was prepared with AI assistance by the FrameSprite team, which operates a game-asset creation product. The workflow below is a review method, not a guarantee of generation quality.</em></p>
<p><em>Cover: AI-generated concept illustration of a game-art workspace. It does not depict an actual attack sequence or engine test.</em></p>
<h2>Key takeaways</h2>
<ul>
<li>Describe a single attack and its protected details.</li>
<li>Inspect the wind-up, contact and recovery at gameplay size.</li>
<li>Check actual hitbox timing and state transitions in the game.</li>
</ul>
<h2>Write an action contract first</h2>
<p>Before generating, describe what the character must do and what must remain recognizable. A useful brief might say: “A side-view guard raises a short sword, strikes once toward the right, then returns to a ready stance. Keep the shield on the other arm and the entire weapon inside the frame.”</p>
<p>That sentence identifies a single strike, direction, weapon, equipment and finish. It does not specify exact joint positions or promise a hitbox. If gameplay requires an exact contact frame, include that as an acceptance condition and expect manual editing or a different animation method if the result cannot meet it.</p>
<p>If a description was inferred from the master, <a href="https://framesprite-workflow.hashnode.dev/review-image-to-prompt-before-reuse">review the image-to-prompt text</a> before adding the action. A wrong equipment name or facing instruction is an input problem worth correcting first.</p>
<p>Break the action into three readable phases:</p>
<table>
<thead>
<tr>
<th>Phase</th>
<th>What to inspect</th>
<th>Typical rejection</th>
</tr>
</thead>
<tbody><tr>
<td>Anticipation</td>
<td>Body and weapon prepare for the strike</td>
<td>Strike begins without a readable wind-up</td>
</tr>
<tr>
<td>Contact</td>
<td>Weapon travels toward the intended target</td>
<td>Grip switches hands or the blade disappears</td>
</tr>
<tr>
<td>Recovery</td>
<td>Character returns to a usable state</td>
<td>Pose never settles or equipment changes shape</td>
</tr>
</tbody></table>
<p>The exact number of images per phase is an art direction choice. More images do not automatically make the action clearer.</p>
<h2>Review the source before the export</h2>
<p>Start with a full-body master that includes the hands, feet and equipment. If the sword is cropped in the source, requesting a more elaborate attack will not reliably recover it. For reference-based motion, use a short, continuous shot with one readable performer. Cuts, occluded hands and rapid camera movement make it harder to interpret what the performer actually did.</p>
<p>The FrameSprite team's <a href="https://www.framesprite.com/guides/ai-sprite-generator-from-image">sprite animation guide</a> recommends inspecting identity, equipment and motion direction before extraction. Also examine action order, root movement and the ending rather than judging smoothness alone. Available features and their names can change, so consult the current interface and documentation. A reference can suggest motion; it does not guarantee a precise weapon path or collision timing.</p>
<h2>Step through the decisive moments</h2>
<p>Watch once at intended game speed, then inspect individual images around the wind-up, impact and recovery. Ask:</p>
<ul>
<li>Is there exactly one intended strike, or an extra unintended swing?</li>
<li>Does the same hand hold the same weapon throughout?</li>
<li>Is the contact pose readable at the actual display size?</li>
<li>Does the body move intentionally, or does it drift relative to the game world?</li>
<li>Can the animation transition from and back to the state machine's idle or movement state?</li>
</ul>
<p>Record the observed image index for the best contact pose. That index is a candidate for a gameplay event, not automatically the correct collision frame. Validate it in the game with the real hitbox, sound and input timing.</p>
<h2>A hypothetical twelve-image review</h2>
<p>Assume twelve retained images named with zero-based indices <code>000</code> through <code>011</code>. Image <code>000</code> is the first image; <code>005</code> is the sixth. This example is an authored acceptance plan, not an observed output, timing recommendation or completed engine test. It assumes that the reviewer can identify one wind-up, one strike and a return to the ready pose.</p>
<pre><code class="language-text"> Index:  00 01 02 03 | 04 05 06 | 07 08 09 10 11
 Phase:  preparation | strike   | recovery
 Contact candidate: 05 (sixth image)

 Proposed damage window: [05, 07)
 Includes indices 05 and 06; excludes 07.
</code></pre>
<p>Here the candidate contact is the sixth image, where the planned blade position first reaches the target area. Reject the sequence if the sword changes hands at <code>004</code>, disappears at <code>005</code>, or never settles by <code>011</code>. Those are example rejection conditions; inspect what the actual sequence contains rather than assigning phases from its filenames.</p>
<p>The proposed window is a gameplay design choice expressed against this sample sequence. It does not exist merely because the images were imported. The game must implement and verify the intended collision behavior. A visible blade reaching the target area also does not establish that a collision happened: the target's real position and collision shapes still matter.</p>
<p>Record the visual candidate separately from the gameplay window. If the images have unequal holds or the playback rate changes, image indices alone do not specify elapsed time. Recheck the behavior after any timing change; do not copy an index-to-time assumption from another clip.</p>
<table>
<thead>
<tr>
<th>Review field</th>
<th>Hypothetical plan</th>
<th>What the reviewer must record on the real asset</th>
</tr>
</thead>
<tbody><tr>
<td>Visual contact candidate</td>
<td>Index <code>005</code></td>
<td>Actual candidate index, or rejection if no readable contact exists</td>
</tr>
<tr>
<td>Proposed damage window</td>
<td>Start at <code>005</code>, stop before <code>007</code></td>
<td>The implemented start/end conditions and whether they match the intended feel</td>
</tr>
<tr>
<td>Recovery target</td>
<td>Ready pose by <code>011</code></td>
<td>Whether the final pose can enter the selected next state without a visible jump</td>
</tr>
<tr>
<td>Equipment continuity</td>
<td>Sword in the same hand throughout</td>
<td>Any changed grip, missing blade or shield deformation</td>
</tr>
</tbody></table>
<p>Before blaming the motion itself, confirm the imported images are the intended ones. The <a href="https://framesprite-workflow.hashnode.dev/godot-spriteframes-import-acceptance">Godot SpriteFrames import review</a> covers that separate check.</p>
<h2>Import, then test the behavior</h2>
<p>Godot supports animations from separate images or sprite sheets using <code>AnimatedSprite2D</code>; the <a href="https://docs.godotengine.org/en/4.4/tutorials/2d/2d_sprite_animation.html">Godot 4.4 2D sprite animation documentation</a> explains both paths. Import a test version into the actual engine, name the action explicitly, and configure whether it should loop. An attack that should play once needs a deliberate return to another state; do not assume the importer will choose that behavior.</p>
<p>Finally test interruptions, repeated input and what happens when the character moves during the strike. Those are game rules, not properties a generated animation can settle for you. Keep the original master and rejected outputs so corrections do not overwrite the approved version.</p>
<p>For the hypothetical guard, choose a single strike that ignores additional attack presses until recovery ends, allows movement during the strike, and can be cancelled by a stun. These are example design decisions, not engine defaults. Use the following checks as blank acceptance targets and record the actual result:</p>
<table>
<thead>
<tr>
<th>Input or situation</th>
<th>Expected behavior for this example</th>
<th>Reject or investigate if</th>
</tr>
</thead>
<tbody><tr>
<td>Press attack once</td>
<td>One strike, followed by the selected idle or movement state</td>
<td>The attack repeats or remains stuck on its last image</td>
</tr>
<tr>
<td>Press attack repeatedly during recovery</td>
<td>Extra presses do not restart this attack</td>
<td>The wind-up restarts unexpectedly or one swing creates unintended repeated damage</td>
</tr>
<tr>
<td>Move during the strike</td>
<td>Character movement follows the chosen game rule; artwork and collision stay aligned</td>
<td>The sprite drifts away from the collision position or the weapon points in the wrong direction</td>
</tr>
<tr>
<td>Apply a stun during the proposed damage window</td>
<td>Attack is cancelled and its damage condition ends according to the chosen rule</td>
<td>Damage remains active after the cancellation or the recovery overwrites the stun state</td>
</tr>
</tbody></table>
<p>If the game instead queues attacks, locks movement or permits a different interruption rule, change the expected column before testing. Keep animation-quality decisions separate from a game-rule mismatch: regenerating a sword swing will not by itself define how queued input or stun cancellation should work.</p>
<h2>Related reading</h2>
<p><em>About the author: The FrameSprite Workflow Team documents workflows for its own game-asset tools. <a href="https://www.framesprite.com/about#workflow-team">About the team</a> · <a href="https://www.framesprite.com/contact">Contact and corrections</a>.</em></p>
<p>The <a href="https://framesprite-workflow.hashnode.dev/sprite-alignment-fixed-canvas-acceptance-test">fixed-canvas acceptance test</a> covers a separate spatial alignment check.</p>
]]></content:encoded></item><item><title><![CDATA[Estimate Sprite Sheet Memory Before Exporting a Large Atlas]]></title><description><![CDATA[Calculate RGBA8 atlas memory, compare layouts and split pages without confusing single-buffer estimates with total application memory.]]></description><link>https://framesprite-workflow.hashnode.dev/sprite-sheet-atlas-memory-budget</link><guid isPermaLink="true">https://framesprite-workflow.hashnode.dev/sprite-sheet-atlas-memory-budget</guid><category><![CDATA[Game Development]]></category><dc:creator><![CDATA[FrameSprite Workflow Team]]></dc:creator><pubDate>Wed, 28 Oct 2026 06:18:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6add6fc04b032073c0397857/9daea3c9-fae6-4afc-b026-50968e958db0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A compressed PNG's file size is not the same as the memory required to edit or display its pixels. For a rough budget, an 8-bit RGBA pixel buffer needs four bytes per pixel. Multiply the atlas width by its height and by four; then allow room for other buffers, source frames and application overhead.</p>
<p><em>Disclosure: This article was prepared with AI assistance by the FrameSprite team, which operates game-asset tools. The calculation is an illustrative lower-bound estimate for one particular pixel format, not a browser benchmark or a product capacity promise.</em></p>
<h2>Key takeaways</h2>
<ul>
<li>Estimate pixels times bytes per pixel, not compressed PNG size.</li>
<li>Check dimensions and real device limits as well as memory.</li>
<li>Test the complete export and reopen it in the target workflow.</li>
</ul>
<h2>Work through a concrete example</h2>
<p>Imagine 60 frames, each 960 × 960 pixels, arranged in six columns and ten rows without gaps. The atlas is 5,760 × 9,600 pixels, or 55,296,000 pixels. One 8-bit RGBA buffer takes:</p>
<pre><code class="language-text">5,760 × 9,600 × 4 = 221,184,000 bytes
221,184,000 ÷ 1,048,576 = 210.94 MiB (approximately)
</code></pre>
<p>This is one buffer, not the total memory used by a browser, editor or GPU. A pipeline may keep decoded frames, an output canvas, intermediate copies and encoded data at the same time. Some formats have a different cost: a 16-bit-per-component floating-point image buffer uses more space per pixel than this example. Actual allocation depends on the implementation.</p>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas">MDN's canvas pixel guide</a> explains the four color components in the common 8-bit <code>ImageData</code> representation. It also documents a floating-point option. This calculation deliberately uses the common 8-bit case rather than assuming all images use it.</p>
<h2>Check width and height separately</h2>
<p>Memory is not the only limit. A 5,760 × 9,600 sheet and another arrangement with the same total area can have different maximum dimensions. Canvas and graphics limits vary by browser, device and environment. There is no single safe dimension to promise for every user.</p>
<p>Use the same 60 frames to compare three layouts. These are calculations for tightly packed 960×960 cells, with no margins, gaps, mipmaps or texture compression. MiB values are rounded to two decimals.</p>
<table>
<thead>
<tr>
<th>Layout</th>
<th align="right">Frames per page</th>
<th>Page size</th>
<th align="right">One RGBA8 buffer per page</th>
<th align="right">All pages' pixel buffers</th>
</tr>
</thead>
<tbody><tr>
<td>One page, 6 columns × 10 rows</td>
<td align="right">60</td>
<td>5,760×9,600</td>
<td align="right">210.94 MiB</td>
<td align="right">210.94 MiB</td>
</tr>
<tr>
<td>One page, 10 columns × 6 rows</td>
<td align="right">60</td>
<td>9,600×5,760</td>
<td align="right">210.94 MiB</td>
<td align="right">210.94 MiB</td>
</tr>
<tr>
<td>Two pages, each 6 columns × 5 rows</td>
<td align="right">30</td>
<td>5,760×4,800 each</td>
<td align="right">105.47 MiB</td>
<td align="right">210.94 MiB</td>
</tr>
</tbody></table>
<p>Changing from six columns to ten only rotates the long side; it does not solve a limit that applies equally to width and height. Two pages shorten the longest dimension to 5,760 and halve the size of each individual output buffer. They still contain the same total pixels. If both pages remain decoded at once, their two RGBA8 buffers alone total the original 210.94 MiB. Exporting one page at a time may reduce the live output-buffer workload, but source frames, temporary copies and application cleanup still need measurement.</p>
<p>Add gutters before accepting these dimensions. For <code>c</code> columns of width <code>w</code>, an inter-cell gap <code>g</code> and outer margin <code>m</code> on each side, width is <code>c × w + (c − 1) × g + 2 × m</code>; apply the same rule to rows for height. This formula assumes the gap and outer margins are separate. Record the exporter's actual convention instead of counting the same padding twice.</p>
<p>Before a full export, try a representative sample on the target machine. Check that the saved file can be reopened, has the expected dimensions and includes the final frame. A success message by itself is not proof of a complete usable atlas.</p>
<p>If you're implementing a browser exporter, <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL">MDN notes</a> that <code>toDataURL()</code> encodes the entire image in an in-memory string, and recommends considering <code>toBlob()</code> for larger images. It also documents <code>data:,</code> as a result for a zero-sized canvas or one exceeding the maximum canvas size. Switching APIs does not eliminate all size or memory constraints.</p>
<h2>Reduce the workload deliberately</h2>
<p>If an atlas fails or is unwieldy, choose a change that fits the target engine:</p>
<ul>
<li>Split one large atlas into multiple pages and record which frames belong to each page.</li>
<li>Export a numbered image sequence if the destination supports it, avoiding a single giant canvas.</li>
<li>Reduce frame dimensions only when the resulting art is acceptable at the intended display size.</li>
<li>Export shorter actions separately instead of packing the entire project into one sheet.</li>
</ul>
<p>Splitting does not automatically lower total storage, nor does it ensure an importer supports multiple pages. Confirm the engine's texture limits, import settings and expected animation mapping. Do not downscale the only source copy; retain full-resolution masters for a later revision.</p>
<p>Give each page an explicit frame range, such as source frames 000–029 and 030–059 in this example, and keep the original playback order across the split. If the first frame after the split is wrong, check the page-to-frame mapping before changing the animation. The <a href="https://framesprite-workflow.hashnode.dev/godot-spriteframes-import-acceptance">Godot SpriteFrames import review</a> provides a related check of the animation actually assembled in the engine.</p>
<h2>A preflight checklist</h2>
<p>Use this record for each page, then repeat it on the actual target device. Leave measured fields empty until checked; the example calculations above are not measurements of a successful export.</p>
<table>
<thead>
<tr>
<th>Stage</th>
<th>Record</th>
<th>Acceptance check</th>
</tr>
</thead>
<tbody><tr>
<td>Input</td>
<td>Frame count, cell dimensions, source frame range</td>
<td>Every intended frame appears exactly once</td>
</tr>
<tr>
<td>Layout</td>
<td>Columns, rows, gap, margin, expected width and height</td>
<td>Formula matches the exporter's padding convention</td>
</tr>
<tr>
<td>Budget</td>
<td>Pixel format, bytes per pixel, estimated buffer MiB</td>
<td>Estimate is labeled per buffer, not total application memory</td>
</tr>
<tr>
<td>Saved file</td>
<td>Actual dimensions, format, reopen result</td>
<td>File opens completely and includes the final frame</td>
</tr>
<tr>
<td>Destination</td>
<td>Import settings, page mapping, playback result</td>
<td>Frame order and action timing survive the handoff</td>
</tr>
</tbody></table>
<p>For the saved-file stage, use the <a href="https://framesprite-workflow.hashnode.dev/sprite-sheet-export-qa-12-frame-png-audit">12-frame PNG export audit</a> as a model for checking dimensions and cell coverage. Adjust its frame count to this asset rather than treating twelve as a required layout.</p>
<p>The goal is a usable asset under real conditions, not merely the smallest file. Measure the pipeline you actually use and treat the arithmetic as an early warning, not as a guarantee of success.</p>
<h2>Related reading</h2>
<p><em>About the author: The FrameSprite Workflow Team documents workflows for its own game-asset tools. The cover is an AI-generated concept illustration, not a product screenshot. <a href="https://www.framesprite.com/about#workflow-team">About the team</a> · <a href="https://www.framesprite.com/contact">Contact and corrections</a>.</em></p>
<p>For image-level acceptance after export, see <a href="https://framesprite-workflow.hashnode.dev/transparent-png-light-dark-edge-check">transparent PNG edge QA</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Choose the Camera Before You Ask for a 2D Game Map]]></title><description><![CDATA[Define camera angle, routes, occlusion and output needs before making map art. Use a review checklist to separate images from playable levels.]]></description><link>https://framesprite-workflow.hashnode.dev/game-map-camera-contract</link><guid isPermaLink="true">https://framesprite-workflow.hashnode.dev/game-map-camera-contract</guid><category><![CDATA[Game Development]]></category><dc:creator><![CDATA[FrameSprite Workflow Team]]></dc:creator><pubDate>Wed, 28 Oct 2026 06:07:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6add6fc04b032073c0397857/6ec82c7b-b039-4a0b-861a-f6c7849ae333.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Specify the player's camera and the kind of deliverable before asking an artist or an image generator for a game map. “Top-down forest” can mean a strict overhead grid, a three-quarter scene with visible wall faces, or a picture that only suggests a level. Those interpretations lead to different choices for navigation, occlusion, and tile reuse.</p>
<p>We are the FrameSprite team and build game-asset tools. This is a planning and review guide; no new generation, playtest, or engine import is claimed. The examples describe possible briefs, not a promise that a particular prompt produces a usable level.</p>
<p>This article was drafted with AI assistance. Learn about the team on the <a href="https://www.framesprite.com/about#workflow-team">FrameSprite About page</a>; send corrections through the <a href="https://www.framesprite.com/contact">contact page</a>.</p>
<p><strong>Key points</strong></p>
<ul>
<li>Specify the player's camera before writing the art brief.</li>
<li>Distinguish a concept image from reusable tiles and level data.</li>
<li>Review routes, occlusion and collision separately in the target game.</li>
</ul>
<p>For a related review of camera details in generated descriptions, see our published <a href="https://framesprite-workflow.hashnode.dev/review-image-to-prompt-before-reuse">prompt review guide</a>.
For a separate grid-import check, our published <a href="https://framesprite-workflow.hashnode.dev/unity-sprite-slicing-cell-pivot-qa">Unity slicing guide</a> covers sprite rectangles, offsets and pivots. Our <a href="https://framesprite-workflow.hashnode.dev/sprite-alignment-fixed-canvas-acceptance-test">fixed-canvas alignment guide</a> explains how a stable reference helps diagnose apparent movement between character frames; it does not verify a map's camera or collision.</p>
<h2>Write the gameplay view in plain language</h2>
<p>For a side-view platform stage, describe the ground and platforms as surfaces the player approaches from the side. State where jumps begin and end, how much headroom the character needs, and which parts of the art are foreground decoration. A beautiful ledge is not useful if the silhouette makes the walkable top impossible to distinguish.</p>
<p>For a strict overhead layout, say that the camera looks straight down and identify the grid orientation. A wall's top surface and footprint need to fit that view. If the brief also requests a frontal building facade, the result can mix incompatible camera cues. That may be acceptable for a concept illustration, but it makes a grid-based gameplay handoff harder to interpret.</p>
<p>A three-quarter view intentionally shows both some horizontal surfaces and some vertical faces. It can make a room easier to read, but it also introduces a depth and occlusion question: when the character walks behind a wall, which layer draws in front? The answer belongs in the engine or scene setup, not solely in the image prompt.</p>
<h2>Choose the output before adding style details</h2>
<p>An art direction image is useful for palette, atmosphere, and composition. A tileset is a reusable collection of tile graphics. A tilemap is placement data using those tiles. One output is not a substitute for the others.</p>
<p>For example, an image of a temple room can communicate lighting and landmark placement while failing a tilemap contract: individual floor pieces might not repeat, perspective lines might cross grid cells, and decorative objects might be baked into the floor. If the team needs an editable level, request reusable tiles or a separate atlas and document how those pieces connect.</p>
<p>Tiled's <a href="https://doc.mapeditor.org/en/stable/manual/layers/">layer documentation</a> distinguishes tile, image, and object layers. A fixed background image can coexist with tile and object data; the team should still specify which layer controls navigation, foreground occlusion, and collision in the receiving game.</p>
<h2>Describe the route before the scenery</h2>
<p>Start the brief with an entrance, exit, central route, blocked areas, and a landmark. Then add the biome, palette, lighting, and props. A workable example might be:</p>
<pre><code class="language-text">View: strict overhead, orthogonal grid
Use: visual concept for a small temple room
Route: entrance south, exit north, central path remains open
Obstacles: walls around the perimeter, two columns outside the path
Art: weathered stone, cool shadows, one warm central landmark
Deliverables: concept image plus a separately designed reusable tileset
</code></pre>
<p>The final line is a request, not evidence the resulting image has become a tileset. A team must still author or verify the separate tiles and map data. If the design uses a three-quarter camera instead, revise the brief to state wall face height and the intended treatment of characters passing behind it.</p>
<h2>Review the layout with a character footprint</h2>
<p>Put a simple rectangle representing the character's collision footprint over the route. Test whether it fits through each passage, whether an apparent entrance is reachable, and whether props obscure turns. This need not be a polished character. The goal is to reveal spatial contradictions early.</p>
<p>Then compare art and data separately:</p>
<ul>
<li><strong>Art:</strong> Is the route visually readable without relying on a collision overlay?</li>
<li><strong>Grid:</strong> Do tile boundaries align with the intended camera and tile dimensions?</li>
<li><strong>Collision:</strong> Do blocked cells or shapes match the playable route?</li>
<li><strong>Objects:</strong> Are spawns, exits, and interactive items placed in known coordinates?</li>
<li><strong>Occlusion:</strong> Can the player still see the character where foreground layers overlap?</li>
</ul>
<p>If the concept has a dramatic pillar drawn in the center of the route but the data lets the player pass through it, decide whether it is scenery, a collision obstacle, or a visual error. Do not quietly assume the image already determines the correct game rule.</p>
<h3>Worked diagnosis: a wall beside a narrow path</h3>
<p>Imagine a strict overhead corridor two grid cells wide, with the character's footprint intended to fit within one cell. The concept places a wall edge along the western side, but a decorative stone protrudes into the visible corridor. This is a hypothetical review case, not a reported playtest.</p>
<ol>
<li>Mark the intended walkable cells and the character footprint on a copy of the art. If the decorative stone appears to occupy a walkable cell, decide whether it should block movement or be redrawn outside the route.</li>
<li>Compare that decision with the actual collision shape or blocked-cell data in the receiving project. If the data blocks more space than the artwork suggests, adjust the data or revise the artwork so the route communicates the restriction.</li>
<li>If the same scene instead uses a three-quarter view, distinguish the wall's ground footprint from its raised visible face. Check whether that face is drawn in front of the character where the character passes behind it; do not treat all visible wall pixels as ground collision automatically.</li>
<li>Inspect the corridor using the intended camera and at gameplay scale. If the route remains ambiguous, record the art, collision or draw-order revision needed and repeat the check in the target project.</li>
</ol>
<h2>Keep the generator and game responsibilities clear</h2>
<p>FrameSprite's <a href="https://www.framesprite.com/guides/ai-2d-game-map-generator">map workflow guide</a> distinguishes map art from reusable terrain and tilemap data; the <a href="https://www.framesprite.com/tools/tilemap-builder">Tilemap Builder</a> can create a starter layout from compatible tile inputs. The map art and a separate tile example are not necessarily the same generated asset. Review what was actually exported and do not claim a playable level merely because a preview looks complete.</p>
<p>For handoff, include the camera description, source image or tileset, grid size, map data, layer meanings, coordinate convention, and a list of checks not yet performed. Test the resulting scene in the target engine and gameplay camera, including movement, collision, and draw order. The right visual angle is the one that supports the game's interaction, not just the one that looks appealing in isolation.</p>
<p><em>Cover image: AI-generated conceptual illustration, not a screenshot of the product.</em></p>
]]></content:encoded></item><item><title><![CDATA[Eight-Direction Sprite QA: Facing, Identity and Mirrored Details]]></title><description><![CDATA[Review eight static sprite directions for facing, camera, identity, scale and mirrored equipment, with a reusable acceptance record and unknowns.]]></description><link>https://framesprite-workflow.hashnode.dev/eight-direction-facing-identity-mirror-qa</link><guid isPermaLink="true">https://framesprite-workflow.hashnode.dev/eight-direction-facing-identity-mirror-qa</guid><category><![CDATA[Game Development]]></category><dc:creator><![CDATA[FrameSprite Workflow Team]]></dc:creator><pubDate>Mon, 26 Oct 2026 03:56:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6add6fc04b032073c0397857/33d4fc4d-51dc-4cec-8a4e-3e5988513c03.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Accept an eight-direction sprite set only after each image passes separate checks for facing, camera, identity, scale and asymmetric details. A complete contact sheet can still contain a mislabeled diagonal, a changed costume or a mirrored weapon on the wrong side. Review the static images against a written direction convention before treating them as an approved set.</p>
<p>This review covers eight static facings. It does not validate animation timing, movement input, texture packing or an engine's final direction mapping. The <a href="https://www.framesprite.com/guides/eight-direction-character-sprite-generator">eight-direction character guide</a> identifies facing, identity, equipment side and camera as review concerns. The procedure below turns those concerns into explicit manual decisions without assuming current generation modes or plan limits.</p>
<blockquote>
<p><strong>Key Takeaways</strong></p>
<ul>
<li>Define screen-space names and the camera view before judging the directions.</li>
<li>Compare each view with its neighbors and with its opposite.</li>
<li>Check the same character across views without demanding identical projected silhouettes.</li>
<li>Record mirrors as derived images and verify asymmetric equipment manually.</li>
</ul>
</blockquote>
<p><img src="https://www.framesprite.com/guides/game-assets/eight-direction-character-grid.svg" alt="An eight-direction character grid illustrates named positions around a central reference." /></p>
<p><em>This owned contract diagram explains an eight-direction arrangement. It is not a set of eight newly generated or independently tested outputs.</em></p>
<h2>Write the screen-space facing convention first</h2>
<p>Direction labels need a shared meaning. For this review, "up" means the character faces toward the top of the screen, and "right" means toward the right edge. These are facing labels, not a claim that the character is moving.</p>
<p>The table uses a screen-space coordinate convention with positive X to the right and positive Y downward. <a href="https://docs.godotengine.org/en/stable/classes/class_vector2.html">Godot's Vector2 documentation</a> provides one example of that convention, including UP as <code>(0, -1)</code>. The vectors below identify directions only; diagonal magnitudes are irrelevant to this visual review. They do not prove that any engine import has been configured correctly.</p>
<table>
<thead><tr><th>Label</th><th>Screen direction</th><th>Direction vector</th><th>Opposite</th></tr></thead>
<tbody>
<tr><td>Up</td><td>Top edge</td><td>(0, -1)</td><td>Down</td></tr>
<tr><td>Up-right</td><td>Top-right corner</td><td>(1, -1)</td><td>Down-left</td></tr>
<tr><td>Right</td><td>Right edge</td><td>(1, 0)</td><td>Left</td></tr>
<tr><td>Down-right</td><td>Bottom-right corner</td><td>(1, 1)</td><td>Up-left</td></tr>
<tr><td>Down</td><td>Bottom edge</td><td>(0, 1)</td><td>Up</td></tr>
<tr><td>Down-left</td><td>Bottom-left corner</td><td>(-1, 1)</td><td>Up-right</td></tr>
<tr><td>Left</td><td>Left edge</td><td>(-1, 0)</td><td>Right</td></tr>
<tr><td>Up-left</td><td>Top-left corner</td><td>(-1, -1)</td><td>Down-right</td></tr>
</tbody>
</table>

<p>Record the camera separately. A three-quarter overhead view and a true 90-degree top-down view expose different surfaces, as the <a href="https://www.framesprite.com/guides/top-down-character-sprite-generator">top-down character guide</a> explains. Do not accept a mixed set because all eight labels are present. If the brief calls for one camera treatment, a direction that suddenly reveals much more of the top of the head needs review.</p>
<p>Avoid assigning labels from filenames alone. Inspect the torso, head and feet together. A turned head can disagree with the body's facing, and a long weapon can point away from the character's main orientation. State whether the project defines facing by the torso or another approved cue.</p>
<h2>Compare adjacent views, then opposite views</h2>
<p>Adjacent comparisons reveal abrupt changes; opposite comparisons reveal front-to-back contradictions. Both matter because a set can look coherent around part of the circle while hiding a single incorrect image.</p>
<p>Arrange the images clockwise using the agreed labels. Compare up with up-right, up-right with right, and continue around the circle until up-left returns to up. Check that the perceived facing advances in the intended order and that the camera treatment remains compatible. Do not expect exact geometric rotations from stylized drawings, but reject an image that plainly faces the wrong sector.</p>
<p>Then compare the four opposite pairs shown in the table. Look for coherent transitions between front and back costume regions. A front chest emblem should not appear on the back simply because a similar shape filled that space. A rear-facing view also need not reveal the same facial information as a front-facing one.</p>
<p>Record the reason for uncertainty when the design makes facing ambiguous. A nearly symmetric robot or hooded figure may offer few cues. Seek an approved design cue or a clearer reference rather than declaring the label correct because it occupies the expected cell.</p>
<h2>Keep identity, scale and ground position distinct</h2>
<p>Identity means the views describe the same approved design, not that their silhouettes occupy identical rectangles. Different facings expose different parts of a cape, face or weapon. Compare stable features while allowing for projection and occlusion.</p>
<p>Check the head-to-body relationship, hair or helmet construction, clothing layers, palette relationships and distinctive equipment. Separate a legitimate hidden feature from a changed one. A belt ornament that disappears behind the body can be expected; a visible ornament changing shape needs a decision.</p>
<p>Review scale and ground position on the same inspection canvas. Use the project's defined ground point or foot-contact convention, rather than centering every image by its visible bounds. A raised sword can change those bounds without moving the character's feet.</p>
<p>Check for apparent growth or shrinkage across the set. The goal is a coherent character at the intended size, not identical pixel height where a pose or protrusion reasonably changes it. If the project has not defined a ground point or size rule, record that missing contract before assigning a pass.</p>
<h2>Inspect mirrored asymmetric details on the character's body</h2>
<p>A horizontal mirror changes left and right in the image. It can supply a derived opposite-side facing, but it can also reverse a shield, shoulder pad, scar or weapon placement. Track a feature by the character's own left or right side, not only by the side of the screen where it appears.</p>
<p>Horizontal reflection pairs left with right, up-left with up-right, and down-left with down-right. It does not exchange up with down. Do not label a mirrored file as newly generated artwork. Record its source and transformation so a later reviewer knows where to look for inherited errors.</p>
<p>For a character with a left-arm shield, compare visible shield placement against the body across all views. A screen-left shield may be correct in one view and wrong in another. If a derived mirror puts it on the wrong arm, either revise the asset or document an explicit art-direction exception. "Mirrored" describes a process, not an automatic quality verdict.</p>
<p>Keep unseen rear details unknown. Mirroring a front or side view cannot establish what the back of an accessory should look like. A full design reference or an approved design decision is needed when that region matters.</p>
<h2>Record accept, revise or unknown for each view</h2>
<p>Use one row per direction and a separate set-level verdict. This prevents seven good images from concealing one unresolved view. A reviewer can accept the facing while holding equipment placement for correction.</p>
<pre><code class="language-text">Set ID / version:
Master design reference:
Camera contract:
Facing cue and screen-space naming convention:
Ground-point and size convention:

Direction:
File / version:
Origin: generated / drawn / derived
If derived, source file and transformation:
Facing: accept / revise / unknown
Camera: accept / revise / unknown
Identity anchors: accept / revise / unknown
Scale and ground point: accept / revise / unknown
Asymmetric details by character side: accept / revise / unknown
Adjacent comparison notes:
Opposite comparison notes:
Hidden details and remaining unknowns:
Required correction:

Set decision: accept / revise / hold
Reviewer and date:
</code></pre>
<p>Accept the static set when all required views have a supported decision and material unknowns have an approved resolution. Identity agreement does not establish matching gait phase, which needs the separate <a href="https://www.framesprite.com/guides/eight-direction-animation-phase-qa">animation phase review</a>. Keep this static acceptance record with the files so later animation or integration work starts from a clearly reviewed design.</p>
<hr />
<p><strong>Author and disclosure:</strong> <a href="https://www.framesprite.com/about#workflow-team">FrameSprite Workflow Team</a> maintains these workflow notes. The team is affiliated with the product and owns the product sources and diagram linked here; these are not independent product evaluations. This draft was prepared with AI writing assistance. Send corrections through the <a href="https://www.framesprite.com/contact">contact page</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Does This Portrait Still Match the Full-Body Master?]]></title><description><![CDATA[Compare one dialogue portrait with its full-body master using visible identity anchors, matched viewing sizes, crop checks and a clear review record.]]></description><link>https://framesprite-workflow.hashnode.dev/dialogue-portrait-full-body-master-review</link><guid isPermaLink="true">https://framesprite-workflow.hashnode.dev/dialogue-portrait-full-body-master-review</guid><category><![CDATA[Game Development]]></category><dc:creator><![CDATA[FrameSprite Workflow Team]]></dc:creator><pubDate>Mon, 26 Oct 2026 03:49:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6add6fc04b032073c0397857/d9a902d5-f7ea-4ba6-9bce-6f947f0f3c42.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Accept a dialogue portrait when its visible identity anchors agree with the full-body master and any differences fit the intended expression, camera or crop. Revise it when it changes a defining feature. Mark a feature unknown when the master is too small, turned away or covered to support a comparison.</p>
<p>This is a manual review of one derivative portrait against one master image. It is not face recognition, an identity score or evidence of a model's consistency rate. The <a href="https://www.framesprite.com/guides/character-portrait-from-full-body-reference">full-body-to-portrait workflow guide</a> uses a supplied master, one portrait at a time and a side-by-side comparison. Shared source lineage helps organize the review; it does not guarantee a visual match.</p>
<blockquote>
<p><strong>Key Takeaways</strong></p>
<ul>
<li>Compare details visible in both images before judging newly exposed detail.</li>
<li>Use a matched head size for identity checks and the intended display size for readability.</li>
<li>Separate expression and cropping changes from costume or identity redesign.</li>
<li>Record the master, derivative and unresolved features with each decision.</li>
</ul>
</blockquote>
<p><img src="https://www.framesprite.com/guides/game-assets/game-asset-image-prompt-contract.svg" alt="A visual asset contract diagram connects a reference image with explicit prompt and output requirements." /></p>
<p><em>This owned reference-contract diagram explains how to keep requirements explicit. It does not depict the character pair discussed below or prove a portrait match.</em></p>
<h2>Start with identity anchors visible in both images</h2>
<p>Choose anchors from the master before inspecting the portrait in detail. This gives the comparison a stable basis and reduces the temptation to accept an attractive redesign. The <a href="https://www.framesprite.com/guides/ai-character-consistency">character consistency guide</a> recommends visible identity anchors tied to the same master, separate from changing actions and emotions.</p>
<p>Useful anchors include the hair silhouette, fringe shape, ponytail position, eye appearance when readable, collar shape, clothing color relationships and a distinctive accessory. Select what distinguishes this particular character. "Dark clothes" is weaker than a specific high collar paired with a particular shoulder ornament.</p>
<p>A historical saved pair illustrates the limits of this method. The master shows a black-haired swordsman in dark clothing, viewed from the side at full-body scale. The saved portrait shows a near-frontal upper-body crop with a ponytail, dark clothing and related costume ornaments. Both images were retained from an earlier workflow; they are not fresh outputs for this article.</p>
<table>
<thead>
<tr>
<th>Archived full-body master</th>
<th>Archived derivative portrait</th>
</tr>
</thead>
<tbody><tr>
<td><img src="https://cdn.hashnode.com/uploads/covers/6add6fc04b032073c0397857/3fd4f89d-d3b4-4df6-babb-a2e6eeb93460.png" alt="Archived side-view full-body swordsman master with black hair and dark clothing." /></td>
<td><img src="https://cdn.hashnode.com/uploads/covers/6add6fc04b032073c0397857/a511454b-16b4-4659-811b-60b2533a0c5b.png" alt="Archived near-frontal upper-body portrait with ponytail and dark costume details." /></td>
</tr>
</tbody></table>
<p><em>Owned saved demonstration pair from 2026-10-04. These are the retained master and derivative, shown without a new generation. The different view and small master face limit which anchors can be checked; the pair does not establish perfect identity or a passed dialogue-interface test.</em></p>
<p>Those images provide shared hair, palette and costume cues. The master face occupies much less of its image and exposes a different view. Fine face geometry and newly visible facial detail therefore remain harder to verify. The record supports a bounded comparison, not a claim of perfect identity.</p>
<h2>Match viewing sizes without distorting the character</h2>
<p>Compare the head regions at roughly the same displayed height to inspect identity, then view the portrait at its planned dialogue size to inspect readability. These are different checks. A large portrait can reveal errors that disappear in a small panel, while a detailed portrait can become unclear when reduced.</p>
<p>Crop a viewing copy around the master's head and shoulders. Scale the viewing copies proportionally; do not stretch width or height to make the faces line up. Enlarging the master helps inspection but adds no missing source detail. A blurred eye stays uncertain even when it fills the screen.</p>
<p>Keep the uncropped master available. It provides context for a collar, hair tie or shoulder accessory that a tight face crop might remove. Also check the portrait's full crop: the face may match while a required clasp or ponytail disappears outside the frame.</p>
<p>If the final dialogue panel is available, preview the portrait there. Record that as a separate display check. This article does not claim that the historical pair passed a real dialogue-interface switch or crop test.</p>
<h2>Review hair, eyes, neckline and accessories separately</h2>
<p>Reviewing anchors individually gives more useful feedback than "looks different." It identifies whether the next revision should preserve the face and fix a collar, or keep the costume and correct a changed hairstyle.</p>
<table>
<thead><tr><th>Anchor</th><th>What to compare</th><th>What may remain unknown</th></tr></thead>
<tbody>
<tr><td>Hair</td><td>Major fringe shapes, color relationships, ponytail and tie</td><td>Hidden side or back structure</td></tr>
<tr><td>Eyes</td><td>Visible shape and appearance at readable scale</td><td>Fine iris detail absent from the small master</td></tr>
<tr><td>Neckline</td><td>Collar height, scarf shape and clothing layers</td><td>Structure covered by hair or a changed angle</td></tr>
<tr><td>Accessories</td><td>Distinctive ornament, strap and visible placement</td><td>Covered attachments or unseen opposite-side details</td></tr>
<tr><td>Palette</td><td>Relative dark, light and accent color relationships</td><td>Exact color values without a separate color specification</td></tr>
</tbody>
</table>

<p>For the historical swordsman, a sensible review would compare the ponytail and its tie, the swept fringe, the high dark neckline and the visible shoulder decoration. It would avoid asserting that every facial detail is unchanged. A near-frontal portrait reveals information that a small side-view master cannot fully constrain.</p>
<p>Write the correction as an observable requirement. "Restore the master's high collar" is actionable. "Make the character more consistent" leaves the editor to guess which feature caused the rejection. The <a href="https://www.framesprite.com/guides/ai-game-art-prompt-examples">prompt examples guide</a> provides a related method: define the deliverable in visible terms and change one field at a time.</p>
<h2>Distinguish an expression change from a redesign</h2>
<p>Judge differences against the requested derivative. A smile, raised brow or new crop may be intentional. A replacement hair ornament, different neckline or altered hair construction needs separate approval even if it improves the picture.</p>
<p>Camera changes also affect appearance. A side view and a near-frontal view expose different portions of the face and costume. Do not demand identical outlines where the view necessarily changes them. Instead, ask whether the visible structure remains compatible with the master, and mark the newly revealed regions as unresolved when no other approved reference exists.</p>
<p>Use these decisions consistently:</p>
<ul>
<li><strong>Accept:</strong> visible anchors match closely enough for the declared portrait use; allowed expression and crop changes are documented.</li>
<li><strong>Revise:</strong> a visible anchor conflicts with the master or a required detail is cropped out.</li>
<li><strong>Unknown:</strong> the source cannot resolve a material feature; obtain another approved reference or a design decision.</li>
</ul>
<p>Unknown does not mean defective. It means the review lacks evidence. If a newly exposed facial detail matters to the production, ask the design owner to approve it before treating this portrait as another master.</p>
<h2>Keep lineage with the single-portrait decision</h2>
<p>Record which master produced which derivative, but keep that record separate from the visual verdict. A filename, shared prompt or source identifier establishes a relationship between files. It does not prove that their visible details match.</p>
<p>Use a review record like this:</p>
<pre><code class="language-text">Master file / version:
Portrait file / version:
Requested derivative: crop, camera, expression
Identity anchors selected before comparison:
Matched-size head comparison completed: yes / no
Intended display-size check: passed / revise / not performed
Hair:
Eyes, including limits of master detail:
Neckline and clothing layers:
Accessories and visible placement:
Allowed expression or camera differences:
Unverifiable regions:
Decision: accept / revise / unknown
Required correction or design approval:
Reviewer and date:
</code></pre>
<p>Review a corrected portrait as a new derivative version. Preserve the previous decision so a later change does not inherit an approval it never received. When the shared anchors match and unresolved regions have an explicit disposition, the portrait has a clear acceptance basis for its intended use. That basis is more useful than an unsupported claim that it is "the same character."</p>
<hr />
<p><strong>Author and disclosure:</strong> <a href="https://www.framesprite.com/about#workflow-team">FrameSprite Workflow Team</a> maintains these workflow notes. The team is affiliated with the product and owns the product sources and diagram linked here; these are not independent product evaluations. This draft was prepared with AI writing assistance. Send corrections through the <a href="https://www.framesprite.com/contact">contact page</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Review an Image-to-Prompt Result Before You Reuse It]]></title><description><![CDATA[Check image-to-prompt text against its reference, separate visible facts from guesses, and correct subject, camera, palette and material fields.]]></description><link>https://framesprite-workflow.hashnode.dev/review-image-to-prompt-before-reuse</link><guid isPermaLink="true">https://framesprite-workflow.hashnode.dev/review-image-to-prompt-before-reuse</guid><category><![CDATA[Game Development]]></category><dc:creator><![CDATA[FrameSprite Workflow Team]]></dc:creator><pubDate>Mon, 26 Oct 2026 03:43:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6add6fc04b032073c0397857/6aadf31a-2594-428c-9259-d3f3e9ba39f8.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Review an image-to-prompt result by checking each claim against the reference image before copying the text into another workflow. Keep visible details, qualify interpretations, and remove invented details. A fluent description can still misread the subject, confuse lighting with material, or name a camera angle more precisely than the image supports.</p>
<p>The useful output is a corrected description with known limits. The <a href="https://www.framesprite.com/tools/image-to-prompt">tool's input and output guide</a> describes fields such as silhouette, pose, camera, composition, material, rendering and palette. Its FAQ also distinguishes description from exact reconstruction. Reusing the text does not establish that a later image will reproduce the original.</p>
<blockquote>
<p><strong>Key Takeaways</strong></p>
<ul>
<li>Compare the image and extracted fields side by side before copying.</li>
<li>Check subject nouns and silhouette before judging style adjectives.</li>
<li>Separate camera from composition, and palette from material and light.</li>
<li>Keep uncertain claims visible in the review record rather than silently treating them as facts.</li>
</ul>
</blockquote>
<p><img src="https://www.framesprite.com/guides/game-assets/game-asset-image-prompt-contract.svg" alt="A game asset contract diagram separates visual reference information from prompt and output requirements." /></p>
<p><em>This owned workflow diagram illustrates a reference-to-prompt contract. It is not a screenshot of an extraction or a newly generated result.</em></p>
<h2>Put the reference beside the extracted fields</h2>
<p>Review the image at a useful viewing size and preserve the original extracted text. Work on a separate corrected copy so another reviewer can tell what changed. A prompt that reads well on its own gives no evidence that its details match the source.</p>
<p>Use three labels during the comparison:</p>
<ul>
<li><strong>Supported:</strong> the feature is visible at the supplied resolution.</li>
<li><strong>Revise:</strong> the wording conflicts with the image or overstates what it shows.</li>
<li><strong>Unknown:</strong> the image does not show enough to decide.</li>
</ul>
<p>Unknown is especially useful for hidden surfaces, exact materials, lens choices and tiny accessories. Do not fill these gaps with familiar character conventions. If a cape covers the back, its hidden clasp remains unknown even when a clasp would make sense.</p>
<p>Confirm the right to reuse the source separately. A successful visual description does not determine ownership, license terms or permission to reproduce a design. Those questions need their own evidence.</p>
<h2>Check the subject and silhouette nouns first</h2>
<p>Subject nouns determine what the next request asks for. Check them before phrases such as "cinematic" or "highly detailed." If the image shows a lamp replacing a knight's head, describing a helmet with a lamp attached changes the design even when both descriptions sound plausible.</p>
<p>A historical recorded analysis provides a useful example. Its reference shows an armored humanoid with a lit table lamp as its head against a dark background. The saved field table classifies the asset as "Character," gives "Painterly" as the closest style, and describes an open palm gesture, cool metallic silver and warm yellow glow. These are observations from an archived input and saved analysis, not a tool run performed for this article.</p>
<p><img src="https://cdn.hashnode.com/uploads/covers/6add6fc04b032073c0397857/9527f771-61f5-4fae-a881-3ba066d20b43.webp" alt="Archived lamp-headed knight reference used for the saved image description." /></p>
<p><img src="https://cdn.hashnode.com/uploads/covers/6add6fc04b032073c0397857/d3073107-ceff-409c-93fe-5c973eada3d0.png" alt="Archived extracted field table: Character, Painterly, lamp-headed knight, pose, palette and rendering." /></p>
<p><em>Archived on 2026-10-07 from our own demonstration. The first image is the retained reference; the second is the saved field-table screenshot. They document that recorded extraction, not a new request or a verified complete clipboard prompt. Camera and material wording still require the review described here.</em></p>
<p>For that record, "table lamp as head" preserves the subject's defining construction. "Armored knight" alone loses it. "Open palm gesture" is a reasonable pose description to compare with the raised hands. The style label is a broad approximation, not a measurement of the original painting process.</p>
<p>The <a href="https://www.framesprite.com/guides/ai-game-art-prompt-examples">game art prompt examples guide</a> organizes prompts into fields and observable deliverables. Apply that approach here: write down the feature that a reviewer must be able to see, then check whether the extracted wording actually requests it.</p>
<table>
<thead><tr><th>Field</th><th>Review question</th><th>Correction when needed</th></tr></thead>
<tbody>
<tr><td>Subject</td><td>Does the noun describe the visible object or character?</td><td>Replace a generic category with the defining construction.</td></tr>
<tr><td>Silhouette</td><td>Are the major masses and attachments visible?</td><td>Keep visible shapes; remove hidden construction claims.</td></tr>
<tr><td>Pose</td><td>Does the description match the limbs and gesture?</td><td>Describe the visible pose without adding an action sequence.</td></tr>
<tr><td>Identity anchors</td><td>Which features should survive reuse?</td><td>List observable anchors separately from mood and pose.</td></tr>
</tbody>
</table>

<p>The <a href="https://www.framesprite.com/guides/ai-character-consistency">character consistency guide</a> separates visible identity anchors from changing action or emotion. That distinction prevents a pose from becoming an accidental permanent trait. A raised hand can change; the lamp-head construction may be the feature the project needs to preserve.</p>
<h2>Separate camera interpretation from composition</h2>
<p>Camera describes the apparent viewpoint; composition describes how the subject occupies the image. "Centered, full body" does not establish "front-facing, eye-level." A character can face slightly to one side while remaining centered and fully visible.</p>
<p>The archived field table uses front-facing and eye-level wording. Treat those as interpretations to review, not absolute ground truth. The reference supports a largely frontal presentation with visible depth, but it does not provide camera metadata. If exact angle matters, use cautious wording such as "near-frontal view" and record that camera height is inferred.</p>
<p>Composition is often easier to check directly. Inspect the feet, head and side edges for cropping. Check whether a single subject dominates the frame and whether a background contains visible objects. "No additional elements" should not erase a visible prop merely because the extractor treats it as part of the character.</p>
<p>Keep the two decisions independent in the corrected text:</p>
<pre><code class="language-text">Viewpoint: near-frontal; exact camera height not established.
Composition: one full-body character, centered on a dark background.
</code></pre>
<p>This note describes the review outcome. It is not a complete recreation prompt.</p>
<h2>Separate palette, material and lighting</h2>
<p>Color words describe appearance, not exact color values. "Cool silver" can be useful when reviewing armor, but it does not specify a reproducible swatch. A warm yellow region may come from emitted light, reflected light or a surface color.</p>
<p>In the historical reference, the lamp glows warmly while the armor has cool highlights and visible purple reflections. The purple is observable, but its appearance does not prove that the armor's base material is purple. "Silver-colored reflective armor with purple reflections" preserves that distinction better than "purple armor."</p>
<p>Check material claims at the same level of caution. Reflective shading can support a metallic appearance. It cannot establish a particular alloy. A lampshade may look fabric-like without revealing its fiber composition. Use "metallic-looking" or "fabric-like" when the exact substance is not known.</p>
<p>Keep rendering language equally bounded. "Painterly" and "soft blended brushwork" describe visible appearance. They do not identify the artist, software or physical technique used to create the source.</p>
<h2>Correct fields before copying the final text</h2>
<p>Change one field at a time and keep a short reason for each edit. That makes the review traceable and helps avoid introducing a contradiction while fixing another. If a later generation is part of your workflow, compare it against the corrected requirements separately.</p>
<p>Do not assume that a Copy control includes every field displayed on a page. The archived evidence for this example does not establish a complete copied prompt. Before reuse, inspect the actual pasted text and check that it contains the fields you approved. A screenshot of a field table is not proof of the clipboard contents.</p>
<p>Use this acceptance record for one extraction:</p>
<pre><code class="language-text">Reference ID / file:
Source-use permission checked separately: yes / pending
Original extracted text saved:
Subject and silhouette: supported / revise / unknown
Pose: supported / revise / unknown
Camera interpretation and confidence:
Composition and crop:
Palette appearance, without claiming measured color values:
Material appearance versus known material:
Unsupported details removed:
Corrected text pasted and checked for completeness: yes / no
Decision: accept for descriptive reuse / revise / hold
Reviewer and date:
</code></pre>
<p>Accept the text when its important claims match visible evidence and its unknowns will not mislead the next user. Hold it when an unresolved detail changes the design brief. A clear description is useful precisely because it states both what the image shows and what it cannot establish.</p>
<hr />
<p><strong>Author and disclosure:</strong> <a href="https://www.framesprite.com/about#workflow-team">FrameSprite Workflow Team</a> maintains these workflow notes. The team is affiliated with the product and owns the product sources and diagram linked here; these are not independent product evaluations. This draft was prepared with AI writing assistance. Send corrections through the <a href="https://www.framesprite.com/contact">contact page</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Unity Sprite Slicing QA: Cell Size, Cell Count and Pivot]]></title><description><![CDATA[Check Unity 6.0 sprite slicing against cell size or count, padding, empty cells, SpriteRects, and pivot units, then verify the applied sub-assets.]]></description><link>https://framesprite-workflow.hashnode.dev/unity-sprite-slicing-cell-pivot-qa</link><guid isPermaLink="true">https://framesprite-workflow.hashnode.dev/unity-sprite-slicing-cell-pivot-qa</guid><category><![CDATA[Game Development]]></category><dc:creator><![CDATA[FrameSprite Workflow Team]]></dc:creator><pubDate>Mon, 26 Oct 2026 03:14:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6add6fc04b032073c0397857/89ab4107-569e-4a10-8185-2182af49da8a.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Accept a Unity sprite sheet slice when the generated SpriteRects match the export contract, their pivots use the intended units and positions, and the applied texture exposes the expected sprite sub-assets. A grid that looks plausible in the Sprite Editor is only the start of that check.</p>
<p>This workflow uses Unity 6.0 documentation and the Multiple sprite import path. It assumes the exported artwork has already passed source-image review. It addresses slicing and pivot acceptance, with no claim that the illustrated material was imported into Unity for this article.</p>
<blockquote>
<p><strong>Key Takeaways</strong></p>
<ul>
<li>Choose Grid by Cell Size or Grid by Cell Count from the known export layout.</li>
<li>Verify offset, padding, and empty-cell policy before accepting the rectangles.</li>
<li>Inspect pivots in the Sprite Editor for a Multiple texture and record their units.</li>
<li>Apply the result, then verify the resulting sprite sub-assets and their use in the project.</li>
</ul>
</blockquote>
<h2>Preserve the existing slice before changing it</h2>
<p>Keep a recoverable version of the texture and its import settings before re-slicing an asset already in use. The Slice dialog's method controls how the new result interacts with existing SpriteRects. Re-slicing is therefore an edit to an asset definition, not just a harmless preview operation. Review the available methods in the <a href="https://docs.unity3d.com/6000.0/Documentation/Manual/sprite/sprite-editor/sprite-editor-window-reference.html">Unity 6.0 Sprite Editor reference</a> before applying a change.</p>
<p>Write down the input contract: image revision, expected cell dimensions or grid count, margins, gutters, occupied cells, and intended pivots. Also record the expected sprite names or the naming convention used by downstream animation work. Attach the reviewed export evidence; the <a href="https://framesprite-workflow.hashnode.dev/sprite-sheet-export-qa-12-frame-png-audit">12-frame PNG export QA example</a> shows the separate file-level checks to complete before treating slicing as the remaining task.</p>
<p><img src="https://www.framesprite.com/guides/spritesheet-example.webp" alt="A published sprite sheet provides a visual example of poses arranged for grid-based slicing." /></p>
<p><em>Existing public sprite-sheet illustration. This is source material, not a Unity interface screenshot, and it does not establish any particular cell dimensions or import result.</em></p>
<p>Do not infer exact dimensions from a scaled image on a web page. Use the actual export specification and source image dimensions. If they disagree, stop at the contract mismatch rather than compensating with guessed slice settings.</p>
<h2>Use the Multiple sprite import path</h2>
<p>Select the texture, set Texture Type to Sprite (2D and UI), and use Sprite Mode Multiple for a sheet that should produce several sprites. Open the Sprite Editor to inspect and edit those sprites. Unity's <a href="https://docs.unity3d.com/6000.0/Documentation/Manual/texture-type-sprite.html">Sprite texture import settings</a> describe this path and distinguish Single from Multiple.</p>
<p>The Inspector's Pivot setting is visible for Single mode. For a Multiple texture, inspect individual sprite pivots in the Sprite Editor. Looking for a Single-mode control in the Inspector can lead to changing the wrong setting or assuming a pivot has been applied to every slice.</p>
<p>Keep Pixels Per Unit in the same acceptance record. It describes how source pixels map to world units, rather than defining the sheet's cell size. For example, a 64-pixel-wide sprite at 64 Pixels Per Unit has a width of one world unit before other transforms. That is an illustrative conversion based on the import relationship, not a recommended universal scale.</p>
<h2>Choose cell size or cell count from known information</h2>
<p>Use Grid by Cell Size when the contract gives the dimensions of each cell. Use Grid by Cell Count when the contract gives the number of columns and rows. The <a href="https://docs.unity3d.com/6000.0/Documentation/Manual/sprite/sprite-editor/use-editor.html">Unity 6.0 slicing instructions</a> describe these grid-based options. If the source layout is undocumented, use the <a href="https://www.framesprite.com/guides/how-to-make-a-sprite-sheet">sprite-sheet preparation guide</a> to identify the canvas and spacing decisions that the export contract needs to state before you choose a slicing mode.</p>
<table>
<thead><tr><th>Known input</th><th>Slicing choice</th><th>Acceptance check</th></tr></thead>
<tbody>
<tr><td>Cell width and height in pixels</td><td>Grid by Cell Size</td><td>Generated rectangles have the specified dimensions and start positions.</td></tr>
<tr><td>Number of columns and rows</td><td>Grid by Cell Count</td><td>Divisions match the intended layout after accounting for margins and gutters.</td></tr>
<tr><td>Only the visible character shapes</td><td>Contract still needed for a fixed grid</td><td>Do not substitute alpha-based detection for known cell geometry.</td></tr>
</tbody>
</table>

<p>Automatic slicing uses the image's alpha information to find sprites. It does not mean “recover the intended fixed grid.” A character occupying a small part of a large cell and a character reaching near its cell edge can lead to different detected regions. Choose the method that matches the exported asset's organization rather than accepting the first visually tidy result.</p>
<p>After slicing, select several SpriteRects, including the first and last occupied cells and a cell beside a gutter. Compare their positions and sizes with the contract. A correct total rectangle count does not prove each rectangle is correct.</p>
<h2>Verify offset, padding, and empty cells</h2>
<p>Treat offset and padding as separate layout inputs. Offset locates the grid relative to the texture edges; padding accounts for space between cells. Unity's <a href="https://docs.unity3d.com/6000.0/Documentation/Manual/sprite/sprite-editor/sprite-editor-window-reference.html">Slice dialog reference</a> lists these controls along with Pixel Size and Column &amp; Row settings.</p>
<p>Inspect the first row and column after entering the values, then inspect the far edge. A wrong starting offset can shift every slice. A wrong gap can make the error grow across successive cells. Do not repair the last rectangle alone when the error originates in the grid settings.</p>
<p>Decide whether empty cells should remain as rectangles. The Keep Empty Rects option affects this choice. A deliberate blank entry may belong to a particular sequence; unused export padding may not. Record the decision, because an unexpected blank sprite can affect a later animation assembly even when the occupied poses look correct.</p>
<p>Use these branches when the slice does not match:</p>
<ul>
<li><strong>Every rectangle starts too far from its pose:</strong> check the grid offset and original margin.</li>
<li><strong>Early cells align but later cells drift:</strong> check padding, cell dimensions, and column or row count.</li>
<li><strong>Rectangle count differs from the expected capacity:</strong> inspect the chosen grid mode and empty-cell policy.</li>
<li><strong>Rectangles follow opaque shapes rather than cells:</strong> verify that Automatic slicing was not used for a fixed-grid contract.</li>
</ul>
<h2>Inspect pivot positions and units explicitly</h2>
<p>Record the pivot's unit mode before interpreting its values. Unity 6.0 supports normalized and pixel pivot units in the Sprite Editor. Both use the sprite rectangle's bottom-left as their origin; normalized <code>(1, 1)</code> identifies its top-right. The <a href="https://docs.unity3d.com/6000.0/Documentation/Manual/sprite/sprite-editor/sprite-editor-window-reference.html">Sprite Editor window reference</a> documents this coordinate convention.</p>
<p>A pair of numbers without a unit mode is incomplete evidence. For a hypothetical 64 by 64 rectangle, normalized <code>(0.5, 0)</code> and pixel <code>(32, 0)</code> describe the bottom center of that rectangle. This equivalence follows from those dimensions; it should not be copied to rectangles with other widths.</p>
<p>Choose a pivot from the project's placement requirement. A ground-contact reference and a visual-center reference solve different tasks. A consistent pivot setting also cannot prove that the artwork inside every cell shares a consistent reference point. Inspect several poses against the same scene reference when verifying the resulting animation.</p>
<p>If all poses shift together, investigate import scale, pivot, and scene placement. If one pose shifts relative to the others, inspect its rectangle and the source layout before applying a global pivot adjustment. The <a href="https://www.framesprite.com/guides/fix-sprite-animation-jitter">sprite jitter diagnosis guide</a> explains why changing artwork bounds and a changing anchor require different checks; use that distinction when deciding whether the problem belongs to the source or the import.</p>
<h2>Apply, inspect sub-assets, and record the result</h2>
<p>Apply the Sprite Editor changes, then expand the texture in the Project window and inspect its sprite sub-assets. Compare the count and identities with the accepted layout. Check the sprites used by the animation or scene, especially after re-slicing an existing texture; the presence of new rectangles alone does not prove that the intended sprites are in use.</p>
<p>Use this copyable acceptance record:</p>
<pre><code class="language-text">Unity version and project revision:
Source texture and export revision:
Recoverable pre-slice version:
Texture Type / Sprite Mode:
Pixels Per Unit:
Grid mode: Cell Size / Cell Count:
Cell dimensions or columns/rows:
Offset / padding:
Keep Empty Rects decision and reason:
Slice method for existing SpriteRects:
Expected / actual SpriteRect count:
Representative rectangle positions and sizes:
Pivot unit mode / values / placement intent:
Changes applied: yes / no:
Expected / actual sprite sub-assets:
Downstream animation or scene references checked:
Editor settings and scene evidence:
Decision: accept / revise / blocked:
Remaining unverified behavior:
</code></pre>
<p>Acceptance should identify the source revision, slice geometry, and resulting assets clearly enough for another person to reproduce the decision. Scene playback remains a separate observation: record it when performed, and leave it unverified when only the import settings have been reviewed.</p>
<p><em>About the author: The FrameSprite Workflow Team documents sprite production workflows for its own product. This draft used AI writing assistance and source-based editorial review; it reports no new engine test. <a href="https://www.framesprite.com/about#workflow-team">Editorial ownership</a> · <a href="https://www.framesprite.com/contact">Contact and corrections</a>.</em></p>
]]></content:encoded></item><item><title><![CDATA[Godot SpriteFrames Import QA: Verify the Animation You Actually Built]]></title><description><![CDATA[Verify a Godot SpriteFrames import against its grid, selected frames, animation names, timing, loop behavior, and placement before accepting it.]]></description><link>https://framesprite-workflow.hashnode.dev/godot-spriteframes-import-acceptance</link><guid isPermaLink="true">https://framesprite-workflow.hashnode.dev/godot-spriteframes-import-acceptance</guid><category><![CDATA[Game Development]]></category><dc:creator><![CDATA[FrameSprite Workflow Team]]></dc:creator><pubDate>Mon, 26 Oct 2026 03:04:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6add6fc04b032073c0397857/94e4177e-bbce-46b4-bd71-8ce75537b5d3.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Accept a Godot sprite import when the SpriteFrames resource matches the intended frame selection, order, durations, loop behavior, and node placement. A texture appearing in the editor proves only that the editor can display it. It does not establish that the animation you built matches the source contract.</p>
<p>This checklist starts with an already reviewed export. It focuses on AnimatedSprite2D and the SpriteFrames editor, using Godot's current stable documentation. It does not repeat image cleanup, alpha repair, or source-frame alignment work, and it reports no new Godot runtime test.</p>
<blockquote>
<p><strong>Key Takeaways</strong></p>
<ul>
<li>Keep sheet rows and columns separate from animation frame count.</li>
<li>Verify the selected cells and their order inside the actual SpriteFrames resource.</li>
<li>Check animation FPS, relative frame duration, playback speed, and loop mode together.</li>
<li>Record placement settings and runtime evidence before marking an import accepted.</li>
</ul>
</blockquote>
<h2>Bring a grid contract into the import</h2>
<p>Write down the expected sheet layout before opening the cell selector. At minimum, record the source revision, grid columns and rows, occupied cells, intended frame order, and animation names. Add intended FPS, holds, and loop behavior so that acceptance has a reference. Keep the reviewed export evidence alongside this contract; the <a href="https://framesprite-workflow.hashnode.dev/sprite-sheet-export-qa-12-frame-png-audit">12-frame PNG export QA example</a> illustrates the separate file-level checks that should precede an engine import.</p>
<p>A sheet's total cell capacity is not necessarily the length of any one animation. Empty cells, several actions on separate rows, or unused poses can make those numbers different. A selector that divides a texture cleanly can still produce the wrong animation if you select every cell without checking the layout.</p>
<p><img src="https://www.framesprite.com/guides/spritesheet-example.webp" alt="A published sprite sheet illustrates a grid of sprite poses to identify before selecting animation frames." /></p>
<p><em>Public source-material illustration, not a Godot screenshot or proof that this sheet passed an import test. No dimensions are inferred from the image.</em></p>
<p>Use a compact contract like this:</p>
<pre><code class="language-text">Source file and revision:
Grid: ____ columns x ____ rows
Occupied cells and their action labels:
Animation name:
Ordered cells or ordered source filenames:
Expected played frame count:
Base FPS and any longer holds:
Loop intent: one-shot / forward loop / ping-pong:
Expected placement reference:
</code></pre>
<p>If the export contract is missing, establish it first. The <a href="https://www.framesprite.com/guides/how-to-make-a-sprite-sheet">sprite-sheet preparation guide</a> provides background on canvas alignment and spacing choices to document before import. Guessing the grid and then judging the result against that guess creates a circular acceptance test.</p>
<h2>Verify the SpriteFrames resource on AnimatedSprite2D</h2>
<p>Select the intended AnimatedSprite2D node and confirm which SpriteFrames resource it uses. Godot's <a href="https://docs.godotengine.org/en/stable/classes/class_animatedsprite2d.html">AnimatedSprite2D class reference</a> identifies <code>sprite_frames</code> as the resource containing the animation frames. This is the resource whose contents you need to inspect.</p>
<p>Godot's <a href="https://docs.godotengine.org/en/stable/tutorials/2d/2d_sprite_animation.html">2D sprite animation tutorial</a> describes creating a new SpriteFrames resource, adding separate images, and adding frames from a sprite sheet. Choose the path that matches the reviewed input. For separate images, verify the imported sequence rather than relying on filename appearance. For a sheet, open the sheet-frame selection interface and set its horizontal and vertical divisions to the agreed grid before selecting cells.</p>
<p>Do not apply Sprite2D Hframes and Vframes instructions to this task. AnimatedSprite2D uses its SpriteFrames resource for animation frames. Mixing the two workflows can leave an apparently sensible grid setting on the wrong object while the actual animation remains incorrect.</p>
<p>If editing an existing resource, preserve a recoverable project version first. Record whether another node uses that same resource before making changes, so the reviewer knows the scope of the edit.</p>
<h2>Check names, selected cells, and playback order</h2>
<p>Accept frame selection against the ordered contract, not against a moving preview alone. Compare the first, last, and every intervening selected frame with the intended action. Confirm that no cell from a neighboring action or an empty area slipped into the sequence.</p>
<p>Then verify the animation name. A correctly assembled animation under an unexpected name can still fail when the game requests a different animation. Record the exact name and case used by the project, and check the animation list for an accidental default or duplicate entry.</p>
<p>Use these diagnostic branches when the preview is wrong:</p>
<ul>
<li><strong>Every frame shows the wrong region:</strong> revisit the sheet divisions and source selection before changing timing.</li>
<li><strong>One unexpected pose interrupts the action:</strong> compare that entry with the selected cells and order.</li>
<li><strong>The action plays correctly in the editor but the game shows another action:</strong> inspect the resource and animation selected by the actual node at runtime.</li>
<li><strong>An empty entry creates a blink:</strong> confirm whether the cell is part of the intended sequence before removing it.</li>
</ul>
<p>Keep the source-frame count, selected-frame count, and played order explicit. Their agreement provides stronger evidence than a screenshot of a populated editor panel.</p>
<h2>Audit timing as a combination of settings</h2>
<p>Animation FPS alone does not determine every frame's display time. SpriteFrames stores a relative duration for each frame, while AnimatedSprite2D exposes playback speed controls. The stable <a href="https://docs.godotengine.org/en/stable/classes/class_spriteframes.html">SpriteFrames reference</a> gives the actual-duration relationship:</p>
<pre><code class="language-text">actual_duration = relative_duration / (animation_fps * abs(playing_speed))
</code></pre>
<p>For an illustrative calculation, at 10 animation FPS and an absolute playing speed of 1, a relative duration of 2 gives a 0.2-second frame. A relative duration of 1 gives 0.1 seconds under those same conditions. These are arithmetic examples, not measurements from a project.</p>
<p>Check longer holds intentionally. A frame that appears to pause may have a larger relative duration, a repeated entry, or both. Record which representation the resource uses. Use the <a href="https://www.framesprite.com/tools/sprite-animation-timing-calculator">animation timing calculator</a> to review the intended played-entry count and total duration, then compare that intent with the resource and playback settings. If the entire animation is unexpectedly slow, check the effective playback speed as well as the animation FPS. AnimatedSprite2D's <a href="https://docs.godotengine.org/en/stable/classes/class_animatedsprite2d.html">speed and playback properties</a> explain why a correct resource can play differently under a node's runtime controls.</p>
<p>Check loop intent separately from speed. Current stable SpriteFrames documentation describes loop modes, including ping-pong behavior; older project versions can expose different controls and APIs. Use the controls and documentation for the Godot version you are accepting. This checklist intentionally avoids version-independent setter code.</p>
<h2>Verify centered placement and offset in context</h2>
<p>Correct frame selection does not guarantee correct placement in the scene. AnimatedSprite2D's <code>centered</code> and <code>offset</code> properties affect how the sprite sits relative to its node, as documented in the <a href="https://docs.godotengine.org/en/stable/classes/class_animatedsprite2d.html">node reference</a>.</p>
<p>Check the animation against the project's placement reference, such as a ground line or interaction origin. Inspect it in the intended scene context and at the scale used for acceptance. Record centered, offset, and the relevant transform settings so someone else can reproduce the view.</p>
<p>If all frames sit consistently too high, inspect node placement and these settings first. If only one frame shifts relative to the character's own reference point, preserve that distinction and return the source or cell-selection question to the export review. A node-wide offset cannot independently fix one bad pose.</p>
<h2>Keep a reproducible import acceptance record</h2>
<p>Save editor evidence for resource contents and playback evidence for actual behavior. A still image can show selected frames or settings, but it cannot prove timing or loop continuity.</p>
<pre><code class="language-text">Godot version and project revision:
Source asset and reviewed export revision:
AnimatedSprite2D node path:
SpriteFrames resource path:
Animation name and case:
Grid columns/rows or ordered image list:
Expected / actual selected frame count:
Order checked against contract: yes / no:
Animation FPS:
Per-frame relative duration exceptions:
Effective playback speed during test:
Loop intent / observed behavior:
Centered / offset / relevant transform:
Placement reference:
Editor settings evidence:
Runtime playback evidence:
Decision: accept / revise / blocked:
Unverified conditions and owner:
</code></pre>
<p>Mark the import accepted only for the conditions actually checked. A resource review can establish what was assembled; runtime observation establishes how the application played it under the recorded settings. Keep those two conclusions separate in the handoff.</p>
<p><em>About the author: The FrameSprite Workflow Team documents sprite production workflows for its own product. This draft used AI writing assistance and source-based editorial review; it reports no new engine test. <a href="https://www.framesprite.com/about#workflow-team">Editorial ownership</a> · <a href="https://www.framesprite.com/contact">Contact and corrections</a>.</em></p>
]]></content:encoded></item><item><title><![CDATA[Duplicate Endpoint or Intentional Hold? Audit a Sprite Loop Seam]]></title><description><![CDATA[Inspect a sprite loop's last-to-first transition, distinguish repeated pixels from intentional holds, and record a defensible keep-or-remove decision.]]></description><link>https://framesprite-workflow.hashnode.dev/sprite-loop-duplicate-endpoint-decision</link><guid isPermaLink="true">https://framesprite-workflow.hashnode.dev/sprite-loop-duplicate-endpoint-decision</guid><category><![CDATA[Game Development]]></category><dc:creator><![CDATA[FrameSprite Workflow Team]]></dc:creator><pubDate>Mon, 26 Oct 2026 02:48:10 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6add6fc04b032073c0397857/8b6ed45c-80f4-472d-bd93-80e36d2be266.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Remove a repeated endpoint only when the playback sequence gives the boundary pose an unintended extra hold. Identical first and last images are a reason to inspect the seam, not proof that a frame is wrong. A deliberate pause may need both entries; a sudden change in motion may survive after you delete one.</p>
<p>The useful inspection unit is the transition from the last played entry to the first, together with a few neighboring entries. This workflow addresses that boundary. It does not certify every frame, repair an entire asset pack, or diagnose irregular stalls throughout a game.</p>
<blockquote>
<p><strong>Key Takeaways</strong></p>
<ul>
<li>Write the actual played sequence before deciding which endpoint to keep.</li>
<li>Compare boundary motion as well as the first and last poses.</li>
<li>Separate equal decoded pixels, intentional hold time, and an unintended duplicate.</li>
<li>Keep the original sequence and record why the proposed change passes or fails.</li>
</ul>
</blockquote>
<h2>Write the loop as a circular sequence</h2>
<p>Start with the entries the player actually displays, including repeated entries and their durations. A file list alone cannot tell you the order or timing of an animation.</p>
<p>Write the sequence on one line, then append its beginning: <code>A → B → C → A | A → B → C → A</code>. The vertical bar marks the wrap. This is an illustrative sequence, not a report of a tested failure. If both A entries have the same pixels, the viewer sees the final A followed by the initial A. Their display times form one continuous stay on that pose.</p>
<p>For an equal-duration illustration at 8 FPS, four entries occupy <code>4 / 8 = 0.5</code> seconds. Removing the final A changes the cycle to three entries and <code>3 / 8 = 0.375</code> seconds. That changes the rhythm of the entire cycle, even if it removes the extra boundary hold. Do not treat a shorter loop as an automatic improvement.</p>
<p>Count played entries, not unique drawings. <code>A → B → C → B</code> contains four entries even though it uses three distinct poses. The <a href="https://www.framesprite.com/tools/sprite-animation-timing-calculator">animation timing calculator's explanation of played entries and holds</a> is useful for checking this distinction.</p>
<p>Record the player's loop mode too. The current stable <a href="https://docs.godotengine.org/en/stable/classes/class_spriteframes.html">Godot SpriteFrames reference</a> distinguishes linear and ping-pong looping and documents endpoint duration behavior for that implementation. Do not assume that every player repeats or holds ping-pong endpoints in the same way. Expand the actual traversal used by your target player before counting it.</p>
<h2>Compare the last three and first three entries</h2>
<p>Inspect a short window around the wrap before changing the sequence. Use the last three and first three played entries where the animation is long enough; for a shorter loop, repeat the circular sequence to expose the same neighborhood.</p>
<p><img src="https://www.framesprite.com/guides/frames-strip.webp" alt="A published sprite frame strip shows successive poses that can be compared in playback order." /></p>
<p><em>Existing public frame-strip illustration. It shows the kind of material to inspect and is not evidence of a new engine test or a diagnosed faulty loop.</em></p>
<p>Choose visible landmarks suited to the motion: a planted foot, the head, a weapon tip, or the center of a body mass. Compare each landmark's movement through the boundary. Keep the canvas, zoom, and placement fixed so that your inspection does not introduce a new apparent jump.</p>
<p>Ask what each neighboring transition does. Does a foot travel steadily and then stop for two entries? Does the body shift in one direction before the wrap and abruptly reverse afterward? Does a weapon tip cross a large distance at the boundary while the rest of the cycle moves in smaller steps?</p>
<p>Matching endpoint poses answer only a position question. They do not establish that movement into and out of those poses is continuous. The <a href="https://www.framesprite.com/guides/fix-sprite-animation-loop-stutter">loop-stutter diagnosis guide</a> makes this distinction explicit. For example, a matching head position can still sit between a fast incoming movement and a differently directed outgoing movement.</p>
<h2>Separate pixel equality from hold intent</h2>
<p>Use decoded image content to investigate equality, then use the animation brief to decide whether the hold belongs. A file hash compares file bytes. Different hashes do not rule out equal decoded pixels, because two image files can encode the same visible content differently. A matching pose by eye is also weaker evidence than a pixel comparison.</p>
<p>For a strict comparison, decode both images using the same process and compare dimensions and corresponding RGBA values. Record what you compared. If fully transparent pixels contain different hidden RGB values, report that distinction rather than calling the entire decoded array identical. For a seam decision, also inspect the rendered appearance under consistent conditions.</p>
<p>Neither form of equality establishes intent. A breathing loop may deliberately settle at rest. A wind-up may need a readable pause. A duplicate introduced by export is a different case, even when the files look the same.</p>
<table>
<thead><tr><th>Boundary finding</th><th>Next decision</th></tr></thead>
<tbody>
<tr><td>Equal rendered pose, extra stay, no intended pause</td><td>Try removing one boundary entry in a copy and compare the new rhythm.</td></tr>
<tr><td>Equal rendered pose, intentional pause</td><td>Keep the required hold time and document how it is represented.</td></tr>
<tr><td>Different poses, visible jump at wrap</td><td>Inspect the intervening motion; deletion alone is not a demonstrated repair.</td></tr>
<tr><td>Matching poses, abrupt movement before or after wrap</td><td>Review the neighboring transitions and timing, not just endpoint equality.</td></tr>
</tbody>
</table>

<h2>Distinguish a pause from a jump</h2>
<p>A pause is time spent without the expected movement. A jump is a discontinuity in position or appearance. A loop can have either, both, or neither.</p>
<p>Replay several cycles at the intended speed and note whether the symptom occurs at the same boundary every time. Then step through the boundary window. A fixed pause with equal boundary poses supports an inspection of accumulated hold time. A fixed jump supports an inspection of pose spacing and placement. A stall that moves around the cycle needs a separate runtime investigation; this endpoint workflow cannot attribute it to the seam.</p>
<p>Compare the original and proposed sequences under the same playback settings. If you remove an entry, recalculate the cycle duration and check whether the new tempo still serves the action. If the original pause was intentional but too long, adjust its duration according to the target player's controls rather than deleting the pose without considering timing.</p>
<p>A one-shot animation has no required last-to-first transition unless the application actually loops it. Do not repair a death, impact, or interaction clip against a seam that users never see.</p>
<h2>Save a keep-or-remove decision with evidence</h2>
<p>Accept the change only when you can explain the boundary behavior and the resulting timing. Use this copyable record for each loop:</p>
<pre><code class="language-text">Animation and source revision:
Target player/version and loop mode:
Actual played entries and per-entry durations:
Original cycle duration:
Boundary window inspected:
Landmarks inspected:
Equality check: file bytes / decoded RGBA / rendered appearance:
Observed symptom: pause / jump / both / neither:
Does it recur at the same wrap? yes / no:
Intended boundary hold and its reason:
Decision: keep / remove / adjust duration / investigate motion:
Revised sequence and cycle duration:
Original-versus-revised playback evidence:
Acceptance reason and unresolved limits:
</code></pre>
<p>“Removed because the endpoints match” is incomplete. A useful decision says which hold was unintended, what changed in the cycle duration, and how the boundary behaved afterward. If no candidate produces the intended motion, retain the source and escalate the motion or timing question with that evidence.</p>
<p><em>About the author: The FrameSprite Workflow Team documents sprite production workflows for its own product. This draft used AI writing assistance and source-based editorial review; it reports no new engine test. <a href="https://www.framesprite.com/about#workflow-team">Editorial ownership</a> · <a href="https://www.framesprite.com/contact">Contact and corrections</a>.</em></p>
]]></content:encoded></item><item><title><![CDATA[GIF Frame Delays vs Game FPS: Preserve the Holds]]></title><description><![CDATA[Convert GIF frame delays into a game timing plan without losing uneven holds. Compare total duration, per-frame timing, and relative durations.]]></description><link>https://framesprite-workflow.hashnode.dev/gif-frame-delays-versus-game-fps</link><guid isPermaLink="true">https://framesprite-workflow.hashnode.dev/gif-frame-delays-versus-game-fps</guid><category><![CDATA[Game Development]]></category><dc:creator><![CDATA[FrameSprite Workflow Team]]></dc:creator><pubDate>Mon, 26 Oct 2026 02:32:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6add6fc04b032073c0397857/f7de487c-ffe5-4a7f-aa98-77d1653bb7c5.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A GIF's timing is a list of frame delays, not necessarily one frames-per-second value. If some drawings hold longer than others, matching the total duration with a single game FPS value changes the rhythm. Preserve the individual holds when that rhythm is part of the action.</p>
<p>This workflow turns a known delay list into a timing plan and an acceptance record. It does not decode a new GIF, alter an image, or claim that a browser will play every delay exactly as stored.</p>
<blockquote>
<p><strong>Key Takeaways</strong></p>
<ul>
<li>GIF delay values use units of one hundredth of a second, or 10 milliseconds.</li>
<li>Equal holds can use one frame rate; uneven holds require per-frame timing or an equivalent representation.</li>
<li>Average FPS can match total duration while losing the action's pauses and accents.</li>
<li>Compare the ordered duration list and the destination playback, not just the displayed FPS number.</li>
</ul>
</blockquote>
<h2>Read each delay before choosing FPS</h2>
<p>Start with the ordered frame-delay list. Record whether each value came from the file's timing data, a decoder report, or an intended timing specification. A player preview alone does not provide that provenance.</p>
<p>The <a href="https://www.w3.org/Graphics/GIF/spec-gif89a.txt">GIF89a specification</a> defines the Graphic Control Extension's delay field in hundredths of a second. For a stored delay value <code>d</code>, the corresponding duration is <code>d × 10 ms</code>. A reported value of 12 therefore represents 120 ms.</p>
<p>Keep the units explicit. A table headed “delay: 12” is ambiguous once copied into an engine where a duration may mean seconds or a multiplier. Prefer columns such as <code>GIF delay units</code>, <code>milliseconds</code>, and <code>engine relative duration</code>.</p>
<p>If a decoder reports a missing or zero delay, do not silently turn it into your preferred FPS. Record the condition and determine the intended hold separately. Stored timing, a player's fallback behavior, and the desired game timing are different evidence.</p>
<h2>Use total duration as a check, not a complete specification</h2>
<p>Total duration is the sum of all frame durations. For <code>N</code> equal holds at <code>F</code> frames per second, that becomes <code>N / F</code> seconds. For unequal holds, add each duration instead. The <a href="https://www.framesprite.com/tools/sprite-animation-timing-calculator">sprite animation timing calculator</a> makes this distinction; it calculates timing rather than changing an asset.</p>
<p>Consider these illustrative lists. They are arithmetic examples, not observations from a decoded file.</p>
<table>
<thead>
<tr>
<th>Example</th>
<th>Ordered holds</th>
<th>Total duration</th>
<th>Average frame rate</th>
</tr>
</thead>
<tbody><tr>
<td>Equal holds</td>
<td>100, 100, 100, 100 ms</td>
<td>400 ms</td>
<td>10 FPS</td>
</tr>
<tr>
<td>Uneven holds</td>
<td>50, 50, 100, 200 ms</td>
<td>400 ms</td>
<td>10 FPS</td>
</tr>
</tbody></table>
<p>Both lists show four frames over 400 ms. The second spends half its duration on the final drawing. Replacing it with four 100 ms holds preserves the total but removes the short opening steps and long final pose.</p>
<p>Average FPS is <code>N / total seconds</code>. It is useful for a duration comparison, but the number alone cannot reconstruct the delay list. Keep both the total and the ordered holds in the handoff.</p>
<h2>Map holds to the destination's timing model</h2>
<p>Choose the representation supported by the game engine before entering values. An engine may accept durations directly, use relative duration multipliers, or expose only equal-duration entries.</p>
<p>Godot's <code>SpriteFrames</code> resource uses an animation FPS together with a relative duration for each frame. The absolute frame duration is its relative duration divided by the animation FPS. (<a href="https://docs.godotengine.org/en/stable/classes/class_spriteframes.html">Godot SpriteFrames documentation</a>)</p>
<p>At a base rate of 10 FPS, the illustrative uneven list maps as follows:</p>
<table>
<thead>
<tr>
<th>Frame</th>
<th>Desired hold</th>
<th>Relative duration at 10 FPS</th>
<th>Check</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>50 ms</td>
<td>0.5</td>
<td>0.5 / 10 = 0.05 s</td>
</tr>
<tr>
<td>2</td>
<td>50 ms</td>
<td>0.5</td>
<td>0.5 / 10 = 0.05 s</td>
</tr>
<tr>
<td>3</td>
<td>100 ms</td>
<td>1</td>
<td>1 / 10 = 0.1 s</td>
</tr>
<tr>
<td>4</td>
<td>200 ms</td>
<td>2</td>
<td>2 / 10 = 0.2 s</td>
</tr>
</tbody></table>
<p>The base FPS is part of the calculation. Changing it without recalculating the multipliers changes every hold. Record both values together.</p>
<p>If the destination only accepts equal-duration entries, repeating a drawing can represent longer holds when all desired durations are exact multiples of a common step. For the illustrative list, a 50 ms step uses one, one, two, and four entries respectively. That is a proposed conversion strategy, not a guarantee about any importer. Confirm that repeated entries preserve the intended event behavior in the destination.</p>
<p>If no suitable exact step exists, decide whether the engine can use a different timing path or whether an approximation is acceptable. Record any rounding per frame and the resulting total. Do not call a rounded conversion timing-equivalent without stating the allowed error.</p>
<h2>An archived example shows why the last hold matters</h2>
<p>Archived 2026-10-20 demo metadata records this duration list in milliseconds:</p>
<pre><code class="language-text">270, 280, 270, 270, 270, 280, 270, 270, 280, 270, 270, 40
</code></pre>
<p>The listed holds total 3,040 ms. Twelve equal holds over that duration would average about 253.33 ms each, or about 3.95 FPS. That would lengthen the final 40 ms hold substantially while shortening the longer holds.</p>
<p>This calculation uses archived metadata, not a new GIF decode or a test of the current public player. The list illustrates a conversion risk: a very short final hold can vanish into an apparently reasonable average. Confirm the source file and timing provenance before using any archived numbers for production.</p>
<h2>Diagnose timing differences by where they appear</h2>
<p>Compare data first, then observe destination playback. An identical list does not guarantee identical presentation across all players and displays.</p>
<table>
<thead>
<tr>
<th>Symptom</th>
<th>Check first</th>
<th>Decision branch</th>
</tr>
</thead>
<tbody><tr>
<td>Total is wrong and every hold differs by the same ratio</td>
<td>Units, base FPS, and relative durations</td>
<td>Correct the scale, then recalculate the total</td>
</tr>
<tr>
<td>Total matches but accents feel different</td>
<td>Ordered per-frame holds</td>
<td>Restore unequal holds rather than adjusting average FPS</td>
</tr>
<tr>
<td>Only the final pose feels too long</td>
<td>Final frame duration and frame order</td>
<td>Check for a flattened or duplicated hold</td>
</tr>
<tr>
<td>Timing data matches but a browser preview differs</td>
<td>Actual observed playback and player behavior</td>
<td>Keep stored timing separate from playback observations</td>
</tr>
<tr>
<td>Game timing changes under load</td>
<td>Destination playback conditions</td>
<td>Investigate runtime scheduling before rewriting source timing</td>
</tr>
</tbody></table>
<p>Game animation FPS, source video FPS, and display refresh rate describe different things. A 60 Hz display does not mean that a twelve-drawing action should have twelve equal 1/60-second holds. Browser playback is also not a precision guarantee for every stored GIF delay.</p>
<h2>Keep a timing acceptance record</h2>
<p>Copy this table into the asset handoff. It allows another developer to reproduce the conversion and identify the scope of the verdict.</p>
<table>
<thead>
<tr>
<th>Record</th>
<th>Value to enter</th>
</tr>
</thead>
<tbody><tr>
<td>Asset and revision</td>
<td>Source file; target resource; revision</td>
</tr>
<tr>
<td>Timing provenance</td>
<td>Decoder or specification; date; archived if applicable</td>
</tr>
<tr>
<td>Ordered holds</td>
<td>Frame identifiers with explicit duration units</td>
</tr>
<tr>
<td>Source total</td>
<td>Sum of the ordered holds</td>
</tr>
<tr>
<td>Destination model</td>
<td>Direct durations, relative duration/FPS, or repeated entries</td>
</tr>
<tr>
<td>Conversion values</td>
<td>Base rate and every multiplier or repeated-entry count</td>
</tr>
<tr>
<td>Rounding</td>
<td>Per-frame differences; total difference; allowed tolerance</td>
</tr>
<tr>
<td>Playback review</td>
<td>Player or engine; conditions; observed discrepancy</td>
</tr>
<tr>
<td>Decision</td>
<td>Accept / revise / needs runtime investigation</td>
</tr>
<tr>
<td>Reviewer and date</td>
<td>Responsible reviewer; review date</td>
</tr>
</tbody></table>
<p>Accept the timing conversion when its ordered holds meet the agreed tolerance and the destination preserves the intended pauses under the reviewed conditions. A matching total is one check. Preserved holds are the evidence that the rhythm survived.</p>
<hr />
<p>Prepared by the <a href="https://www.framesprite.com/about#workflow-team">FrameSprite Workflow Team</a>, the operator of the linked timing calculator. This ownership is a commercial interest. AI assisted drafting; the archived timing example is identified separately from current testing. Send factual corrections through the <a href="https://www.framesprite.com/contact">contact page</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Transparent PNG Edge QA on Light and Dark Backgrounds]]></title><description><![CDATA[Review the same transparent PNG on light and dark backgrounds, distinguish alpha damage from color spill, and record a repeatable edge QA decision.]]></description><link>https://framesprite-workflow.hashnode.dev/transparent-png-light-dark-edge-check</link><guid isPermaLink="true">https://framesprite-workflow.hashnode.dev/transparent-png-light-dark-edge-check</guid><category><![CDATA[Game Development]]></category><dc:creator><![CDATA[FrameSprite Workflow Team]]></dc:creator><pubDate>Mon, 26 Oct 2026 02:28:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6add6fc04b032073c0397857/9f2e2c24-4764-4f75-a917-ca455dc1a740.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Review the same PNG over both a light background and a dark background before accepting its edges. A pale fringe can disappear on white, while a dark outline can hide on black. Passing one background is evidence about that background, not the whole cutout.</p>
<p>This procedure checks transparency and edge color in an existing image. It is for deciding whether a cutout is ready for a particular use, not for proving that a file extension guarantees transparency. It describes a review method; no character image or removal tool was tested for this article.</p>
<blockquote>
<p><strong>Key Takeaways</strong></p>
<ul>
<li>A PNG can contain opaque artwork and background; its extension does not prove a useful cutout.</li>
<li>Partial alpha is valid for antialiased edges and soft effects.</li>
<li>Missing subject pixels and contaminated edge colors need different corrections.</li>
<li>Preserve the original and compare sensitive details on both backgrounds after each change.</li>
</ul>
</blockquote>
<h2>Separate opacity from edge color</h2>
<p>Alpha controls how much of a pixel contributes to the displayed result. Color channels describe that pixel's color. Those are separate properties, which is why removing a background can leave colored fringes.</p>
<p>The <a href="https://www.w3.org/TR/png-3/#6AlphaRepresentation">PNG specification</a> defines intermediate alpha values as partial transparency and describes PNG color values as non-premultiplied. In practical terms, a partly transparent edge can still carry an unwanted color that becomes visible when composited over a new background.</p>
<p>Do not require every edge pixel to be fully opaque or fully transparent. Hair, a curved contour, motion blur, and a glow can need intermediate opacity. A harsh threshold may remove the fringe while damaging the intended edge.</p>
<p>The <a href="https://www.framesprite.com/guides/transparent-sprite-sheet-maker">transparent sprite sheet guide</a> distinguishes background removal from despill. Removal changes what remains visible; despill addresses unwanted background color on retained details. That distinction should guide the diagnosis before any edit.</p>
<h2>Set up a comparison that preserves the evidence</h2>
<p>Use one source file, one display scale, and two solid backgrounds. Keep the image unchanged between views so the backgrounds are the only variable.</p>
<ol>
<li>Preserve the original PNG and identify the revision being reviewed.</li>
<li>Place it over a solid light background.</li>
<li>Duplicate the view with a solid dark background, using the same file and scale.</li>
<li>Inspect the complete silhouette at its intended display size.</li>
<li>Inspect suspect regions more closely, then return to the intended display size for the verdict.</li>
<li>If the destination has a known background, inspect that as an additional view.</li>
</ol>
<p>Write down the actual background colors rather than “light” and “dark” alone. A repeat reviewer should be able to reproduce the comparison. Also record the viewing application, since an editor view and a destination renderer may not show the same result.</p>
<p>A transparency checkerboard is useful for orientation, but it is a poor sole acceptance surface. Its alternating colors can conceal edge patterns. Two plain backgrounds make the relationship between the subject and its surroundings easier to inspect.</p>
<h2>Inspect vulnerable details in a fixed order</h2>
<p>Start with details that background removal can erase or discolor. Follow the same route in both views so a change in inspection order does not become a change in coverage.</p>
<table>
<thead>
<tr>
<th>Region</th>
<th>Inspect on light and dark backgrounds</th>
<th>Revision risk</th>
</tr>
</thead>
<tbody><tr>
<td>Hair and fur</td>
<td>Fine strands, gaps, color fringe</td>
<td>Thin details disappear</td>
</tr>
<tr>
<td>Fingers and toes</td>
<td>Tips, spaces between digits</td>
<td>Small anatomy merges or is cut away</td>
</tr>
<tr>
<td>Weapon and accessories</td>
<td>Narrow edges, holes, pointed ends</td>
<td>Metal edges pick up background color</td>
</tr>
<tr>
<td>Clothing</td>
<td>Color continuity and small folds</td>
<td>Subject colors match the removal color</td>
</tr>
<tr>
<td>Soft effects</td>
<td>Gradual opacity and intended glow color</td>
<td>A soft effect becomes a hard outline</td>
</tr>
</tbody></table>
<p>Compare a suspect area with the preserved original. A transparent gap between fingers may be correct; a transparent hole inside an intact glove may be damage. The intended subject boundary supplies the difference.</p>
<p>For effects, write down the intended appearance before editing. A pale aura around a character is not automatically a white halo defect. It becomes a defect when the retained color or opacity conflicts with the intended effect and destination.</p>
<h2>Diagnose the failure before adjusting it</h2>
<p>Classify the observed problem as retained background, missing subject, or unwanted color. More than one can occur in the same region.</p>
<table>
<thead>
<tr>
<th>Observation</th>
<th>Diagnostic branch</th>
<th>Next step</th>
</tr>
</thead>
<tbody><tr>
<td>A solid rectangle surrounds the subject</td>
<td>Background may still be opaque</td>
<td>Inspect alpha or transparency in an editor; confirm the actual file</td>
</tr>
<tr>
<td>A thin colored fringe changes visibility between the two backgrounds</td>
<td>Retained edge color may be contaminated</td>
<td>Compare edge RGB with the source background and intended subject color</td>
</tr>
<tr>
<td>A fingertip or strand is absent in both views</td>
<td>Subject alpha may have been removed</td>
<td>Compare against the original before further removal</td>
</tr>
<tr>
<td>A glow ends in a sharp contour</td>
<td>Partial alpha may have been clipped</td>
<td>Restore or revise the intended opacity transition</td>
</tr>
<tr>
<td>Source looks correct but the destination looks fringed</td>
<td>Destination handling may differ</td>
<td>Compare the same file and record the renderer settings before editing pixels</td>
</tr>
</tbody></table>
<p>These branches narrow the investigation; they do not prove the cause from a screenshot alone. Inspecting alpha separately helps distinguish an opaque background from a pale subject. Inspecting retained color helps distinguish a color fringe from an intended translucent effect.</p>
<p>Avoid repeatedly increasing removal strength until the edge looks clean on one background. That can trade a visible fringe for lost anatomy.</p>
<h2>Protect subject colors that resemble the background</h2>
<p>Green clothing, blue hair, or a bright weapon can overlap the background's color range. A color-based removal operation has no inherent understanding that one green pixel is a shirt and another is scenery. The removal guide specifically warns about subject colors that match the background. (<a href="https://www.framesprite.com/guides/transparent-sprite-sheet-maker">Source</a>)</p>
<p>When that overlap exists, mark the protected regions before revising the cutout. Compare those regions with the original after each change. A useful correction should address the unwanted background without silently deleting the matching subject area.</p>
<p>Save each accepted revision separately. If a correction damages a protected region, return to the preserved source or prior revision and change the approach. Do not depend on memory to reconstruct lost detail.</p>
<h2>Keep the verdict tied to the file and destination</h2>
<p>Record the actual format being reviewed. GIF transparency uses a transparent color index, which differs from PNG's alpha representation. A GIF preview cannot establish that a PNG's partial-alpha edges are correct. (<a href="https://www.w3.org/Graphics/GIF/spec-gif89a.txt">GIF89a specification</a>)</p>
<p>Copy this acceptance record for each candidate:</p>
<table>
<thead>
<tr>
<th>Record</th>
<th>Value to enter</th>
</tr>
</thead>
<tbody><tr>
<td>File and revision</td>
<td>Exact PNG name; revision identifier</td>
</tr>
<tr>
<td>Preserved original</td>
<td>Source reference and location</td>
</tr>
<tr>
<td>Review setup</td>
<td>Application; display scale; light and dark background colors</td>
</tr>
<tr>
<td>Destination</td>
<td>Intended display size and background</td>
</tr>
<tr>
<td>Fine-detail coverage</td>
<td>Hair, digits, accessories, clothing, effects reviewed</td>
</tr>
<tr>
<td>Alpha findings</td>
<td>Retained background, missing details, or opacity transitions</td>
</tr>
<tr>
<td>Color findings</td>
<td>Fringe location and expected edge color</td>
</tr>
<tr>
<td>Protected colors</td>
<td>Subject regions at risk from removal</td>
</tr>
<tr>
<td>Decision</td>
<td>Accept / revise / needs destination review</td>
</tr>
<tr>
<td>Follow-up</td>
<td>Specific regions; correction; reviewer; date</td>
</tr>
</tbody></table>
<p>Accept when the intended silhouette and effects remain intact and the edge behavior is suitable on both review backgrounds and the intended destination. If a defect remains, name the region and whether it concerns opacity or color. That gives the next editor a correction task that can be verified without repeating the whole diagnosis.</p>
<hr />
<p>The <a href="https://www.framesprite.com/about#workflow-team">FrameSprite Workflow Team</a> prepared this source-based checklist with AI drafting assistance. The team operates the linked sprite product and benefits commercially from it. The checklist is not a claim of measured character-cutout quality. Report errors or missing context through the <a href="https://www.framesprite.com/contact">contact page</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Sprite Alignment QA: A Fixed-Canvas Acceptance Test]]></title><description><![CDATA[Check sprite alignment on a fixed canvas using semantic anchors, separate intended motion from drift, and record an accept-or-revise decision.]]></description><link>https://framesprite-workflow.hashnode.dev/sprite-alignment-fixed-canvas-acceptance-test</link><guid isPermaLink="true">https://framesprite-workflow.hashnode.dev/sprite-alignment-fixed-canvas-acceptance-test</guid><category><![CDATA[Game Development]]></category><dc:creator><![CDATA[FrameSprite Workflow Team]]></dc:creator><pubDate>Mon, 26 Oct 2026 02:23:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6add6fc04b032073c0397857/fb30fc2a-9cbf-44f7-ba8f-ebd6c88ce02c.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A sprite alignment test needs a fixed outer canvas and a named point on the character. Keep every frame at the same top-left position, then compare that point through the action. A moving silhouette alone cannot tell you whether the character has drifted.</p>
<p>This workflow is for an artist or developer deciding whether a frame sequence is ready for integration. It separates source-frame alignment from drawing settings in the game. It is a proposed acceptance procedure, not a report that any particular asset has passed.</p>
<blockquote>
<p><strong>Key Takeaways</strong></p>
<ul>
<li>Choose an anchor that describes the character's role in the action, such as a planted foot or body root.</li>
<li>Compare frames on the same canvas without repositioning them to match their visible edges.</li>
<li>Judge feet, torso, and outer silhouette separately so intended movement does not become a false failure.</li>
<li>Record the affected frames and coordinates before deciding to accept or revise.</li>
</ul>
</blockquote>
<h2>Define the anchor before comparing frames</h2>
<p>The anchor is the point whose movement you intend to evaluate. It is a semantic choice: a planted foot, hip center, or another point that makes sense for the action. Write down both the landmark and the phase in which it should remain stable.</p>
<p>For a standing idle, a ground contact point can be useful. For an airborne pose, the same foot is no longer a stationary reference. A recoil action may deliberately shift the torso while the feet stay planted. A single rule such as “all visible pixels must stay centered” would reject these valid changes.</p>
<p>Use a short contract before opening the comparison:</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>Example acceptance contract</th>
</tr>
</thead>
<tbody><tr>
<td>Action</td>
<td>Standing idle</td>
</tr>
<tr>
<td>Primary anchor</td>
<td>Center of the planted left foot at ground contact</td>
</tr>
<tr>
<td>Coordinate system</td>
<td>Source canvas, origin at top-left; x increases right, y increases down</td>
</tr>
<tr>
<td>Stable interval</td>
<td>Frames where the left foot is planted</td>
</tr>
<tr>
<td>Allowed change</td>
<td>Breathing motion in shoulders and torso</td>
</tr>
<tr>
<td>Review tolerance</td>
<td>Set by this asset's intended display scale and art direction</td>
</tr>
</tbody></table>
<p>The tolerance belongs to the project. Do not invent a universal pixel limit after seeing the result. If the action has no stable contact point, define an expected path instead, then evaluate whether the path is intentional and continuous.</p>
<h2>Keep the outer canvas fixed</h2>
<p>Place every frame at the same top-left coordinate in a comparison document. Preserve the full frame rectangle, including empty space. Switch between frames or overlay them with reduced opacity; do not drag each pose into visual alignment.</p>
<p>The <a href="https://www.framesprite.com/guides/fix-sprite-animation-jitter">sprite jitter diagnosis guide</a> recommends comparing poses on the same canvas and distinguishing the drawing's bounds from its anchor. That distinction prevents the comparison process from hiding the error it is meant to find.</p>
<p>Use this sequence:</p>
<ol>
<li>Keep an untouched copy of the source sequence.</li>
<li>Confirm that every comparison layer uses the intended outer canvas.</li>
<li>Place all layers at the same top-left coordinate.</li>
<li>Mark the agreed anchor on a separate reference layer.</li>
<li>Alternate a reference frame with each candidate frame.</li>
<li>Record anchor coordinates before making a correction.</li>
</ol>
<p>If frames arrive with different outer dimensions, stop this test and resolve their intended placement first. Centering each frame by its visible shape creates a new placement rule. It does not recover the original one.</p>
<h2>Compare feet, torso, and silhouette separately</h2>
<p>A stable foot with moving shoulders tells a different story from a whole character shifting sideways. Inspect those regions independently before moving any pixels.</p>
<p>Start with the primary anchor during its stable interval. Then inspect a secondary landmark, such as the hip or chest center. Finally, examine the outer silhouette for changes caused by clothing, hair, a weapon, or the action itself.</p>
<table>
<thead>
<tr>
<th>Observation on the fixed canvas</th>
<th>Likely interpretation to investigate</th>
<th>Next comparison</th>
</tr>
</thead>
<tbody><tr>
<td>Planted foot and torso both shift together</td>
<td>Whole-pose placement drift may be present</td>
<td>Compare their coordinate changes across adjacent frames</td>
</tr>
<tr>
<td>Foot stays put while torso bends</td>
<td>Pose motion may be intentional</td>
<td>Compare with the action's planned body movement</td>
</tr>
<tr>
<td>Torso stays put while a weapon extends</td>
<td>Silhouette expansion may explain the visible change</td>
<td>Keep the body reference fixed and inspect the weapon path</td>
</tr>
<tr>
<td>Only one frame changes position abruptly</td>
<td>A local placement error is possible</td>
<td>Compare that frame with both neighbors</td>
</tr>
</tbody></table>
<p>These are diagnostic branches, not automatic verdicts. A deliberate hop can move feet and torso together. The action contract supplies the missing meaning.</p>
<p>Do not use the alpha bounding box as a substitute for a body root. The <a href="https://www.w3.org/TR/png-3/#6AlphaRepresentation">PNG specification's alpha representation</a> describes pixel opacity, including partial transparency. It does not define a character landmark. The resulting practical inference is that visible-pixel bounds can describe where artwork exists, but cannot establish where a character's root belongs.</p>
<h2>Allow the displacement the action requires</h2>
<p>Judge a jump against its intended trajectory, not against a planted-foot rule. The same applies to a sit-down, knockback, lean, or recoil. The alignment guide explicitly distinguishes intended action displacement from unwanted jitter. (<a href="https://www.framesprite.com/guides/fix-sprite-animation-jitter">Source</a>)</p>
<p>Divide the action into phases if needed: contact, departure, travel, and return. Mark which anchor is meaningful in each phase. A foot may be the useful reference during contact; the hips may be a better reference during travel.</p>
<p>If the intended trajectory is unknown, record “requires art-direction review.” Moving every pose toward a common center would make the sequence look stable while changing the action. An acceptance test should expose that uncertainty rather than silently resolve it through edits.</p>
<h2>Check drawing settings when the source passes</h2>
<p>A sequence that stays aligned in the fixed-canvas comparison can still look displaced after import. Reproduce the comparison with the game's actual drawing settings before changing the source.</p>
<p>In Godot, <code>AnimatedSprite2D</code> exposes <code>centered</code> and <code>offset</code> drawing properties. Its documentation also notes that centering a texture can place it between pixels in relevant cases. Record those settings when comparing an engine preview with the source. (<a href="https://docs.godotengine.org/en/stable/classes/class_animatedsprite2d.html">Godot AnimatedSprite2D documentation</a>)</p>
<p>If the source comparison passes and the game view fails, investigate the drawing origin and offsets. If both fail at the same frames, return to source placement. If the views disagree only during intentional travel, check whether both use the same action and anchor contract.</p>
<h2>Record an accept-or-revise decision</h2>
<p>Keep coordinates and observations beside the decision so another reviewer can repeat it. Copy this table for each sequence:</p>
<table>
<thead>
<tr>
<th>Record</th>
<th>Value to enter</th>
</tr>
</thead>
<tbody><tr>
<td>Asset and revision</td>
<td>File name or asset identifier; revision</td>
</tr>
<tr>
<td>Action and frame order</td>
<td>Action name; ordered frame list</td>
</tr>
<tr>
<td>Canvas and placement</td>
<td>Width × height; shared top-left coordinate</td>
</tr>
<tr>
<td>Anchor contract</td>
<td>Landmark; stable phases or expected path; tolerance</td>
</tr>
<tr>
<td>Affected frames</td>
<td>Frame identifiers and measured anchor coordinates</td>
</tr>
<tr>
<td>Secondary check</td>
<td>Torso and silhouette observations</td>
</tr>
<tr>
<td>Intended displacement</td>
<td>Accepted movement and its reason</td>
</tr>
<tr>
<td>Game comparison</td>
<td>Drawing settings; whether source and game agree</td>
</tr>
<tr>
<td>Decision</td>
<td>Accept / revise / requires art-direction review</td>
</tr>
<tr>
<td>Follow-up</td>
<td>Specific frames to change; reviewer; date</td>
</tr>
</tbody></table>
<p>Accept when the observed movement matches the agreed anchor contract and the destination draws the sequence as intended. Revise when unexplained movement remains. After a correction, repeat the same comparison with the same coordinate system; changing the test at the same time makes the result hard to interpret.</p>
<hr />
<p>Written by the <a href="https://www.framesprite.com/about#workflow-team">FrameSprite Workflow Team</a>, which maintains the linked sprite tool and guides and has a commercial interest in them. AI assisted the drafting of this source-based workflow. No asset acceptance test is claimed here. Send corrections through the <a href="https://www.framesprite.com/contact">contact page</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Sprite Sheet Export QA: A 12-Frame PNG Audit]]></title><description><![CDATA[Audit sprite-sheet exports with a measured 12-frame PNG: verify grid math, frame order, RGBA pixels, transparency, engine timing, and pivots.]]></description><link>https://framesprite-workflow.hashnode.dev/sprite-sheet-export-qa-12-frame-png-audit</link><guid isPermaLink="true">https://framesprite-workflow.hashnode.dev/sprite-sheet-export-qa-12-frame-png-audit</guid><category><![CDATA[Game Development]]></category><category><![CDATA[Godot]]></category><category><![CDATA[unity]]></category><dc:creator><![CDATA[FrameSprite Workflow Team]]></dc:creator><pubDate>Mon, 26 Oct 2026 01:47:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6add6fc04b032073c0397857/0cb8a4f3-13f4-4ab5-80a1-58811f31f87a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Originally published by the FrameSprite Workflow Team at <a href="https://www.framesprite.com/guides/sprite-sheet-export-qa-12-frame-png-audit">framesprite.com</a>. We operate the linked browser packer. This article was prepared with AI writing assistance and checked against the downloadable export, source frames, evidence record, and vendor documentation. The character artwork is our own AI-generated sample.</em></p>
<p><strong>Quick answer.</strong> A <strong>sprite-sheet export audit</strong> checks that a packed image still contains the intended frames, order, geometry, and transparency before engine import. Confirm four separate things: the grid divides into whole cells; every cell contains its intended source frame; transparency survives; and the engine uses the intended playback speed and pivot. A correct PNG can still animate badly if the last two settings are wrong.</p>
<p><img src="https://www.framesprite.com/research/sprite-sheet-export-qa-12-frame/coffee-warlock-cast-12-frame-sheet.png" alt="Twelve real animation frames arranged as four columns and three rows in a transparent PNG sprite sheet" /></p>
<p><em>The actual downloaded sheet from the measured example. The 1200 × 630 social preview is a separately composed derivative; this full-size PNG is the evidence image.</em></p>
<p>This is an audit of one real export, not a benchmark of sprite-sheet tools or a promise about every input. The example starts with twelve numbered, transparent character frames. Its four-column, three-row output is a 3328 × 3360 PNG. We compared the RGBA pixels of all twelve output cells against their corresponding source files; the largest channel difference was zero in this sample. Download the <a href="https://www.framesprite.com/research/sprite-sheet-export-qa-12-frame/source-frames.zip">twelve source frames</a>, the <a href="https://www.framesprite.com/research/sprite-sheet-export-qa-12-frame/coffee-warlock-cast-12-frame-sheet.png">actual exported PNG</a>, and the <a href="https://www.framesprite.com/research/sprite-sheet-export-qa-12-frame/evidence-v1.json">per-cell evidence record</a> to check the result yourself. The test used our own AI-generated character art; the packer arranged existing frames rather than generating new motion.</p>
<table>
<thead>
<tr>
<th>Audit question</th>
<th>Observed in this sample</th>
<th>What you should verify in your own export</th>
</tr>
</thead>
<tbody><tr>
<td>Grid</td>
<td>4 columns × 3 rows</td>
<td>Rows equal <code>ceil(frame count / columns)</code></td>
</tr>
<tr>
<td>Cell geometry</td>
<td>832 × 1120 px</td>
<td>Sheet dimensions and padding match your declared cells</td>
</tr>
<tr>
<td>Sheet geometry</td>
<td>3328 × 3360 px</td>
<td>Whole-number boundaries, no unexpected margin</td>
</tr>
<tr>
<td>Transparency</td>
<td>RGBA alpha values include 0 and 255</td>
<td>Transparent and opaque pixels survive; inspect partial-alpha edges too</td>
</tr>
<tr>
<td>Contents</td>
<td>12 of 12 cells matched the source pixels</td>
<td>First, middle, last, and ideally every cell match their inputs</td>
</tr>
</tbody></table>
<h2>Start with an explicit frame order</h2>
<p>Name frames before packing them. In our test, <code>frame_000.png</code> through <code>frame_011.png</code> made the intended sequence easy to inspect. The <a href="https://www.framesprite.com/tools/sprite-sheet">browser packer</a> sorts selected images by filename; do not assume that your operating system's file-picker order will become the animation order. Its current page documents the pack, slice, and resize actions separately. For this task, use <strong>Pack a PNG sequence</strong>, not the slicer.</p>
<p>Open the source files on a checkerboard and check that they belong to the same action. A numbered sequence can still contain a duplicate pose, a missing pose, or a different camera angle. Packing faithfully preserves such mistakes. Keep a source folder and a copy of the ordered filenames so that a surprising playback result can be traced back to a specific input.</p>
<h2>Check the grid math before inspecting the artwork</h2>
<p>For twelve frames at four columns, <code>ceil(12 / 4) = 3</code> rows. With zero padding and a common 832 × 1120 cell, width is <code>4 × 832 = 3328</code> and height is <code>3 × 1120 = 3360</code>. These values match the downloaded file in our test. Each frame is located at column <code>index % 4</code> and row <code>floor(index / 4)</code>, starting at index zero.</p>
<p>Padding changes those equations. If a tool inserts <em>between-cell</em> padding <code>p</code> and no outer margin, width is <code>columns × cell width + (columns − 1) × p</code>; height uses rows in the same formula. Do not divide a padded sheet evenly and expect the correct cell width. Record the actual padding value and configure your engine's slicing to match it. Our zero-padding case is a measured example, not a universal recommendation for filtered textures.</p>
<p>The <a href="https://www.framesprite.com/guides/how-to-make-a-sprite-sheet">grid-making guide</a> explains canvas alignment and spacing choices. This article focuses on the separate question of <strong>whether the exported file actually meets its handoff contract</strong>.</p>
<h2>Reproduce the twelve-cell check</h2>
<p>The published evidence bundle contains <code>frame_000.png</code> through <code>frame_011.png</code>, the unaltered exported sheet, and a JSON record of the settings, source file hashes, and all twelve comparison results. The sheet's SHA-256 is <code>12c5349cbd4fa83867170b5f5e207442594ef435b183d756891a4bec7513db61</code>. Verify the downloaded file against that digest before comparing cells.</p>
<p>Decode both inputs and output to RGBA without resizing or color-profile normalization. For input index <code>i</code>, crop the 832 × 1120 output cell at <code>x = (i % 4) × 832</code>, <code>y = floor(i / 4) × 1120</code>. Compare the crop with its source PNG using a three-digit, zero-padded index: for example, <code>frame_006.png</code> for <code>i = 6</code>. Compare the RGBA pixels and record the largest absolute difference across the four channels. The <a href="https://www.framesprite.com/research/sprite-sheet-export-qa-12-frame/evidence-v1.json">JSON record</a> reports zero for each of the twelve cells, including frame zero, frame six, and frame eleven. A nonzero value in your reproduction calls for checking the file digests and decoding method before inferring that the export changed.</p>
<p>This test checks file contents, not whether the action reads well in motion. The source frames are available so the result is independently inspectable, while the artwork remains identified as FrameSprite's own AI-generated sample.</p>
<h2>Verify alpha and every cell, not only the preview</h2>
<p>The downloaded sample is an RGBA PNG whose alpha channel contains both transparent and opaque pixels. That establishes that the sheet has transparency; it does not by itself prove that every edge is clean. Examine a dark background and a light background. Look for opaque rectangles, colored fringes, and inconsistent character baselines. The <a href="https://www.w3.org/TR/png-3/">W3C PNG specification</a> describes PNG's alpha channel; a file extension alone is not proof that a particular image contains the alpha data you need.</p>
<p>For a stronger content check, crop each logical cell from the exported sheet and compare it with the corresponding source PNG at the same dimensions. Our recorded test compared all twelve RGBA cell crops channel by channel and found no differences. That is evidence for this one export. If your input canvases differ, first check the packer's centering behavior and compare the placed image region instead of assuming every source fills a whole cell. The <a href="https://www.framesprite.com/tools/sprite-sheet">tool page</a> says its packer uses the widest and tallest inputs as a common cell and centers smaller images.</p>
<p>If even one cell is wrong, inspect the filename sequence, input count, chosen columns, and any padding before changing engine animation settings. An engine cannot restore a frame that never made it into the sheet.</p>
<h2>Import the tested grid into an engine</h2>
<p>In Godot, create an <code>AnimatedSprite2D</code> and a <code>SpriteFrames</code> resource, then choose <strong>Add frames from a Sprite Sheet</strong>. Godot's <a href="https://docs.godotengine.org/en/stable/tutorials/2d/2d_sprite_animation.html">2D sprite animation guide</a> shows the sheet picker, horizontal and vertical frame counts, selection of the intended cells, and the separate Speed (FPS) control. For this exact sheet, enter four images horizontally and three vertically, select the twelve cells in order, and then set FPS according to the animation you want. The PNG does not encode that playback decision.</p>
<p>In Unity, import the texture as a sprite with <strong>Sprite Mode: Multiple</strong> and use the Sprite Editor to cut the grid into sprites. Unity documents both <a href="https://docs.unity3d.com/cn/6000.0/Manual/sprite/sprite-editor/open-sprite-editor.html">opening the Sprite Editor</a> and <a href="https://docs.unity3d.com/cn/6000.0/Manual/sprite/sprite-editor/automatic-slicing.html">grid-based slicing options</a>. For this no-padding sample, use the measured 832 × 1120 cell geometry or the matching four-by-three grid, inspect the first and last slices, and set animation timing in the engine. Review the pivot or origin against your game's ground contact or rotation point; the packing operation did not choose a gameplay pivot.</p>
<p>The engine steps above are handoff instructions based on the vendors' documentation. We validated the exported PNG and its cells; we did <strong>not</strong> run this specific twelve-frame sample through a Unity or Godot project. Treat an in-engine playback check as the final acceptance step in your own project.</p>
<h2>A five-step acceptance pass</h2>
<ol>
<li>Read the sheet's pixel dimensions and color mode from the downloaded file.</li>
<li>Recalculate rows, columns, cell size, padding, and any outer margin from the settings you actually used.</li>
<li>Inspect frame zero, one middle frame, and the last frame on light and dark backgrounds. Compare every cell if the asset is release-critical.</li>
<li>Import the sheet with the same grid settings. Verify that the engine shows twelve unique intended frames in order.</li>
<li>Set FPS, loop behavior, and pivot in the engine. Watch the first-to-last seam at game scale; a correct export does not guarantee a good loop.</li>
</ol>
<p>If the grid and alpha checks pass but the character still jumps, the next question is visual alignment, not file corruption. Use the <a href="https://www.framesprite.com/guides/fix-sprite-animation-jitter">sprite-animation jitter guide</a> to separate changing artwork bounds from a changing origin. If the loop pauses only at the seam, review duplicated endpoints with the <a href="https://www.framesprite.com/guides/fix-sprite-animation-loop-stutter">loop-stutter guide</a>.</p>
<h2>What this test does not prove</h2>
<p>The twelve-frame result proves a narrow claim: with these source PNGs and settings, the exported grid dimensions and all twelve cell pixels matched the recorded inputs. It does not establish a universal error rate, compare performance against separate textures, validate every browser, or prove engine-ready animation timing. The transparent zero-gap grid may also need different spacing or filtering choices in a different renderer.</p>
<p>For a reusable process, keep the ordered source sequence, the exported PNG, the packing settings, and the engine's FPS/pivot settings together. That small record makes the next import failure diagnosable rather than a guessing exercise.</p>
<p><strong>Editorial note:</strong> The <a href="https://www.framesprite.com/about#workflow-team">FrameSprite Workflow Team</a> authors this article and operates the linked browser tool; that page describes its verification and corrections policy. The character art in the measured sample was AI-generated by the team. Measurements are from one published export and were checked against its source frames; no third-party customer outcome or comparative speed claim is asserted. For corrections, use the site's <a href="https://www.framesprite.com/contact">contact page</a>.</p>
]]></content:encoded></item></channel></rss>