How to use sprite sheets with GameMaker

In this tutorial you are going to learn:
- How to pack sprite strips and grids with TexturePacker, and import them into GameMaker
- How to pack your sprites tightly, so they need less memory
- How to load a packed sheet as a GameMaker texture group, where the sprites become regular sprite assets
- How to load a packed sheet with a loader script, for HTML5 and older GameMaker versions
The complete tutorial project is available on GitHub.
Which method should I use?
TexturePacker offers four ways to get your sprites into GameMaker. They differ in what you end up with on the GameMaker side — animation frames of a single sprite, or a set of independent sprite assets — and in which GameMaker versions and targets support them.
| Method | GameMaker | Sprites become | Notes |
|---|---|---|---|
| Sprite strip / grid | any version | frames of one sprite | a single animation of equally sized sprite frames |
| Texture group format | 2024.14 or newer | regular sprite assets | the recommended route; not on HTML5 / GX.games |
| Loader script format | any version | drawn by a script | works everywhere; sprites can be rotated on the sprite sheet for a better pack rate |
If you are on GameMaker 2024.14 or newer and don't build for HTML5, use the texture group format: it packs your sprites tightly and gives you normal sprite assets to work with. Use the loader script format when you need HTML5, an older GameMaker, or the tighter packing that rotated sprites allow.
Install TexturePacker
With TexturePacker you can easily create sprite sheets for GameMaker. Download TexturePacker, it is available for macOS, Windows and Linux. You can test it within a 7-day trial period before purchasing a license:
Create a sprite strip
Start TexturePacker and drop the sprites that should be packed onto TexturePacker's main window. You can also drop folders, TexturePacker will automatically scan them for image files and add them to the sheet.
Make sure that no Framework is selected ("Choose Data Format" button must be displayed). Otherwise, click on the button and choose "Sprite sheet only".

- After dropping your animation sprites on TexturePacker, in the center view a preview of the packed sprite sheet is displayed.
- Use the folder icon to open a file selector and set the file name for the packed Texture file.
- Select algorithm Grid / Strip. TexturePacker will pack the sprites in alphabetical order from left to right, until the maximum texture size is reached.
- Export sprite sheet will save the sprite sheet texture on disk.
Please make sure that the texture file name has a _stripN suffix. The number N tells GameMaker the
number of frames in which the strip should be split during import.
Import sprite strip in GameMaker
In GameMaker simply drop the sprite sheet on the Sprites folder of the Asset Browser:

GameMaker automatically imports the sprite sheet and opens the sprite editor. At the bottom of the sprite editor window you can see the individual sprite frames in which the sheet has been split, according to the file name suffix.
If you've changed the sprite sheet, use the Import button to re-import the new sprite sheet.
Pack sprites with grid layout
In TexturePacker's Grid / Strip mode, sprites are packed as a strip from left to right until the maximum texture width is reached. Then a new line of sprites is started, and the sprites are placed on a grid:

Of course, you can increase the Max size of the texture to get all sprites packed as a strip. But in this section we will learn how to import a sprite sheet with grid layout.
First, don't add a _stripN suffix to the sprite sheet's file name, because we don't want GameMaker
to split the sheet automatically.
Import sprite sheet with grid layout in GameMaker
After importing the sprite sheet in GameMaker, click on Edit Image in the sprite editor. Then select Convert to Frames from the Image menu:

To split the sheet in sprite frames you have to enter...
- the total number of sprite frames
- the sprites per row
- the size of a sprite frame
On the right side of the window a preview of the sprite frames is displayed.
With the Horizontal / Vertical Cell Offset the top-left corner of the grid can be set.
To extract the "betty-run-left" sprites in our example, we skip the first column and row by setting
the cell offsets to 1 and reduce the number of frames to 5:

Drawbacks
... of the strip/grid layout as described above:
-
Equal sized frames:
All cells of a strip or grid have the same size. If your sprites differ in size, a lot of space is wasted. -
Only a convenient solution for animations:
All sprites of a sprite sheet are grouped in one GameMaker Sprite as animation frames. But individual sprites of the sheet cannot be accessed independently, and they cannot be referenced by name. -
Sprite sheets are re-packed:
When a sprite strip sheet is imported and split by GameMaker, the individual sprite images are saved in your project directory. The sheet image is no longer needed after importing it. During the build, the sprites are packed on internal sprite sheets, which are squared and have a "power-of-two" size (e.g. 2048x2048). This size constraint often wastes memory due to unused texture space. -
Texture embedding:
For some platforms, GameMaker embeds the internally generated sprite sheets in the executable. For large games, the executable will get very big. If memory, address space, or loader performance is limited, this might be a problem.
Create a tightly packed sprite sheet
With TexturePacker you can pack your sprites more tightly than using a grid layout. The sprite name and the position on the sprite sheet are written to a JSON data file.
TexturePacker offers two data formats for GameMaker. They differ in how you load the sheet at runtime:
- Texture group format — GameMaker builds a native texture group from the sheet and you access the sprites as regular Sprite assets. Needs GameMaker 2024.14 or newer.
- Loader script format — a small helper script draws the sprites straight from the sheet. Works with every GameMaker version and on HTML5.
TexturePacker provides a large number of packer options to reduce the size of the resulting sprite sheets, whichever format you choose:
- Sprite sheet size need not be squared or power-of-two.
- Texture image can be saved with reduced color space to optimize file size.
Here's a comparison of the sprite sheets GameMaker would generate internally (when using individual sprites or sprite strip/grids) vs packed with TexturePacker:
packed with GameMaker
2048 x 2048, RGBA8888 PNG
2472 kB (on disk)

