---
title: "Phaser JS and Spine 2D: A Great Combination for Game Development"
description: "Developers can create rich and engaging games with the available tools and frameworks. One such powerful duo that has gained traction is Phaser JS and Spine 2D. Together, they provide game developers with the ability to create smooth animations and integrate them seamlessly into their games."
author: "Tajammal Maqbool"
last_updated: "2024-10-18"
---

# Phaser JS and Spine 2D: A Great Combination for Game Development

> Developers can create rich and engaging games with the available tools and frameworks. One such powerful duo that has gained traction is Phaser JS and Spine 2D. Together, they provide game developers with the ability to create smooth animations and integrate them seamlessly into their games.

**Author:** Tajammal Maqbool  
**Published:** October 18, 2024  
**Tags:** phaser js, game development

Developers can create rich and engaging games with the available tools and frameworks. One such powerful duo that has gained traction is [Phaser JS](https://phaser.io/) and [Spine 2D](http://esotericsoftware.com/). Together, they provide game developers with the ability to create smooth animations and integrate them seamlessly into their games.

<iframe width="100%" height="415" src="https://www.youtube.com/embed/HJdQ5GQGahI?si=sHNbDctSLonZXNfk" title="YouTube video player" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerPolicy="strict-origin-when-cross-origin" allowFullScreen></iframe>

In this blog, we’ll explore why [Phaser JS](https://phaser.io/) and [Spine 2D](http://esotericsoftware.com/) are a great combination for game development and how they work together to bring your games to life.

## Phaser JS:
[Phaser JS](https://phaser.io/) is a fast and robust open-source game framework used to develop HTML5 games. Phaser JS is particularly great for developing 2D games and supports both Canvas and WebGL rendering.

<Image
  src="/images/blogs/phaser-js-website-game-development.avif"
  width="1200"
  height="780"
  alt="Phaser JS for game development"
  title="Phaser JS"
  sizes="100vw"
/>

## Spine 2D:
[Spine 2D](http://esotericsoftware.com/) is an animation tool designed specifically for creating 2D skeletal animations. Unlike traditional frame-by-frame animations, Spine 2D focuses on the movement of bones and joints to animate characters.
<Image
  src="/images/blogs/spine-2d-for-animation.avif"
  width="1200"
  height="780"
  alt="Spine 2D for animation"
  title="Spine 2D"
  sizes="100vw"
/>

> Why? Some questions now should be raised in mind, why use Spine 2D for creating animations, Why not use Spritesheets?

Let me explain this,

hmm, Spritesheet is the most common way to create animations in game development but it has some drawbacks which we need to resolve,

<Image
  src="/images/blogs/spritesheet.avif"
  width="1200"
  height="780"
  alt="What is Spritesheet"
  title="Spritesheet"
  sizes="100vw"
/>

## Spritesheet
A sprite sheet is a bitmap image file that contains several smaller graphics in a tiled grid arrangement.

Let’s assume you have one character animation sprite sheet, which is 1MB in size, so what if you have 20 characters in a game? and Total will be 20MB.

This is just for characters, so imagine if you have backgrounds and much more then your game will take minutes to load. Nowadays, nobody wants to wait for seconds then who will wait for mints?

<Image
  src="/images/blogs/loading-screen-of-game.avif"
  width="1200"
  height="780"
  alt="Loading Screen in Games"
  title="Loading Screen"
  sizes="100vw"
/>

Spine 2D is the best solution who this problem. Now you will not load multiple files but a single image which will have all your images along with the .atlas file and the .json files.

<Image
  src="/images/blogs/files-to-need-in-phaserjs-and-spine-2d.avif"
  width="1200"
  height="780"
  alt="Files for Phaser js and Spine 2D"
  title="Phaser js and Spine 2D"
  sizes="100vw"
/>

<Image
  src="/images/blogs/texture-atlas-phaser-js.avif"
  width="1200"
  height="780"
  alt="Texture Atlas in Phaser JS"
  title="Phaser JS"
  sizes="100vw"
/>

The problem has just gone —

No, need to load multiple spritesheets. There are many other issues with sprite sheets like “multiple requests from the server to fetch all files”, “more code need to write” etc.

But with Spine 2D just a single .avif and the most interesting thing is that if your character has multiple skins like red, white, or black then even Spine 2D has a better solution for it. You just need to select a skin in the code by using .setSkin and also no need to recreate animations for each skin in Spine 2D. Much more…

**Why need to use Spine 2D?**
* **Efficiency**: Since you are manipulating bones and not redrawing entire frames, the animations are lightweight, using fewer resources.
* **Flexibility**: Animating with bones allows for smoother transitions and reusable animations across different characters or assets.
* **Dynamic Animation**: You can create animations that interact with physics or respond dynamically to gameplay, such as ragdoll effects or reactive movements.

Let’s now implement the code to add characters to the Phaser JS game. I am using “@esotericsoftware/spine-phaser”: “4.1.24” and “phaser”: “3.85.2”

## Installation
Install the plugin of Spine 2d in Phaser JS by using the below command:

```bash
npm install @esotericsoftware/spine-phaser
```

## Configuration
After the installation, need to update the configuration of Phaser JS. Add the Plugin in the config which will be used to create the game.

```js
import Phaser from "phaser"
import PreLoadingScene from "./Scenes/PreLoadingScene"
import LoadingScene from "./Scenes/LoadingScene"
import MainScene from "./Scenes/MainScene"
import GameScene from "./Scenes/GameScene"
import { SpinePlugin } from "@esotericsoftware/spine-phaser"

const config: Phaser.Types.Core.GameConfig = {
    type: Phaser.AUTO,
    width: 1080,
    height: 720,
    physics: {
        default: 'arcade',
        arcade: {
            gravity: { x: 0, y: 100 },
            debug: false
        }
    },
    parent: "body",
    dom: {
        createContainer: true
    },
    scale: {
        mode: Phaser.Scale.FIT,
        autoCenter: Phaser.Scale.CENTER_BOTH
    },
    scene: [PreLoadingScene, LoadingScene, MainScene, GameScene],
    plugins: {
        scene: [{
            key: "spine.SpinePlugin",
            plugin: SpinePlugin,
            mapping: "spine"
        }]
    }
}

export default config
```

## Loading Assets
Using the Spine plugin, need to load the .json file, the .atlas file which will create the Spine Object later. There are three functions which we can use to load files.

The common way to import the Spine assets into a Phaser game is using the Phaser loader methods:
* spineBinary(key, url) - Loads .skel files containing skeleton and animation data.
* spineJson(key, url) - Loads the .json files containing skeleton and animation data.
* spineAtlas(key, url, premultipliedAlpha) - Loads the texture atlas files.

```js
import Phaser from "phaser"

export default class LoadingScene extends Phaser.Scene {
    constructor() {
        super({ key: "LoadingScene" })
    }

    preload() {
        this.load.image("logo2", "images/logo2.avif")
        this.load.image("buildings", "images/buildings.avif")
        this.load.image("tile", "images/tile.avif")

        this.load.spineJson("stickman", "stickman/stickman.json")
        this.load.spineAtlas("stickman-atlas", "stickman/stickman.atlas")

        this.cameras.main.setBackgroundColor(0xffffff)
        this.add.image(
            this.game.config.width as number / 2,
            this.game.config.height as number / 2 - 100,
            "full-logo"
        ).setScale(0.95)

        const barWidth = this.game.config.width as number - 60
        const barHeight = 30
        const barX = 30
        const barY = this.game.config.height as number - 70

        const loadingBar = this.add.graphics()
        loadingBar.fillStyle(0xff5592, 1)
        loadingBar.fillRoundedRect(barX, barY, barWidth, barHeight, 15)

        const progressBar = this.add.graphics()

        let progressText = this.add.text(
            this.game.config.width as number / 2,
            barY + 60,
            "Loading: 0%",
            { fontFamily: "Super-Smash", align: "center", color: "#f70343", fontSize: "24px" }
        ).setOrigin(0.5, 1)

        this.load.on("progress", (value: number) => {
            if (value > 0) {
                progressBar.clear()
                progressBar.fillStyle(0xf70343, 1)
                progressBar.fillRoundedRect(
                    barX,
                    barY,
                    barWidth * value,
                    barHeight,
                    15
                )
            }

            progressText.setText(`Loading: ${Math.floor(value * 100)}%`)
        })
    }

    create() {
        this.cameras.main.setBackgroundColor(0xffffff)
        this.time.delayedCall(1000, () => {
            this.scene.start("MainScene")
        })
    }

    update() { }
}
```

## Spine Object
Everything is done, now we can load our character in Phaser JS by using .spine function.

```js
import Phaser from "phaser"
import Ground from "../Components/Ground"
import { BACKGROUND_COLOR, GROUND_COLOR, TILE_SIZE } from "../utils"
import { SkinsAndAnimationBoundsProvider } from "@esotericsoftware/spine-phaser"

export default class GameScene extends Phaser.Scene {
    private grounds: Ground[]

    constructor() {
        super({ key: "GameScene" })

        this.grounds = []
    }

    preload() {
    }

    create() {
        this.createBg()
        this.createTiles()
        this.createGrounds()

        // Create Spine Object
        const spineObject = this.add.spine(400, 500, "stickman", "stickman-atlas");
        spineObject.skeleton.setSkinByName("color-presets/blue-shadow");
        spineObject.setScale(0.15)
        
        // Play Animation
        spineObject.animationState.setAnimation(0, "1_/idle", true);
    }

    createBg() {
        this.cameras.main.setBackgroundColor(BACKGROUND_COLOR)
        this.add.tileSprite(
            Phaser.Math.Between(-300, -50),
            this.game.config.height as number + 20,
            2 * (this.game.config.width as number),
            this.game.config.height as number - 200,
            "buildings"
        ).setOrigin(0, 1).setScale(0.75)
    }

    createTiles() {
        let tileScale = 1 - ((150 - TILE_SIZE) / 150)
        tileScale = tileScale > 1 ? 1 : tileScale
        this.add.tileSprite(0, 0, this.game.config.width as number, TILE_SIZE, "tile").setTileScale(tileScale, tileScale).setOrigin(0, 0).setAlpha(0.75)
        this.add.tileSprite(0, this.game.config.height as number - TILE_SIZE, this.game.config.width as number, TILE_SIZE, "tile").setTileScale(tileScale, tileScale).setOrigin(0, 0).setAlpha(0.75)
        this.add.tileSprite(0, TILE_SIZE, TILE_SIZE, this.game.config.height as number - 2 * TILE_SIZE, "tile").setTileScale(tileScale, tileScale).setOrigin(0, 0).setAlpha(0.75)
        this.add.tileSprite(this.game.config.width as number - TILE_SIZE, TILE_SIZE, TILE_SIZE, this.game.config.height as number - 2 * TILE_SIZE, "tile").setTileScale(tileScale, tileScale).setOrigin(0, 0).setAlpha(0.75)
        this.physics.world.setBounds(TILE_SIZE, TILE_SIZE, this.game.config.width as number - TILE_SIZE, this.game.config.height as number - TILE_SIZE)
    }

    createGrounds() {
        let groundX = TILE_SIZE + Phaser.Math.Between(10, 20)
        let groundY = this.game.config.height as number - TILE_SIZE
        let lastGroundHeight = -1
        let canCreateGround = true
        while (canCreateGround) {
            let groundWidth = Phaser.Math.Between(80, 180)
            let groundHeight = Phaser.Math.Between(100, 550)
            if (lastGroundHeight !== -1) {
                while (Math.abs(lastGroundHeight - groundHeight) < 150) {
                    groundHeight = Phaser.Math.Between(100, 550)
                }
            }
            lastGroundHeight = groundHeight

            if (groundWidth + groundX > (this.game.config.width as number - TILE_SIZE)) {
                groundWidth = (this.game.config.width as number - TILE_SIZE) - groundX - Phaser.Math.Between(5, 15)
                canCreateGround = false
            }

            let ground = new Ground(this, groundX, groundY, groundWidth, groundHeight, GROUND_COLOR)
            this.grounds.push(ground)

            groundX += groundWidth + Phaser.Math.Between(-30, 10)

            if (groundX > (this.game.config.width as number - TILE_SIZE - groundWidth)) {
                canCreateGround = false
            }
        }
    }

    update() {
    }
}
```

Enjoy it now!!!
<Image
  src="/images/blogs/phaser-js-and-spine-2d-example.avif"
  width="1200"
  height="780"
  alt="Phaser js and Spine 2D"
  sizes="100vw"
/>

## Conclusion
The combination of Spine 2D and Phaser JS is a game-changer for 2D game developers. With Spine 2D’s powerful animation capabilities and Phaser’s performance and ease of use, you can create games that are not only visually stunning but also efficient and cross-platform compatible.

> Follow and Support me on [Medium](https://medium.com/@tajammalmaqbool11) and [Patreon](https://www.patreon.com/TajammalMaqbool). Clap and Comment on Medium Posts if you find this helpful for you. Thanks for reading it!!!

---

## Related Articles

- [Godot Array - A Comprehensive Guide (GDScript 4.x)](https://tajammalmaqbool.com/pages/blogs/godot-array-a-comprehensive-guide.md)
- [Godot Dictionary - A Comprehensive Guide (GDScript 4.x)](https://tajammalmaqbool.com/pages/blogs/godot-dictionary-a-comprehensive-guide.md)
- [Godot Enum - A Comprehensive Guide (GDScript 4.x)](https://tajammalmaqbool.com/pages/blogs/godot-enum-a-comprehensive-guide.md)
- [Godot vs Unity - A Comprehensive 2025 Guide](https://tajammalmaqbool.com/pages/blogs/godot-vs-unity-a-comprehensive-comparison.md)
- [How to make Snake Game in JavaScript](https://tajammalmaqbool.com/pages/blogs/how-to-make-snake-game-in-javascript.md)

## Sitemap

See the full [sitemap](https://tajammalmaqbool.com/sitemap.md) for all pages.
