DEV Community

Cover image for TCJSgame Tutorial #2: Drawing Images, Sprites, and Animations
Kehinde Owolabi
Kehinde Owolabi

Posted on

TCJSgame Tutorial #2: Drawing Images, Sprites, and Animations

TCJSgame Tutorial #2: Drawing Images, Sprites, and Animations

Welcome to the second tutorial in the TCJSgame series. In the first tutorial, you learned how to set up the engine and move a green square with arrow keys. Now it is time to bring your game to life with images, sprites, and smooth animations.

In this tutorial, you will learn:

  • How to load and display images in your game
  • How to create animated sprites with multiple frames
  • How to control animations based on movement direction
  • How to manage sprite sheets for efficient game art

By the end, you will have a character that walks, runs, and stops with smooth frame-by-frame animation.

Game character sprite animation


What Are Sprites?

A sprite is a 2D image or animation that represents a game object like a player, enemy, or item. In TCJSgame, sprites are handled through the Sprite class, which manages multiple frames of animation.

There are two ways to work with images in TCJSgame:

  • Static Image – A single image that does not change (like a background or collectible coin)
  • Animated Sprite – A sprite sheet with multiple frames that cycle to create movement

Loading a Static Image

Before you can display an image, you need to create a Component with the type set to "image". The color parameter becomes the image source URL.

Create a new file called index.html in your project folder and add this code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TCJSgame Tutorial 2 - Images</title>
    <style>
        body {
            margin: 0;
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            background: #1a1a2e;
        }
        canvas {
            box-shadow: 0 10px 25px rgba(0,0,0,0.3);
            border-radius: 8px;
        }
    </style>
</head>
<body>
    <script src="tcjsgame-v3.js"></script>

    <script>
        const display = new Display();
        display.start(800, 600, document.body);

        // Create a static image component using a working image URL
        // Parameters: width, height, imageUrl, x, y, "image"
        const player = new Component(100, 100, "https://placekitten.com/100/100", 350, 250, "image");
        display.add(player);

        function update() {
            // Move with arrow keys
            if (display.keys[39]) player.x += 5;
            if (display.keys[37]) player.x -= 5;
            if (display.keys[38]) player.y -= 5;
            if (display.keys[40]) player.y += 5;

            // Keep on screen
            if (player.x < 0) player.x = 0;
            if (player.x > display.canvas.width - player.width) player.x = display.canvas.width - player.width;
            if (player.y < 0) player.y = 0;
            if (player.y > display.canvas.height - player.height) player.y = display.canvas.height - player.height;
        }

        const instruction = new Component("16px", "Arial", "white", 20, 30, "text");
        instruction.text = "Arrow keys to move the cat image";
        display.add(instruction);
    </script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Character image on canvas

Note: The image URL https://placekitten.com/100/100 is a working placeholder service that returns a real cat image. You can replace it with your own image file later.


Understanding Sprite Sheets

A sprite sheet is a single image file that contains multiple frames of animation arranged in a grid. For example, a walking animation might have 4 frames side by side in one row.

Sprite Sheet Layout:
[Frame1] [Frame2] [Frame3] [Frame4]

The TCJSgame Sprite class handles slicing this sheet into individual frames and cycling through them at a set speed.


Creating an Animated Sprite

To create an animated sprite, you need:

  1. A sprite sheet image
  2. The width and height of each frame
  3. The total number of frames
  4. The animation speed (how many game frames per sprite frame)

Since you may not have a sprite sheet yet, this example creates a simple animated square that changes color to simulate animation. This works entirely inside the code with no external images needed.

Here is the complete working example:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TCJSgame Tutorial 2 - Sprite Animation</title>
    <style>
        body {
            margin: 0;
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            background: #1a1a2e;
        }
        canvas {
            box-shadow: 0 10px 25px rgba(0,0,0,0.3);
            border-radius: 8px;
        }
    </style>