packed with TexturePacker
2002 x 1692, 8-bit indexed PNG
196 kB (on disk)

Texture group format
GameMaker's texturegroup_add() builds a texture group from an external sprite sheet at runtime — in
GameMaker 2024.14 or newer, and not on HTML5 or GX.games. The sprites become regular sprite assets:
GameMaker animates them for you, and you can use image_index and draw_self() as with any other
sprite. No helper script is required.
- Select GameMaker (texturegroup) as Framework
- Specify the file name for the Data file. By default, the Texture file will be saved in the same directory.
- Choose MaxRects as Algorithm. Rotation is not available for this format — the GameMaker texture group format cannot store rotated sprites, so TexturePacker does not offer a rotation option here.

Animations are detected automatically, so walk/0001.png … walk/0016.png become a single 16-frame
sprite instead of 16 separate ones. You can turn that off with Auto-detect animations in the
Advanced settings, and set the playback speed with Frame speed (fps) — it defaults to 30, the demo
project uses 12.
Publish into the datafiles folder
Publish the data file and the texture into the datafiles folder of your GameMaker project — any
subfolder is fine. datafiles is GameMaker's fixed Included Files root, and its content becomes the
working directory at runtime, so datafiles/spritesheets/cityscene-texturegroup.json is loaded as
"spritesheets/cityscene-texturegroup.json".
GameMaker lists both files in the Included Files panel:

Load the sprite sheet
Loading takes four lines, and no script needs to be added to your project. Put them in the Create
event of a controller object — oScene in our example — and create the objects that use the sprites
right after, so the sheet is loaded before anything looks a sprite up:
var _buf = buffer_load("spritesheets/cityscene-texturegroup.json");
var _json = json_parse(buffer_read(_buf, buffer_text));
buffer_delete(_buf);
texturegroup_add("cityscene", _json.textures, { sprites: _json.sprites });
instance_create_layer(1400, 850, "Player", oPlayerTextureGroup);Read the buffer with buffer_text — the data file is plain text without a NUL terminator.
TexturePacker reduces the file names to valid identifiers: capguy/walk/0001.png becomes capguy_walk
when it is part of a detected animation, or capguy_walk_0001 when it is a single image. Assign such a
sprite in the object that uses it — oPlayerTextureGroup in our example — and GameMaker plays the
animation for you, with no Draw event and no manual frame counting:
sprite_index = asset_get_index("capguy_turn");Release the texture group when it is no longer needed, for example in the cleanup event.
texturegroup_add() raises a fatal error if a group with that name already exists.
texturegroup_delete("cityscene");Advantages
- The sprites are regular sprite assets —
image_indexanddraw_self()both work. - GameMaker plays the animations; you don't implement frame timing yourself.
- No loader script in your project.
- Trimming and 9-patch borders (which become nine-slice data) are carried over from TexturePacker automatically. Pivot points carry over too, and become the sprite origin, but only when TexturePacker's write pivot points setting is enabled.
Drawbacks
- Requires GameMaker 2024.14 or newer (including LTS 2026), and does not work on HTML5 or GX.games.
- Sprites cannot be rotated on the sheet, so the packing is slightly less tight than with the loader script format.
- These sprites only exist at runtime, once
texturegroup_add()has run: there is no Asset Browser entry to browse or drag into a room, and the only way to reach one isasset_get_index().
Loader script format
Use this format when you build for HTML5 or GX.games, when you target a GameMaker older than 2024.14.
- Select GameMaker (loader script) as Framework
- Specify the file name for the Data file. By default, the Texture file will be saved in the same directory.
- Choose MaxRects as Algorithm.

Add the generated sprite sheet and data file into the datafiles folder, following the same folder placement described above.
To load the sprite sheet, add the following script from our GitHub example repository to your project:
The script provides two methods, the first one is used to load the JSON data file and the texture image. You can call it in the creation code of your room or scene, and store the handle in a global variable:
global.atlas_cityscene = tp_load_texture_atlas("spritesheets/cityscene.json");To draw a sprite from the atlas, use the following function:
tp_draw_sprite_from_atlas(global.atlas_cityscene, "Background.png", 0, 0);
...
tp_draw_sprite_from_atlas(global.atlas_cityscene, "capguy/walk/0001.png", x, y);The function also accepts optional scale and rotation parameters:
tp_draw_sprite_from_atlas(atlas, name, x, y, scale_x = 1, scale_y = 1, angle = 0).
If the atlas is no longer needed, it should be released to avoid memory leaks. This can be done in the cleanup event handler:
tp_release_texture_atlas(global.atlas_cityscene);For a demo application please check out this GitHub repository. It displays a scene background and an animated player character:

Advantages
- Works with every GameMaker version and on every target, including HTML5 and GX.games.
- TexturePacker can rotate sprites for improved pack ratios — the texture group format cannot do this.
Drawbacks
Loading external sprite sheets at runtime also comes with some drawbacks:
- Since the sprites of the sprite sheet are not accessible as Sprite objects in GameMaker, they cannot be added to a layer via drag-and-drop.
- The sprite sheet loader cannot add multiple animation frames to one sprite object, that's why animations must be implemented manually. However, as demonstrated in our GitHub example, this is quite simple.