October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan NowOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk5 min

Build a Browser Game: Canvas, JavaScript, and the Animation Loop

A practical guide to the essentials of a browser game: retain game state in JavaScript, draw it with Canvas 2D, and use requestAnimationFrame for timing and animation.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To make a small HTML5 game, create a page with a canvas, keep game objects in JavaScript, and use requestAnimationFrame() to update and redraw the scene. Calculate movement from the callback’s timestamp rather than moving a fixed amount per frame, so the game behaves consistently across different display refresh rates.

What a browser game needs

A compact browser game has three basic parts: an HTML page, JavaScript that stores and updates the game state, and a way to display the scene. Canvas 2D is a convenient starting point when you want to draw a unified scene with moving objects. It is not the only option: MDN’s guide to drawing graphics also discusses SVG and WebGL.

As an Amazon Associate I earn from qualifying purchases.

Canvas drawings are pixels on a drawing surface, not separate DOM elements. Keep positions, velocities, and other object data in JavaScript so you can update them and draw the scene again each frame. If individual objects need to remain directly addressable as page elements, DOM-based graphics may be a better fit.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How to make a game loop with requestAnimationFrame()

window.requestAnimationFrame(callback) asks the browser to call a function before a repaint. It is a one-shot request: the callback must request another frame to keep the animation running. The browser’s callback timing generally follows the display’s refresh rate, which can vary by device. Browsers commonly pause callbacks in background tabs and hidden iframes. See MDN’s requestAnimationFrame() reference.

This minimal example moves a square across a canvas. Save it as an HTML file and open it in a browser:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Canvas game loop</title>
  </head>
<body>
  <canvas width="640" height="360" aria-label="A square moving across the game area">
    A square moves from left to right across the game area.
  </canvas>
  <script>
    const canvas = document.querySelector("canvas");
    const ctx = canvas.getContext("2d");
    const player = { x: 40, y: 40, speed: 180 };
    let previousTime;
    let frameId;
    let running = true;

    function frame(timestamp) {
      if (!running) return;

      const deltaSeconds = previousTime === undefined
        ? 0
        : Math.min((timestamp - previousTime) / 1000, 0.05);
      previousTime = timestamp;

      player.x += player.speed * deltaSeconds;
      if (player.x > canvas.width) player.x = -24;

      ctx.clearRect(0, 0, canvas.width, canvas.height);
      ctx.fillRect(player.x, player.y, 24, 24);

      frameId = requestAnimationFrame(frame);
    }

    frameId = requestAnimationFrame(frame);

    function stopGame() {
      running = false;
      cancelAnimationFrame(frameId);
    }
  </script>
</body>
</html>

The first callback only establishes a starting timestamp, so the initial elapsed time is zero. On later callbacks, the code converts the timestamp difference from milliseconds to seconds, multiplies it by the player’s speed, clears the previous drawing, and paints the updated position. The example’s 0.05-second maximum is an implementation choice that limits a large movement jump after a delayed frame; it is not a browser requirement.

Why movement should use elapsed time

If you add a fixed distance on every callback, an object’s speed depends on how often callbacks occur. A display refreshing at 120 Hz can receive more callbacks per second than one refreshing at 60 Hz, so fixed-per-frame movement can run at different speeds. Instead, express speed in units per second and multiply by elapsed seconds: distance = speed × deltaSeconds.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A background tab may stop receiving animation callbacks. When the page becomes active again, the timestamp difference can be much larger than a typical frame interval. Capping elapsed time, as in the example, can prevent a sudden jump. The cap’s value should suit the game; it trades simulating the entire delay for avoiding an unexpectedly large update.

How to add input and build the scene

Keep input handling separate from drawing where practical. Event handlers can record which keys or pointer buttons are held; the game loop then reads that state when updating the player. This keeps the loop responsible for applying game rules and rendering, rather than drawing directly in response to each input event.

  1. Create the surface: Add a <canvas> element and obtain its 2D context with canvas.getContext("2d").
  2. Represent the world: Store each player, obstacle, or other object as JavaScript data, such as position and speed.
  3. Record input: Register keyboard, pointer, or touch event handlers that update input state.
  4. Start the loop: Request a frame with requestAnimationFrame(frame).
  5. Update and draw: Calculate elapsed time from the callback timestamp, update game state, clear or cover the old frame, and draw the current scene.
  6. Continue only while active: Request another frame during gameplay, and retain the request ID for a pause, game-over state, or teardown.
  7. Load images before drawing them: If the game uses image assets, wait for each image’s load event before calling drawImage(). See MDN’s Canvas drawing guide.

How to stop or pause the loop

Store the ID returned by the most recent requestAnimationFrame() call. Pass that ID to cancelAnimationFrame(frameId) to cancel the scheduled callback. In the example, the running flag also prevents the callback from updating or drawing if it is invoked after the game has been stopped.

To resume after a pause, reset previousTime before requesting a new frame. Otherwise, the first resumed callback may calculate elapsed time across the entire pause and move objects too far. The loop should schedule another callback only when the game is meant to run. MDN’s anatomy of a video game explains the game-loop structure and timing trade-offs.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

When to consider a different rendering or update approach

Canvas 2D is a practical default for a small game whose objects are redrawn together. SVG or DOM elements can suit scenes where individual objects need to be addressable, while WebGL may be appropriate for graphics workloads that warrant its additional complexity. The right choice depends on scene complexity, accessibility and interaction needs, performance requirements, and what you already know.

Updating and drawing once per animation callback is the simplest loop and is suitable for many basic games. Games with physics or deterministic simulation may need fixed-step updates separated from rendering. That design can make simulation timing more predictable, but it adds complexity; choose it based on the game’s requirements rather than adding it automatically.

Make canvas content understandable and usable

Canvas content is not automatically exposed to screen readers like ordinary DOM content. Provide an accessible description and equivalent controls or information for meaningful game content. The example includes an aria-label and fallback text; for a game whose meaning changes as it runs, update the description or provide an equivalent accessible representation. See MDN’s Canvas accessibility guidance.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.