This guide will help you get started quickly with the JSON Optimizer.
Load Project: Use the InputNode to load your .json, .atlas, and .png files.
Choose a preset (Plus mode): In Plus mode, use the right panel to select one of the built-in presets (e.g., "Basic Fallback"). This automatically builds an optimization graph for you. In Free mode, build a minimal manual graph: InputNode → QuantizerNode → OutputNode.
Run the graph: Click the "Run Graph" button.
Compare results: Switch to the "Viewer" tab to visually compare the original and optimized animations.
Review changes: In the "Results" and "Statistics" tabs, you will find detailed information about which keys were modified or deleted.
Socket Types & Data Flow
socketstypesdataconnections
This article explains the data types used by sockets in the node graph and what can be connected where.
Quick rule
Most processing nodes operate on a Spine Payload. Atlas-related nodes operate on an Atlas Project or Sprites. Use ValidatorNode when you’re unsure what a socket expects.
Socket types
Spine Payload — runtime-friendly representation of the Spine project used by most processing nodes.
Atlas Project — atlas metadata + page images used by atlas nodes.
Recommendation: Always start with Lossless optimizations. Use Lossy methods only when further file size reduction is required, and always check the result visually.
How to Measure Effectiveness
guidemetricsstatistics
To understand how effective your optimization is, pay attention to the following metrics:
File Size: The final size of the .json file is the main goal of optimization.
Keyframe Count: In the "Statistics" tab, you will find tables and charts showing how many keyframes were removed in each animation.
Visual Comparison: Always use the "Viewer" tab to compare the "before" and "after" animations. Ensure that Lossy optimizations have not introduced unacceptable visual artifacts.
Report in the "Results" Table: Here you can examine in detail every specific change that was made to your data.
Viewer: Controls Panel
viewercontrolsui
The Viewer tab overlays a Controls panel on top of the canvas. It’s split into three groups:
Animation Controls
Animation — selects which Spine animation is playing.
View Options — numeric offsets used to position the comparison view: Spacing X / Spacing Y (distance between original/optimized) and Offset X / Offset Y (global shift).
The Performance panel shows real-time runtime metrics for the currently playing skeleton(s).
Real-time
FPS — frames per second measured by the monitor.
Frame Time — time per frame in milliseconds (lower is better).
FPS chart — last 120 samples displayed as bars.
Skeleton metrics
Visible Slots — how many slots were visible on the last sample.
Avg. Visible — rolling average of visible slots (more stable than a single frame).
Vertices — current vertex count used for rendering.
Bounds (px) — current skeleton bounds width×height in pixels.
Avg. Bounds — rolling average bounds.
Largest Texture — the largest atlas page (by dimensions) detected for the skeleton.
Texture Memory — estimated total texture memory for loaded atlas pages. When available, the panel also shows a per-page breakdown (file name, dimensions, and estimated size).
Improvements (when Optimized is available)
Slot Improvement — compares Avg. Visible between Original vs Optimized.
Memory Change — compares Texture Memory between Original vs Optimized.
Viewer: Timeline
viewertimelineanimationplaybackguide
The Timeline panel is a dockable animation sequencer at the bottom of the Viewer. It lets you arrange animation clips on multiple tracks, control playback, and preview transitions.
Tracks & Clips
Up to 4 animation tracks — each track maps to a Spine trackIndex layer. Higher tracks blend on top of lower ones.
Add animation — click the + button on any track to open the animation picker (with search filter).
Drag & resize clips — drag the clip body to move it along the track; drag left/right edges to change start time or duration.
Delete clips — select a clip and press Delete or Backspace.
Overlap detection — overlapping clips on the same track are highlighted in red.
Transport Controls
Play / Pause / Stop — standard playback controls.
Previous / Next — jump to the start of the previous or next clip.
Animation speed — slider from 0.1× to 3.0×.
Auto-stop — playback stops automatically at the end of the timeline.
Transition Mixing
Global mix duration — sets the default crossfade time between consecutive clips.
Per-transition overrides — click the transition bubble between two clips to set a custom mix duration. You can reset individual overrides back to the global value.
Seeking & Scrubbing
Playhead — drag the handle on the time ruler or click the ruler to seek.
Cursor time preview — hovering over the track area displays the time under the cursor.
Web Worker — seek and event computation is offloaded to a background worker for smooth performance.
Physics-Aware Playback
Toggle physics between Update (active constraints) and None (disabled).
During seek, the skeleton time is advanced to properly settle physics constraints.
Multi-Skeleton Support
Target pair selector — each track can target all pairs (🌐 All pairs) or a specific skeleton.
Per-skeleton position and scale — X/Y offset and scale inputs for individual skeletons.
Draw order — numeric input to control render layer priority.
Toggle: track info, time, frame number, FPS, skeleton labels.
Custom text — watermark string.
Position — 4 corners (top-left, top-right, bottom-left, bottom-right).
Font size — 8–64px, plus text color picker.
Repeat & Multi-Skeleton
Repeat count — 1–100 loop iterations.
Target pair — record a specific skeleton or all.
Skeletons mode — Combined (single file) / Separate files (one file per skeleton).
Filename Template
Template string with variables: {project}, {animation}, {W}, {H}, {fps}. A live preview of the resolved filename is displayed.
Timeline Integration
The recording range appears as a red/orange dashed clip on the timeline. Drag edges to resize the range, drag the body to reposition, or use the settings button (⚙️) to open full recording settings.
Results Tab
resultstableoptimization
The Results tab shows detailed optimization output.
Results table — a row-per-change view of JSON optimization results (e.g. which animation/bone/property/keyframe was modified or removed).
Sprite/Atlas comparison — when texture optimization is used, this section compares sprite sizes and packing outcomes.
Tip: use the Results view to answer: what exactly changed?
Statistics Tab
statisticschartsmetrics
The Statistics tab summarizes optimization results as charts and aggregated metrics.
Optimization Chart — visualizes optimization impact over the dataset (when JSON optimizations produced changes).
Metrics Table — key numbers such as original/new sizes and reduction percent.
Texture Packing Statistics — if atlas repacking was used, shows pages, total/used area, efficiency, and a per-page breakdown.
Use this tab to answer: how much did we improve overall?
Pipeline Examples (Video)
videoexamplespipelinesyoutube
Below are complete, real-world pipelines from the official YouTube playlist.
Spine2D Experiments 1: Curve Optimization with RDP Algorithm — baseline Spine 2D optimization demo: the Ramer–Douglas–Peucker (RDP) algorithm removes redundant curve/key data to shrink JSON. RDP can remove important keys, so always review animations and exclude sensitive bones/slots with filter nodes before the RDP node. Watch on YouTube
Spine2D Experiments 2: Curve Optimization with Quantization Algorithm — quantization is the safest Spine 2D curve optimizer: rounding values (often to 1 decimal) gives immediate size wins. Example: [0.115,14.5,0.222,-27.78] → [0.1,14.5,0.2,-27.8]. Use the quantization node as the last step before the output node to lock in optimization; the video shows before/after with near‑imperceptible visual change. Watch on YouTube
Spine2D Experiments 3: Rebuilding Curves with Spline & Refit Algorithms — Spline is lossless for straight segments (removes redundant middle keys), while Refit is lossy but powerful: it rebuilds complex sections as a single Bézier curve. These algorithms are ideal after physics baking and cleanup via the Schneider node. Note: Refit iterations above 100 can take minutes to find the best curve. The video shows how both methods work and the % of optimization gained. Watch on YouTube
Spine2D Experiments 4: Removing Redundant Data with Attachment Visibility & Cleanup Nodes — structural Spine 2D optimization using Attachment Visibility and Cleanup nodes. Attachment Visibility disables attachments when alpha fades to 0 and restores them on fade‑in, reducing draw calls and removing redundant keys. Cleanup removes logical redundancies: unused color/alpha tracks, IK rotation keys fully controlled by constraints, and path constraint keys. The video shows how these logic‑based optimizations differ from curve simplification. Watch on YouTube
Spine2D Experiments 5: Building Non-Linear Pipelines (Filters & Merge Node) — non-linear Spine 2D optimization: split the data stream with filters (Animation, Bone, Slot, Parameter, or Skin), run different algorithms on parallel branches (for example, aggressive compression for body bones while keeping facial animation lossless), then combine branches with the Merge node into one skeleton. This workflow goes beyond a simple Input → Optimize → Output pipeline and lets you target only what should be optimized. Watch on YouTube
Spine2D Experiments 6: Texture Repacking & Selective Scaling with Custom Nodes — advanced texture optimization pipelines: the Atlas Unpacker/Repacker nodes let you unpack, resize/compress, and repack atlases inside one graph, faster than standard Spine workflows. Using the non‑linear pipeline, you can split assets, keep character textures at 100% while scaling others down to 25%, then merge everything back into a single shared atlas. Atlas/Asset Viewer nodes provide visual inspection and filtering of assets and atlases at every stage. This makes it easier to define resize targets, process selectively, and recombine results as a reusable template. Watch on YouTube
Spine2D Experiments 7: Instant Skeleton Resizing with a Scale Node — end‑to‑end Spine 2D optimization pipeline plus skeleton scaling. The Scale node replaces a tedious Spine workflow (export/import, repack/unpack, fix paths) with a single graph step: set the target size, preview instantly in the viewport, and iterate in real time. The video ties together curve/key optimization, asset processing, and skeleton resizing in one streamlined pipeline. Watch on YouTube
Spine2D Experiments 8: Public Beta Node Overview — walkthrough of every node available in the public beta and how re‑polish bridges animator and developer needs. Covers redundant key cleanup, Bézier‑to‑linear simplification, rounding for size, invisible attachment removal, unused property cleanup, non‑English character detection, and atlas unpack/filter/scale/repack. The Dual Viewer validates results with side‑by‑side visual and FPS comparison. Public Beta is live at re-polish.com. Watch on YouTube
Spine2D Experiments 9: Baking Physics Constraints to Keyframes — In this video, I demonstrate my latest experiments with baking physics constraints into standard keyframes. The new baking algorithm ensures: Perfect Loops (zero jitter or jumps between start and end frames), Optimized File Size (automatic curve cleanup keeps the JSON size close to the original), and Backwards Compatibility (baked keys let you use Spine 4.2 physics animations in Spine 4.1 or even 4.0). Watch on YouTube
Nodes
Input
Imports Spine project files, JSON-only inputs, demo assets, and ZIP packages into the graph.
Purpose: This is the main entry point for most workflows. It reads JSON, atlas, and textures and normalizes them into internal data that downstream nodes can process.
Supported modes:
1. Full project: JSON + atlas + textures
2. JSON-only: process skeleton/animation data without an atlas
3. ZIP package: a packaged zip containing a complete project
4. Demo assets: built-in sample data for quick testing
Main inputs:
- **project_in**: packaged project input for upstream handoff or re-entry into the graph
Main outputs:
- **payload_out**: normalized data for optimization nodes
- **original_json_out**: source JSON for comparison or reconstruction
- **atlas_project_out**: parsed atlas project when textures are provided
Picture Input
Adds standalone PNG/JPEG images to the viewport so you can build cleaner previews and presentation shots.
Purpose: Use this node to place images directly into the viewport as presentation layers or visual helpers. It is useful when you want to decorate a scene, add supporting graphics, and make project previews look more polished before recording videos or demos.
Workflow: Load one image, then use the viewport tools to scale it, move it, and adjust its z-index so it sits correctly inside your composition. This makes it convenient for building attractive showcase shots without touching the main Spine project data.
This node is standalone and does not expose graph sockets.
Output
Collects the final results and lets you download optimized data as separate files, grouped file sets, or archives.
Purpose: Acts as the export hub for optimized JSON, atlas data, textures, and sprites. It can assemble results from a single JSON/atlas/textures set, from multiple file sets, or from sprites only when you want image output without rebuilding a full Spine project.
Export options:
- Download files individually or as a single archive
- Download packed results or an archive with unpacked files
- Export JSON in compact form or in a readable pretty-printed form
- Export complete Spine project data or only sprite/image results
Chaining workflow: OutputNode does not have to be the absolute end of the graph. You can pass its outputs into another pipeline stage and run the same files through additional optimization passes. In practice, key and curve optimization was tested for up to 3 consecutive iterations.
Canonical socket keys:
- Inputs: **payload**, **original_json**, **atlas_project**, **payloads**, **atlas_projects**, **original_jsons**, **sprite_batch_in**
- Outputs: **reconstructed_json_out**, **original_json_passthrough_out**, **payload_out**, **changes**, **atlas_assets**
RDP
Simplifies animation curves using the Ramer-Douglas-Peucker (RDP) algorithm.
Purpose: Reduces the number of keyframes in linear or near-linear animation segments by removing points that lie on a straight line between two others.
Golden Rule: Disabled by default. It protects curves with significant shape (area/curvature) from being flattened to linear. Disable it only for more aggressive compression after visual review.
Socket keys: payload_in, payload_out, changes
Approximates dense keyframe sequences with fewer Bezier curves to reduce size.
Purpose: Fits fewer curves while staying within a tolerance.
Socket keys: payload_in, payload_out, changes
Quantizer
Reduces precision of numeric values in keyframes and curves.
Purpose: Simple but effective way to reduce file size by rounding numbers to a specified number of decimal places.
Usage: Can be applied to almost any animation data. Becomes more effective with higher keyframe counts.
Caution: Too aggressive quantization (low precision) can cause jitter or visual artifacts in animations.
Socket keys: payload_in, payload_out, changes
Cleanup
Performs various cleanup tasks to remove redundant or unnecessary animation data.
Purpose: Specialized tool for removing specific types of redundant data that other optimizers might miss.
Usage: Connect your payload to 'payload_in' and take the result from 'payload_out'. If you want a per-change report, also use 'changes'.
Socket keys:
- Inputs: payload_in
- Outputs: payload_out, changes
Currently supported cleanups:
1. **Remove Unused Color/Alpha Tracks**: Removes color/alpha timelines for slots that are never visible during the animation.
2. **Remove Redundant IK Rotations**: Removes rotation keyframes from bones that are fully controlled by an IK constraint with 100% mix.
3. **Remove Path Constraint Keys**: Removes rotate/translate keyframes from bones fully controlled by a path constraint (mix values at 100%).
4. **Sanitize Non-English Characters**: Replaces non-English characters in names/identifiers to avoid issues in downstream tools.
Scale
Scales all numeric values in animation keyframes by a specified factor.
Purpose: Uniformly scales animation data, useful for resizing skeleton proportionally or adjusting animation intensity.
Usage: Connect **payload_in** and take the result from **payload_out**.
Socket keys: payload_in, payload_out
Schneider
Fits smooth Bezier curves to animation keyframes using the Schneider curve fitting algorithm.
Purpose: Advanced curve fitting that produces natural-looking Bezier curves from dense keyframe sequences.
Availability: **Plus-only node**.
How it works: The Schneider algorithm analyzes keyframe positions and tangents to generate optimal Bezier control points that closely match the original motion.
Parameters:
- **Error Tolerance**: Maximum allowed deviation from original keyframes. Lower = more accurate, higher = smoother curves.
- **Corner Angle**: Threshold angle (degrees) at which to split curve into segments.
Best for:
- Hand-drawn or imported animations with many keyframes
- Converting linear interpolation to smooth Bezier curves
- Reducing keyframe count while maintaining curve quality
Note: More computationally intensive than simpler algorithms like RDP, but produces superior curve quality.
Socket keys: payload_in, payload_out, changes
Physics Constraint Bake
Bakes Spine PhysicsConstraint motion into bone rotate/translate keyframes and removes physics timelines.
Purpose: Converts runtime physics simulation into explicit keyframes so animations are deterministic and editable without physics constraints. After bake, physics constraints and physics timelines are removed from the payload.
Availability: **Plus-only node**.
Inputs/Outputs:
- **payload_in** → **payload_out** (baked)
- **changes** (optional change list)
Controls:
- **Sample FPS**: Simulation sampling rate for the bake.
- **Bake Rotation**: Write baked rotation keys.
- **Bake Translation**: Write baked translation keys.
- **Bake Translation (Children)**: Apply translate bake to child bones that rely on physics motion.
Notes:
- Requires original Spine JSON to reconstruct simulation data.
- Use when you want to remove physics constraints but keep the motion.
Attachment Visibility
Optimizes rendering by setting a slot's attachment to null when its alpha is zero.
Purpose: Prevents the game engine from having to process or render invisible attachments.
Usage: Processes **payload_in**, outputs optimized **payload_out**, and optionally reports **changes**.
Socket keys: payload_in, payload_out, changes
Payload Merger
Merges multiple processed animation payloads back into a single unified payload.
Purpose: Essential for parallel processing pipelines where different animations or bone groups are optimized separately and need to be recombined.
Inputs:
- **base**: Master payload (skeleton structure)
- **overrides**: One or more payloads whose tracks replace base tracks
Output:
- **merged_out**: Merged payload
Socket keys: base, overrides, merged_out
Animation Viewer
Visual tool for inspecting and comparing animation curves before and after optimization.
Purpose: Provides graphical representation of keyframes and curves for selected track.
Usage: Connect **before_in** and **after_in** to overlay original vs optimized. Optionally connect **changes** to highlight impacted tracks.
Socket keys: before_in, after_in, changes
Animation Filter
Filters animation tracks based on animation name (e.g., 'run', 'idle').
Purpose: Useful for applying different optimization strategies to different animations.
Usage: Filters **payload_in** into **payload_out** for single-payload workflows, or accepts **project_in** and forwards the enriched project through **project_out** so downstream project-aware nodes can discover animations immediately. The node also exposes **animation_list** for UI selection.
Socket keys: payload_in, project_in, payload_out, project_out, animation_list
Asset Filter
Filters atlas assets by name, works in two modes: before unpacker (filters atlas text) or after unpacker (filters sprites).
Purpose: Controls which assets are included in the workflow - either which assets to unpack from atlas, or which unpacked sprites to include in repacking.
Inputs/Outputs:
- Atlas mode: **atlas_in** → **atlas_out**
- Sprites mode: **sprites_in** → **sprites_out**
Socket keys: atlas_in, atlas_out, sprites_in, sprites_out
Bone Filter
Filters bone animation tracks based on bone name.
Purpose: Allows targeting or excluding specific bones from the optimization process.
Usage: Filters **payload_in** into **payload_out**.
Socket keys: payload_in, payload_out
Skin Filter
Filters animation and asset data based on skin names.
Purpose: Process only specific skins from a multi-skin Spine project.
Usage: Filters **payload_in** into **payload_out** for payload workflows, or accepts **project_in** and returns **project_out** with project metadata enriched by the selected skin subset.
Socket keys: payload_in, project_in, payload_out, project_out
Slot Filter
Filters slot animation tracks based on slot name.
Purpose: Useful for targeting or excluding specific slots that may have special timing or visibility requirements.
Usage: Filters **payload_in** into **payload_out**.
Socket keys: payload_in, payload_out
Parameter Filter
Filters animation tracks based on their property type (e.g., rotation, scale, color).
Purpose: Allows applying subsequent optimizations only to specific types of animation data.
Usage: Filters **payload_in** into **payload_out**.
Socket keys: payload_in, payload_out
Atlas Unpacker
Extracts individual sprites from a Spine texture atlas.
Purpose: Breaks down an atlas file into its component sprites, allowing for individual analysis or repacking.
Usage: Connect **atlas_project** from InputNode to **atlas_project** (or the legacy **atlas_project_in**) on this node. Optionally connect **skeleton_json_in** (from InputNode's **original_json**) to enable mesh-aware cropping (trims sprites to mesh hull bounds instead of rectangular bounds, which can significantly reduce texture memory for mesh attachments).
Outputs:
- **sprites_out**: Standardized sprites array (for viewer/repacker)
- **sprites_data_out**: Extracted sprite images/metadata (structured bundle)
- **atlas_out**: Atlas project passthrough
Socket keys: atlas_project, atlas_project_in, skeleton_json_in, sprites_out, sprites_data_out, atlas_out
Atlas Repacker
Repacks individual sprites into one or more new, optimized texture atlases.
Purpose: Optimizes texture memory and potentially reduces draw calls by creating efficient sprite sheets.
Usage: Accepts sprites either via **sprites_data_in** (structured sprites bundle) or via **sprites_out** (standardized sprites array). If needed for alpha handling / polygon packing, provide the original skeleton via **original_json**. Outputs a packed atlas as **atlas_out**.
Socket keys: sprites_data_in, sprites_out, original_json, atlas_out
Atlas Viewer
Lightweight atlas visualization and analysis tool for inspecting atlas structure before unpacking.
Purpose: Provides a fast way to preview atlas pages and regions without performing the heavy unpacking operation. Helps validate atlas structure and identify unused regions.
Usage: Connect a single atlas project to **atlas_project** (or legacy **atlas_in** / **atlas**), or inspect a batch of atlas projects via **atlas_projects**. Optionally connect skeleton JSON to **json** for usage analysis.
Socket keys: atlas_project, atlas_projects, atlas_in, atlas, json
Atlas Merger
Combines multiple atlas sources into a single unified atlas.
Purpose: Merge multiple atlas projects into one.
Input:
- **atlas_inputs**: Multi-input array of atlas projects
Outputs:
- **atlas_out**: Merged atlas project
- **merged_out**: Legacy merged output
- **merged_atlas_out**: Legacy merged output
Socket keys: atlas_inputs, atlas_out, merged_out, merged_atlas_out
Typical pipeline:
```
InputA → Unpacker → Repacker ─┐
├→ Atlas Merger → Unpacker → Repacker → Output
InputB → Unpacker → Repacker ─┘
```
Before Repacker the data is individual sprites; after Repacker it becomes an atlas project. Atlas Merger combines the atlas projects, then a final Unpacker → Repacker pass repacks everything tightly into a single unified atlas.
Asset Viewer
Displays individual sprites from an unpacked atlas.
Purpose: Visual debugging tool for atlas manipulation.
Usage: Accepts sprites via **sprites_out** (standard) or legacy **sprites_in** / **sprites_data**.
Socket keys: sprites_out, sprites_in, sprites_data