</head>
<body>
    <script src="tcjsgame-v3.js"></script>

    <script>
        const display = new Display();
        display.start(800, 600, document.body);

        // Create an animated character using a simple rectangle
        // We will manually animate it by changing color
        const player = new Component(64, 64, "#ff6b6b", 368, 268, "rect");
        display.add(player);

        // Animation frames
        const colors = ["#ff6b6b", "#4ecdc4", "#45b7d1", "#96ceb4", "#feca57", "#ff9ff3"];
        let frameIndex = 0;
        let frameCounter = 0;
        const frameSpeed = 8; // Change color every 8 game frames
        let moving = false;

        function update() {
            moving = false;

            // Movement
            if (display.keys[39]) {
                player.x += 5;
                moving = true;
            }
            if (display.keys[37]) {
                player.x -= 5;
                moving = true;
            }
            if (display.keys[38]) {
                player.y -= 5;
                moving = true;
            }
            if (display.keys[40]) {
                player.y += 5;
                moving = true;
            }

            // Animate only when moving
            if (moving) {
                frameCounter++;
                if (frameCounter >= frameSpeed) {
                    frameCounter = 0;
                    frameIndex = (frameIndex + 1) % colors.length;
                    player.color = colors[frameIndex];
                }
            } else {
                // Reset to first color when stopped
                player.color = colors[0];
                frameIndex = 0;
                frameCounter = 0;
            }

            // Keep on screen
            if (player.x < 0) player.x = 0;
            if (player.x > display.canvas.width - player.width) player.x = display.canvas.width - player.width;
            if (player.y < 0) player.y = 0;
            if (player.y > display.canvas.height - player.height) player.y = display.canvas.height - player.height;
        }

        const instruction = new Component("16px", "Arial", "white", 20, 30, "text");
        instruction.text = "Arrow keys to move - character changes color when moving";
        display.add(instruction);

        console.log("Game running with animated character!");
    </script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Animated sprite walking


Creating a Real Sprite Sheet Animation

If you have your own sprite sheet, here is how to use it. This example uses a simple canvas-generated sprite sheet so it works without external files:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TCJSgame Tutorial 2 - Real Sprite Sheet</title>
    <style>
        body {
            margin: 0;
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            background: #1a1a2e;
        }
        canvas {
            box-shadow: 0 10px 25px rgba(0,0,0,0.3);
            border-radius: 8px;
        }
    </style>
</head>
<body>
    <script src="tcjsgame-v3.js"></script>

    <script>
        const display = new Display();
        display.start(800, 600, document.body);

        // Create a sprite sheet by drawing on a canvas
        const sheetCanvas = document.createElement('canvas');
        sheetCanvas.width = 256;  // 4 frames * 64px
        sheetCanvas.height = 64;
        const ctx = sheetCanvas.getContext('2d');

        // Draw 4 different walking frames
        const frameColors = ['#ff6b6b', '#ff8c42', '#f9c74f', '#90be6d'];
        for (let i = 0; i < 4; i++) {
            // Body
            ctx.fillStyle = frameColors[i];
            ctx.fillRect(i * 64, 8, 48, 48);
            // Eyes
            ctx.fillStyle = '#2d3436';
            ctx.fillRect(i * 64 + 12, 28, 8, 8);
            ctx.fillRect(i * 64 + 32, 28, 8, 8);
            // Legs - moving position based on frame
            ctx.fillStyle = '#2d3436';
            if (i % 2 === 0) {
                ctx.fillRect(i * 64 + 12, 52, 8, 12);
                ctx.fillRect(i * 64 + 32, 52, 8, 12);
            } else {
                ctx.fillRect(i * 64 + 8, 52, 8, 12);
                ctx.fillRect(i * 64 + 36, 52, 8, 12);
            }
        }

        const spriteSheetImage = new Image();
        spriteSheetImage.src = sheetCanvas.toDataURL();

        spriteSheetImage.onload = () => {
            // Create sprite: 64x64 frames, 4 frames, update every 6 game frames
            const walkSprite = new Sprite(spriteSheetImage, 64, 64, 4, 6);

            const player = new Component(64, 64, "", 368, 268, "image");
            player.image = spriteSheetImage;
            player.sprite = walkSprite;

            // Override update to use sprite drawing
            player.update = function(ctx) {
                if (this.sprite) {
                    this.sprite.draw(ctx, this.x, this.y);
                }
            };

            display.add(player);

            let moving = false;

            window.update = function() {
                moving = false;

                if (display.keys[39]) {
                    player.x += 5;
                    moving = true;
                }
                if (display.keys[37]) {
                    player.x -= 5;
                    moving = true;
                }
                if (display.keys[38]) {
                    player.y -= 5;
                    moving = true;
                }
                if (display.keys[40]) {
                    player.y += 5;
                    moving = true;
                }

                // Update animation
                if (moving && player.sprite) {
                    player.sprite.update();
                }

                // Keep on screen
                if (player.x < 0) player.x = 0;
                if (player.x > display.canvas.width - player.width) player.x = display.canvas.width - player.width;
                if (player.y < 0) player.y = 0;
                if (player.y > display.canvas.height - player.height) player.y = display.canvas.height - player.height;
            };
        };

        const instruction = new Component("16px", "Arial", "white", 20, 30, "text");
        instruction.text = "Arrow keys to move - walking animation when moving";
        display.add(instruction);

        console.log("Game running with sprite sheet animation!");
    </script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Adding Multiple Animations

