Skip to content

Recipe: Exporting to Phaser

Take a sprite with named animations + slices for hitboxes, export it as a sprite sheet + JSON, and load it into a Phaser 3 project. The editor's exporter is Aseprite-compatible, which Phaser's loader.atlas consumes directly.

![Screenshot: the Motestack export dialog on the Sprite Sheet tab with the Aseprite JSON output and a Phaser scene side-by-side showing the hitbox rectangle drawn from the slice — placeholder]

Goal

Hand a single PNG + JSON pair to Phaser and call sprite.play('walk'), sprite.body.setSize(...) reading from a slice — no glue code, no manual frame-by-frame definitions.

What ships in the JSON

The editor emits a fully Aseprite-compatible Hash layout. Phaser's Phaser.Loader.Atlas understands this layout natively — same JSON shape Aseprite uses.

Important fields:

json
{
  "frames": {
    "frame0.png": { "frame": { "x": 0, "y": 0, "w": 32, "h": 32 }, "duration": 100, ... },
    ...
  },
  "meta": {
    "app": "motestack",
    "version": "0.10.x",
    "frameTags": [
      { "name": "idle", "from": 0, "to": 3, "direction": "forward" },
      { "name": "walk", "from": 4, "to": 11, "direction": "pingpong" }
    ],
    "slices": [
      {
        "name": "hitbox",
        "color": "#ff0000",
        "keys": [
          { "frame": 0, "bounds": { "x": 8, "y": 4, "w": 16, "h": 24 } },
          { "frame": 4, "bounds": { "x": 6, "y": 4, "w": 20, "h": 24 } }
        ]
      }
    ]
  }
}

Steps in Motestack

1. Tag your animations

In the Frames timeline, click + Tag over the frames you want to name. Set name = "walk", direction = "pingpong", etc. Repeat for idle, attack, death, …

2. Author slices for hitboxes / hurtboxes

Y activates the Slice tool. Drag a rectangle on the canvas to create the slice. In the Slices panel:

  • Name: hitbox (or hurtbox, pickup, feet-anchor).
  • Color: any colour — for visual identification only, doesn't affect export.
  • Per-frame keys: drag the slice on different frames to give it different bounds per frame. The exporter remaps frame indices to the local sprite-sheet positions.

3. Export the sprite sheet

Ctrl+K → "Export sprite sheet". In the dialog:

  • Source: pick the sprite or animation you want to export.
  • Layout: grid is fine for most cases.
  • Padding: 0 unless your engine needs gutters.
  • Scale: 1 — upscale at engine load time if you want, don't bake it in.

Save both files. You get myhero.png + myhero.json.

Steps in Phaser

1. Load the atlas

ts
preload() {
  this.load.atlas(
    'hero',
    'assets/myhero.png',
    'assets/myhero.json',
  )
}

2. Create the sprite

ts
create() {
  const hero = this.add.sprite(100, 100, 'hero', 'frame0.png')

  // Phaser auto-creates animations from the `meta.frameTags` array in
  // the JSON (Phaser ≥ 3.50 with `loader.aseprite` is even simpler;
  // `loader.atlas` requires you to define them once):
  this.anims.createFromAseprite('hero')

  hero.play({ key: 'walk', repeat: -1 })
}

(For older Phaser versions, iterate meta.frameTags manually and call this.anims.create({ key, frames, ... }).)

3. Read slices for hitboxes

Slices live under meta.slices. Phaser doesn't auto-bind them to physics bodies — read the JSON yourself:

ts
const json = this.cache.json.get("hero");
const hitbox = json.meta.slices.find((s) => s.name === "hitbox");

// Per-frame keys: pick the most recent key with frame ≤ current frame
function resolveSlice(slice, frameIdx) {
  let active = null;
  for (const k of slice.keys) {
    if (k.frame <= frameIdx) active = k;
    else break;
  }
  return active?.bounds;
}

hero.on(Phaser.Animations.Events.ANIMATION_UPDATE, (_anim, frame) => {
  const bounds = resolveSlice(hitbox, frame.index);
  if (bounds)
    hero.body.setSize(bounds.w, bounds.h, false).setOffset(bounds.x, bounds.y);
});

You now have a hitbox that resizes with each frame of the animation, driven entirely by the metadata you authored in Motestack.

Tips

  • Pivots (slice → pivot point) are also in the JSON: slice.keys[i].pivot = { x, y }. Use them to attach effects (muzzle flash, footstep dust) at sprite-relative anchors.
  • Onion-skin tints don't export — they're a workspace overlay only. The PNG is the clean composed output.
  • Color cycles don't bake. If your project uses indexed mode + cycles, the sprite sheet exports static colours. To animate cycles in Phaser, ship the sprite sheet plus a cycles JSON and rotate slots at runtime via a custom material.
  • Frame durations ride in the JSON (frames[].duration). Phaser uses them by default when constructing animations from frameTags.

See also

Motestack is a personal hobby project. The editor and these docs ship under no warranty — back up your `.mstack` files.