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.
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>
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:
- A sprite sheet image
- The width and height of each frame
- The total number of frames
- 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>
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>
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();
}
}
Using Your Own Images
To use your own images:
- Save your image files in the same folder as your index.html
- Use relative paths like "player.png" or "sprites/walk.png"
- 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);
};
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
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
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)