For a complete game character, you will want different animations for different actions: idle, walking, running, jumping. Here is a pattern you can use:

// Create different sprite sheets for different actions
const idleSprite = new Sprite(idleSheet, 64, 64, 2, 15);
const walkSprite = new Sprite(walkSheet, 64, 64, 4, 8);
const jumpSprite = new Sprite(jumpSheet, 64, 64, 3, 10);

let currentAnimation = idleSprite;
let isJumping = false;
let isMoving = false;

function update() {
    // Determine which animation to use
    if (isJumping) {
        if (currentAnimation !== jumpSprite) {
            currentAnimation = jumpSprite;
            player.sprite = currentAnimation;
        }
    } else if (isMoving) {
        if (currentAnimation !== walkSprite) {
            currentAnimation = walkSprite;
            player.sprite = currentAnimation;
        }
    } else {
        if (currentAnimation !== idleSprite) {
            currentAnimation = idleSprite;
            player.sprite = currentAnimation;
        }
    }

    // Update current animation
    if (player.sprite) {
        player.sprite.update();
    }
}
Enter fullscreen mode Exit fullscreen mode

Using Your Own Images

To use your own images:

  1. Save your image files in the same folder as your index.html
  2. Use relative paths like "player.png" or "sprites/walk.png"
  3. For sprite sheets, make sure the frame dimensions are consistent

Example with local files:

const playerImage = new Image();
playerImage.src = "player.png";

playerImage.onload = () => {
    const player = new Component(64, 64, "player.png", 368, 268, "image");
    display.add(player);
};
Enter fullscreen mode Exit fullscreen mode

Performance Tips for Sprites

When working with multiple sprites or large sprite sheets, keep these tips in mind:

  • Preload images before the game starts using Image.onload to avoid flickering
  • Reuse Sprite objects instead of creating new ones every frame
  • Use smaller frame sizes (32x32 or 64x64) for better performance
  • Combine sprites into one sheet to reduce HTTP requests
  • Limit frame counts – 4 to 8 frames per animation is usually enough for smooth movement

Sprite sheet optimization


Troubleshooting

Issue Solution
Image does not appear Check the file path. If using a URL, make sure the image exists. Try placekitten.com to test.
Sprite frames are misaligned Verify your frameWidth and frameHeight match the actual frame size in your sprite sheet.
Animation is too fast Increase the frameSpeed value (higher = slower animation).
Animation is too slow Decrease the frameSpeed value (lower = faster animation).
Sprite only shows first frame Make sure you are calling sprite.update() every frame.
Images flicker Preload all images before starting the game loop. Use onload callbacks.
Image does not load Check browser console for CORS errors. Use local files instead of external URLs if needed.

What's Next

You now have a character that can be animated with sprites! In the next tutorial, you will learn:

  • Topic 3: Handling mouse and touch input for mobile games
  • Topic 4: Using the built-in collision detection
  • Topic 5: Building a complete platformer with tilemaps

Next steps animation


Resources

  • Official TCJSgame Website: tcjsgame.vercel.app
  • Discord Community: discord.gg/j45mn75q
  • GitHub Repository: github.com/terracodes004/tcjsgame
  • Free Sprite Sheets: opengameart.org
  • Working Image Placeholders: placekitten.com

Share Your Creation

I would love to see the animated characters you create! Share your game in the comments or reach out on Discord.

Next tutorial: Handling Mouse and Touch Input – Building for Desktop and Mobile

Happy coding. 🎮✨

Top comments (0)