---
title: "How to create Sprite Sheets and Animations for Kivy"
description: "Learn how to create optimized sprite sheets using TexturePacker for Kivy applications, with step-by-step examples for animated characters"
canonical: "https://www.codeandweb.com/texturepacker/tutorials/how-to-create-sprite-sheets-and-animations-with-kivy"
date: "2025-06-26T00:00:00.000Z"
modified: "2025-06-26T00:00:00.000Z"
source: "html"
---

# How to create Sprite Sheets and Animations for Kivy

Andreas Löw

[texturepacker](https://www.codeandweb.com/texturepacker/tutorials.md),  [tutorial](https://www.codeandweb.com/tags/tutorial),  [kivy](https://www.codeandweb.com/tutorials/kivy.md),  [python](https://www.codeandweb.com/tags/python),  [sprite-sheets](https://www.codeandweb.com/tags/sprite-sheets),  [animation](https://www.codeandweb.com/tags/animation)

[GitHub](https://github.com/CodeAndWeb/kivy-sprite-sheet-example)

![How to create Sprite Sheets and Animations for Kivy](https://www.codeandweb.com/static/159257bb7cfba0c346ff89776212bc74/a6312/_opengraph-image.png)

## What you are going to learn

In this tutorial, you'll learn how to:

*   **Create optimized sprite sheets** for your Kivy app or game
*   Implement a **sprite animation**
*   Optimize **memory usage** and **rendering performance**

## Prerequisites

Before starting this tutorial, you should have:

*   Python 3.8+ installed
*   Basic familiarity with Python

## Step 1: Create a new Kivy project

Let's start by creating a new folder for the project and initialise a new python environment inside:

```bash
mkdir kivy-sprite-sheet-example
cd kivy-sprite-sheet-example

python -m venv venv
```

On windows activate with

```
call venv\Scripts\activate.bat
```

on linux / macos

```
source venv/bin/activate
```

install kivy

```
pip install kivy
```

## Step 2: Download the assets

Download the assets for this project from GitHub and extract them into the projects' folder.

[assets-raw.zip](https://github.com/CodeAndWeb/kivy-sprite-sheet-example/raw/refs/heads/main/assets-raw.zip)

The files consist of a walking animation for a character and a background image.

In addition to that, it also contains a `cityscene.tps` which is a [TexturePacker](https://www.codeandweb.com/texturepacker.md) project file we'll use to create the sprite sheet.

```
assets-raw
├── cityscene
│   ├── capguy
│   │    └── walk
│   │        ├── 0001.png
│   │        ├── 0002.png
│   │        ├── 0003.png
│   │        ├── ...
│   └── background.png
├── cityscene.tps
```

This folder is called **assets-raw** because we consider this the input assets that are not yet processed or optimized for the demo.

Note: We use **Zero-padded numbers** - e.g. `0001` instead of `1` which helps us to easily build animations in Kivy later.

While here, also create a new folder to store the optimized assets in:

```
mkdir assets-optimized
```

## Step 3: Prepare the Graphics for the Demo

Using individual sprites versus sprite sheets has significant performance implications:

| Aspect | Individual Sprites | Sprite Sheets |
| --- | --- | --- |
| **Loading** | 👎 Slow because each file is loaded separately | 👍 Single file loads much faster. |
| **File Size** | 👎 Larger sizes (unoptimized) | 👍 20-40% smaller with optimization (e.g. png compression) |
| **Rendering** | 👎 Many draw calls = lower FPS | 👍 Batched rendering = higher FPS |
| **Management** | 👎 Complex asset organization | 👍 One file replaces dozens |

The solution is to pack your graphics into a **sprite sheet** (texture atlas) using a tool like TexturePacker.

### Why TexturePacker?

TexturePacker creates optimized sprite sheets with automatic packing algorithms, rotation, and trimming. Key benefits include:

*   **Perpetual licensing**: Pay once, use forever—no subscriptions. Generous discounts for indies.
*   **Regular updates** with new features and bug fixes
*   **Free support** included with every license
*   **Platform flexibility**: Works with virtually every game engine, giving you complete freedom
*   **Long-term commitment**: We're not a hobby project that gets abandoned — TexturePacker has been actively developed since 2010, and we're here to stay

You can follow along with the free trial version included in this tutorial.

[Download TexturePacker 9.0.0](https://www.codeandweb.com/texturepacker/download/9.0.0/win)

for Windows

[Also available for macOS and Linux](https://www.codeandweb.com/texturepacker/download.md)

### Packing the Sprite Sheet

**Step 1: Launch TexturePacker** Open the TexturePacker application.

**Step 2: Add Your Sprites** Drag and drop the `assets-raw/cityscene` folder onto the TexturePacker window. TexturePacker automatically detects and adds all sprites in that folder to the sheet.

**Step 3: Configure Settings**

*   **Texture Format**: Select **PNG-8** to reduce file size by up to 75%.
*   **Framework**: Click the field next to _Framework_ and choose **Kivy** from the list.
*   **Atlas File**: Click the folder icon next to the _Atlas File_ field, navigate to your `assets-optimized` folder, and name the file `cityscene.atlas`.
*   **Other Settings**: Leave all other settings at their default values.

**Step 4: Save Your Project** Save the configuration as a `.tps` project file (e.g., in the `assets-raw` folder). This makes it easy to update the sprite sheet later — just add new sprites to the `cityscene` folder and re-publish.

**Step 5: Generate the Sprite Sheet** Click **Publish** to generate the optimized sprite sheet and atlas file.

  ![Using TexturePacker with Kivy](https://www.codeandweb.com/static/d0db1ad3be562431b957ee155d450439/283d3/using-texturepacker-with-kivy%402x.png)

Using TexturePacker with Kivy

TexturePacker offers many additional options to fine-tune optimization — but for now, the default settings are a great starting point. One feature you may find useful soon is **Multipack**: when enabled, TexturePacker automatically splits your sprites across multiple sheets if a single one isn’t large enough. This allows you to keep adding assets without worrying about hitting texture size limits.

Additionally, TexturePacker includes a powerful **command-line interface** that allows you to update sprite sheets automatically — perfect for integration with build scripts.

## Step 4: Displaying a static background

Now let’s get into the fun part — coding! 🎉 That’s what you’re here for, right?

Create a `src` folder to hold your game source code and create a `main.py` in it.

```python
import kivy
kivy.require('2.3.0')

from kivy.atlas import Atlas
from kivy.app import App
from kivy.clock import Clock
from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image

class CitySceneApp(App):
    def build(self) -> FloatLayout:
        layout = FloatLayout()

        # Add background image
        background = Image(
            source='atlas://assets-optimized/cityscene/background',
            fit_mode="fill"
        )
        layout.add_widget(background)

        return layout

if __name__ == '__main__':
    CitySceneApp().run()
```

The `CitySceneApp` class extends Kivy’s `App` and overrides the `build()` method to construct the user interface. It uses a `FloatLayout`, which allows widgets to be freely positioned using relative coordinates.

An `Image` widget is added to the layout to display the background. The `source` is set using the Kivy atlas syntax: `atlas://assets-optimized/cityscene/background`, which tells Kivy to load the `background` sprite from the `cityscene.atlas` in the `assets-optimized` folder.

The image is configured to scale to fill the window `fit_mode="fill"`

Finally, `CitySceneApp().run()` starts the app and opens the window with the rendered background. This provides a solid visual foundation for building more complex animated scenes.

Start it using

```bash
python src/main.py
```

And you should see

  ![Kivy game scene with static background](https://www.codeandweb.com/static/906a2886a9c8edfd897aebe07c2cae35/283d3/kivy-game-scene-with-static-background%402x.png)

Kivy game scene with static background

## Step 6: Creating an animated character

Let's now create an animated character that walks from left to right. For this, we use the `Image()` class of Kivy combined with an `Animation()`.

### Animation helper function

This short function grabs all animation frames from an atlas and returns them in order:

```python
def frames_for_animation(atlas_path, prefix):
    """Return a list of frame sources from a Kivy atlas for animation."""
    atlas = Atlas(f"{atlas_path}.atlas")
    keys = sorted(k for k in atlas.textures if k.startswith(prefix))
    return [f"atlas://{atlas_path}/{k}" for k in keys]
```

You can use this function this way

```python
frames_for_animation("assets-optimized/cityscene", "capguy_walk_")
```

The first parameter is the atlas to use, the second the prefix of the animation. Note that texturepacker converts the frame names by concatenating folders with `_` and omitting the file name extension. E.g. `capguy/walk/0001.png` becomes `capguy_walk_0001`.

### The animated character class

`AnimatedCharacter` is the sprite that walks across the scene. Under the hood it's just a `kivy.uix.image.Image`, but we give it a few extras. It loads the sprite frames from the function we created before and uses a timer to update the animation and move.

```python
# Animation constants
FRAME_RATE = 6.0           # frames per second
PIXELS_PER_FRAME = 24.0    # pixels per second

class AnimatedCharacter(Image):
    """Image widget that cycles through frames to animate a character."""

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.frames = frames_for_animation(
            "assets-optimized/cityscene", "capguy_walk_"
        )
        # start position, initial frame
        self.x = -self.width
        self.y = 240
        self.frame_index = 0
        self.size_hint = (None, None)
        self.source = self.frames[self.frame_index]
        self.bind(texture=self._on_texture)
        # schedule updates
        Clock.schedule_interval(self._update, 1.0 / FRAME_RATE)

    def _update(self, dt: float) -> None:
        """Advance frame and position each tick."""
        self.frame_index = (self.frame_index + 1) % len(self.frames)
        self.source = self.frames[self.frame_index]
        self.x += PIXELS_PER_FRAME
        if self.x >= Window.width + 2 * self.width:
            self.x = -self.width

    def _on_texture(self, instance, texture) -> None:
        """Set image size when texture loads."""
        self.size = texture.size
```

A quick thought about the type of animation we are using:

**A walking animation**

When the animation frame changes, we also adjust the sprite’s position. If we did not synchronize both actions, the legs would not move between the frames, but the character would appear to slide — or “moon‑walk” — across the floor between frames.

**What about other kinds of objects?**

For assets whose movement is independent of their animation — think of a starship that glides smoothly while its thrusters flicker — you would set `FRAME_RATE` to 60 fps to make the movement smooth and update the frames at a lower rate.

### Adding the animation to the Scene:

The final step is to add the `AnimatedCharacter()` to the scene:

```python
class CitySceneApp(App):
    """Kivy application to display the animated city scene."""

    def build(self):
        layout = FloatLayout()
        background = Image(
            source="atlas://assets-optimized/cityscene/background",
            fit_mode="fill",
        )
        layout.add_widget(background)
        # highlight-start
        layout.add_widget(AnimatedCharacter())
        # highlight-end
        return layout
```

## Step 6: Performance & Memory Optimization

To further improve your app’s performance and reduce memory usage, consider the following techniques. We won’t go into detail here to keep the tutorial concise, but these tips should give you a good starting point for further optimization.

### Adapting to screen resolutions

Optimize your atlas dimensions according to the capabilities of your target devices.

In the **Advanced Settings** in TexturePacker, you’ll find an option called **Scaling Variants**. This feature allows you to generate multiple versions of your sprite sheet at different resolutions.

At runtime, you can select the most suitable variant based on the screen or window size — using high-resolution assets for large displays, and smaller, optimized textures for lower-resolution screens.

This approach improves performance by avoiding the overhead of rendering large textures on small screens, and ensures sharp, crisp visuals on high-resolution displays.

### Colors and image formats

The **PNG-8** format we used in our TexturePacker settings reduces the total number of colors in the sprite sheet to 256 and applies dithering. In most cases, this delivers good visual quality while dramatically reducing the file size of the atlas.

However, it does **not** reduce the amount of RAM required to render the sheet!

If you notice artifacts on your sprites or require more colors, consider switching to **PNG-32**.

Another option is to group sprites with the same color on a single sheet - e.g. everything that is blue on one, red on another.

### Multipack

If your game uses many sprites, the default texture size of **2048×2048** may not be sufficient. While you can increase the texture size, some hardware may impose limitations.

**TexturePacker** can automatically split your sprites across multiple sprite sheets — this feature is called **multipack**. You still get a single atlas file, but it references multiple atlas images. Your application will not notice any difference.

You can also choose to deliberately split sprites across sheets. For example, if a character appears in multiple scenes, you can place it on a separate sheet to enable reuse.

## Structured data

```json
{
  "@context": "https://schema.org",
  "@type": "TechArticle",
  "headline": "How to create Sprite Sheets and Animations for Kivy",
  "author": [
    {
      "@type": "Person",
      "name": "Andreas Löw"
    }
  ],
  "dateModified": "2025-06-26T00:00:00.000Z",
  "image": "/static/159257bb7cfba0c346ff89776212bc74/a6312/_opengraph-image.png"
}
```
