Build a simple TypeScript spritesheet animator

Click on the image below to switch the animation:

Sprite animation has been the heart and soul of most 2d games since the 1980s. Some 8 bit machines had hardware support for sprites. This step-by-step tutorial shows how to create a minimal TypeScript program that loads a PNG spritesheet, animates 20 frames (60×60 pixels) from the first row, and cycles through 5 rows when you click the canvas. The finished project runs in the browser and is dependency-free.

What you will build


Files you will create


Spritesheet image

We're using the spritesheet from my game Goldmine.

If your spritesheet uses a different frame size or different columns/rows, change the constants in sprite.ts accordingly.

index.html

Create an HTML page that hosts a canvas and loads the compiled JavaScript or download it here: index.html. Save this as index.html in your project folder:

<!doctype html>
<html>
<head>
  <meta charset="utf-8" />
  <title>Spritesheet Animator</title>
  <style>
    canvas { background: #111; display:block; margin:20px auto; image-rendering: pixelated; }
    body { color:#ddd; font-family:system-ui, sans-serif; text-align:center; padding:18px; }
  </style>
</head>
<body>
  <h3>Click the canvas to cycle rows</h3>
  <canvas id="c" width="240" height="240" class="example-canvas"></canvas>
  <script src="./sprite.js"></script>
</body>
</html>

sprite.ts (TypeScript)

Copy the code below (or download it here: sprite.ts) and save it as sprite.ts. It is annotated and deliberately small so you can adapt it easily.

// sprite.ts
const CANVAS_ID = 'c';

// Spritesheet configuration
const FRAME_W = 60;
const FRAME_H = 60;
const FRAMES_PER_ROW = 20;
const ROW_COUNT = 5;

// Animation configuration
const FPS = 12;
const MS_PER_FRAME = 1000 / FPS;

const canvas = document.getElementById(CANVAS_ID) as HTMLCanvasElement | null;
if (!canvas) throw new Error('Canvas not found');
const ctx = canvas.getContext('2d');
if (!ctx) throw new Error('2D context not available');

// Optional scale for on-screen crispness
const SCALE = 2;
canvas.width = FRAME_W * SCALE;
canvas.height = FRAME_H * SCALE;
ctx.imageSmoothingEnabled = false;

const img = new Image();
img.src = 'spritesheet.png';
img.onload = () => start();
img.onerror = (e) => console.error('Failed to load spritesheet', e);

let currentFrame = 0;
let currentRow = 0;
let lastTimestamp = 0;
let accumulator = 0;

function update(deltaMs: number) {
  accumulator += deltaMs;
  while (accumulator >= MS_PER_FRAME) {
    accumulator -= MS_PER_FRAME;
    currentFrame = (currentFrame + 1) % FRAMES_PER_ROW;
  }
}

function draw() {
  if (!ctx) return;
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  const sx = currentFrame * FRAME_W;
  const sy = currentRow * FRAME_H;
  const sw = FRAME_W;
  const sh = FRAME_H;

  const dx = 0;
  const dy = 0;
  const dw = FRAME_W * SCALE;
  const dh = FRAME_H * SCALE;

  ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);
}

function loop(ts: number) {
  if (!lastTimestamp) lastTimestamp = ts;
  const delta = ts - lastTimestamp;
  lastTimestamp = ts;

  update(delta);
  draw();

  requestAnimationFrame(loop);
}

function start() {
  currentFrame = 0;
  currentRow = 0;
  lastTimestamp = 0;
  accumulator = 0;

  canvas.addEventListener('click', () => {
    currentRow = (currentRow + 1) % ROW_COUNT;
    currentFrame = 0;
  });

  requestAnimationFrame(loop);
}

How it works — quick explanation


Compile TypeScript and run

If you have TypeScript installed globally, run:

tsc sprite.ts --target ES6 --lib DOM,ES6

This produces sprite.js in the same folder. Put index.html, sprite.js, and spritesheet.png together and open index.html in your browser.


Customizing the animator


Next steps and ideas


More TypeScript tutorials

Interactive fractal graphics zoom

Animated plasma effect