Tilesheets.js

A small library providing helpers to handle tile sheets.

Tilesheets.js helps you handling tile sheets with JavaScript.
A tile sheet is a big image containing multiple smaller ones. In next demos, we'll use these tile sheets:

Spring Summer Fall Winter
Tiles extracted from The Spriters Resource and VGMaps, from The Legend of Zelda: Oracle of Seasons

Tilesheet

Basic usage

Tilesheet class is the main concept of Tilesheet.js. It needs a path to the image and sizes information:

import { Tilesheet } from 'tilesheets';

const sheetSpring = new Tilesheet('./images/spring.png');
sheetSpring
    .setTileSize(16) // Tiles on the sheet are 16px*16px
    .setMargin(1);   // There is a 1px gap between each tile

A tilesheet must wait for its image to be loaded. To do that, simply wait for promise waitForLoading:

await sheetSpring.waitForLoading();
// It's safe to use the sheet now!

When loaded, the sheet can be used! Three methods are provided according to your needs:

  1. getTileRect: return rectangle representation of the given tile (a JSON object with properties width, height, x and y);
  2. getTileStyle: use above method to return CSS style to perform on a DOM element to display given tile;
  3. getTileDomElement: use above methods to return a span element displaying given tile.

These methods just need the index of the tile to get. Index start from 0, from the top left tile:

For example, if we want to render a bush:

await sheetSpring.waitForLoading();
const bushTile = sheetSpring.getTileDomElement(5);
document.body.appendChild(bushTile);
(This has been resized with CSS.)

Speaking about resizing: in order to keep the pixelated render of tiles, you can use the following CSS rules (thanks StackOverflow!):

body {
    image-rendering: optimizeSpeed;             /* STOP SMOOTHING, GIVE ME SPEED  */
    image-rendering: -moz-crisp-edges;          /* Firefox                        */
    image-rendering: -o-crisp-edges;            /* Opera                          */
    image-rendering: -webkit-optimize-contrast; /* Chrome (and eventually Safari) */
    image-rendering: pixelated;                 /* Chrome                         */
    image-rendering: optimize-contrast;         /* CSS3 Proposed                  */
    -ms-interpolation-mode: nearest-neighbor; 
}

Animation

An animation is the combination of an array of tile indexes and a speed, in milliseconds. An optional name can be provided:

const waterAnimation = {
    tiles: [38, 39, 40, 41],
    speed: 300,
    name: 'water',
};

const flowerAnimation = {
    tiles: [1, 12, 19, 20],
    speed: 300,
    name: 'flower',
};

sheetSpring.setAnimations([waterAnimation, flowerAnimation]);

Animations can be used only within a canvas element, via the classes Sprite and Scene.

Sprite

Basic usage

A sprite is an object manipulating tiles, displaying them one by one in a canvas element.

import { Sprite, Tilesheet } from 'tilesheets';

const sheetSpring = new Tilesheet('./images/spring.png');
sheetSpring.setTileSize(16).setMargin(1);

const myCanvas = document.getElementById('my-canvas');

// Be sure that the sheet is loaded
springSheet.waitForLoading().then(() => {
    // Create a new sprite from a canvas
    const mySprite = new Sprite(myCanvas);

    mySprite
        .useTilesheet(sheetSpring)  // Tell the sprite to use the spring sheet
        .setCurrentTile(5)          // Set current tile to 5 (the bush one)
        .render();                  // And render it!
});
(This has been resized with CSS.)
The same bush than before, but this time in a canvas!

Of course, we can specify another tilesheet for the same sprite, we just have to call useTilesheet with the Tilesheet object we want to use, for example the one from fall:

(This has been resized with CSS.)
Using another tilesheet is a simple and quick way to switch graphics.

Animation

Interesting thing about sprites is that they can use their tilesheet animations, by their name:

mySprite
    .playAnimation('flower')
    .render();
(This has been resized with CSS. You should know that now, I won't repeat anymore!)
Yeah, dancing flowers!

It's of course possible to switch animation of the sprite on the fly, or simply stop it:

function switchAnimation(newAnimation) {
    const possibleAnimations = ['water', 'flower'];

    if (possibleAnimations.indexOf(newAnimation) >= 0) {
        mySprite.playAnimation(newAnimation).render();
    } else {
        mySprite.stopAnimation();
    }
}

//    ...
//    Rest of the code where we attach `switchAnimation` on buttons click handlers.
//    Check GitHub repository for full code!
//    ...
Select animation:

Even if the examples show some flowers and water, sprites are great to animate objects like characters, vehicles or monsters.

If we want to display and animate decors, it would be better to use a scene.

Scene

Basic usage

A scene is composed of multiple arrays of tiles. Each of these tiles is rendered into a canvas element, by using a specific Tilesheet object.

import { Scene, Tilesheet } from 'tilesheets';

const sheetSpring = new Tilesheet('./images/spring.png');
sheetSpring.setTileSize(16).setMargin(1);

const myCanvas = document.getElementById('my-canvas');

// Be sure that the sheet is loaded
springSheet.waitForLoading().then(() => {
    // Define wich tiles to render, by their index
    const myScene = new Scene([
        [21, 22, 23, 24,  9],
        [28, 29, 30, 31,  9],
        [22,  5,  3,  6, 10],
        [29,  5, 17, 17, 18],
    ], myCanvas);

    myScene
        .useTilesheet(sheetSpring)  // Tell the scene to use the spring sheet
        .render();                  // And render it!
});
A nice forest during the spring...

Like sprites, we can define another tilesheet for the same scene, again thanks to the method useTilesheet. What if winter is coming?

It's suddenly coldly!

Animations

When using sprites, we have to manually tell which animation to play, via its name. It's way simpler with scenes!

If a scene uses a tile defined in an animation, this tile will be automatically animated:

const tiles = [
    [ 1,  1,  1,  1,  1],
    [17, 17, 17, 17, 17],
    [36, 36, 36, 36, 36],
    [38, 38, 38, 38, 38],
];

const animatedSceneCanvas = document.getElementById('my-canvas');
const myAnimatedScene = new Scene(tiles, animatedSceneCanvas);

myAnimatedScene
    .useTilesheet(sheetSpring)
    .render();
Yeah, animated flowers above animated water!

Advanced usage

By combining methods resetTiles(), useTilesheet() and render(), we can create dynamic scenes by switching the tilesheet to use on the fly:

const tiles = [
    [28, 29, 28, 29, 21, 22, 28, 29, 21, 22, 21, 22, 28, 29,  0,  0, 11, 42, 47, 47],
    [ 8, 25, 26, 27, 28, 29,  5,  5, 28, 29, 28, 29,  5,  5,  5,  6,  5, 42, 47, 47],
    [ 4, 32, 33, 34, 21, 22,  8,  8,  2,  3,  4, 21, 22, 17, 13,  5,  5, 42, 47, 47],
    [17, 14, 14, 14, 28, 29,  8,  8,  9, 10,  7, 28, 29, 36, 36, 36, 36, 51, 47, 47],
    [21, 22, 23, 24, 13, 13,  3,  3, 10, 10,  0, 11, 42, 38, 38, 38, 47, 47, 47, 47],
    [28, 29, 30, 31, 13, 21, 22,  6,  1, 10,  0, 11, 42, 38, 38, 38, 47, 47, 47, 47],
    [22,  2,  3,  3,  7, 28, 29,  6,  1,  7,  7, 18, 42, 45, 46, 45, 35, 50, 50, 50],
    [29, 16,  7,  7,  0,  1,  1, 21, 22, 21, 22,  8, 42, 45, 45, 46, 44,  8,  8,  8],
    [21, 22,  9,  0,  0,  0,  7, 28, 29, 28, 29, 21, 22, 50, 50, 50,  8,  2,  3,  3],
];

function switchSeason(season) {
    const seasonToTilesheet = {
        spring: sheetSpring,
        summer: sheetSummer,
        fall: sheetFall,
        winter: sheetWinter,
    };

    scene
        .resetTiles()   // Reset back scene tiles to the initial ones (useful for animated tiles)
        .useTilesheet(seasonToTilesheet[season]) // Use new tilesheet
        .render();                               // Re-render!
}

//    ...
//    Rest of the code where we attach `switchSeason` on buttons click handlers.
//    Check GitHub repository for full code!
//    ...
Select season:

Paletted sprites

Using palettes

By default, sprites render directly the image of the tilesheet they're using, without any changes on it. It's possible to use other palettes than the default one, in order to dynamically switch colors of the sprites.

For this section, we'll use this tilesheet containing two tiles of Yoshi:

Yoshi
Tiles extracted from The Spriters Resource from Super Mario Advance 3: Yoshi's Island

As we've seen before, it's quite easy to render these tiles via an animated sprite:

yoshiSprite
    .useTilesheet(sheetYoshi)   // Use the main sheet with Yoshi tiles
    .playAnimation('hourray');  // Play the hourray animation


Hello Yoshi!

As you may know, there are a lot of different Yoshi colors... We don't really want to make our tilesheet bigger by adding new tiles on it, so how can we display a yellow Yoshi?
By using palettes!

First we have to define the reference palette of the Yoshi tilesheet. It's simply another tilesheet loading an image which contains only colors. For our example, the image of the palette is this one:

Green Yoshi palette
As you can see, it's a really small picture containing only the colors of our sprite.

This image will be loaded as a tilesheet:

const sheetGreenYoshiPalette = new Tilesheet('./images/green_yoshi_palette.png');
sheetGreenYoshiPalette.setTileSize(5).setMargin(0);

Then, we can tell the tilesheet with Yoshi to use the palette of this new tilesheet as a reference:

sheetYoshi.setReferencePalette(sheetGreenYoshiPalette.getReferencePalette());

This operation allows Tilesheets.js to know how to handle and link colors. When done, we can create new palettes and use them!

Let's say we have this palette image:

Yellow Yoshi palette
An other small image containing another colors palette for Yoshi.

We can define this image as a palette, like we've done before for the reference one:

const sheetYellowYoshiPalette = new Tilesheet('./images/yellow_yoshi_palette.png');
sheetYellowYoshiPalette.setTileSize(5).setMargin(0);

const yellowYoshiPalette = sheetYellowYoshiPalette.getReferencePalette();

Now that the palette is defined, we can tell the sprite to use it:

yoshiSprite
    .useTilesheet(sheetYoshi)       // Use the main sheet with Yoshi tiles
    .usePalette(yellowYoshiPalette) // Use the yellow palette we've just defined
    .playAnimation('hourray');      // Play the hourray animation


Hello Yellow Yoshi!

Palettes are a powerful tool to color-swap sprites without having to draw them enterly in a PNG file. Simply define a new palette file and you're done!

It's of course possible to switch palettes on the fly, by calling the method usePalette() when we want to, as shown in the below example:



Select palette:

(It also updates the favicon of this page, check the tab!)