HTML6 GamesFree Web Games / devlog

No install. Just play.

Free mini games right in your browser. Sign in to save your records to your account and pick up on any device.

All games
All games
All dev log posts

The game only moved once a second, and only on iPad

Oct 25, 2026 Β· Tech Β· ν•œκ΅­μ–΄

I built Cloud Hop and tested it thoroughly in desktop Chrome. Then I opened it on an iPad, and the little yellow chick moved about once a second. The same code ran at a smooth 60fps in Chrome. Fixing it took three attempts, and the second one went in the wrong direction.

The game draws to an HTML <canvas> element with the 2D drawing API. Chrome and Safari run the same code, but their internals differ. Every browser on iPhone and iPad uses the rendering engine behind Safari (WebKit), so being fast in Chrome says nothing about iPad.

When there is no real iPad at hand, you can imitate one with the iOS simulator that ships with Xcode, Apple's developer tool for the Mac. Safari inside the simulator uses the same engine as a real device, but the drawing is done by the Mac's graphics chip, so the speed can differ from a real device. That difference is the point of this post.

In the simulator's Safari, Cloud Hop ran at 11 to 14fps. The game logic was fine, but drawing a single frame took a long time. On the real iPad the chick moved about once a second, which is unplayable.

Since this would never show up in Chrome alone, I first added a ?debug=fps URL option that prints the frames per second on the board. Seeing a number made it possible to compare where it was slow.

The background has to look continuous out to the edges of the screen. To do that, the shared helper (view.stretch()) read the edge rows of the canvas every frame and stretched them outward, copying the canvas onto itself four times with drawImage. A simplified version looks like this.

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d'); // 2D drawing context

// reads the canvas that was already drawn and draws it onto itself
ctx.drawImage(canvas, 0, 0, width, 1, 0, 0, width, edge);

In Chrome that copy costs almost nothing. In Safari it was very slow.

First I removed the stretching call from Cloud Hop and painted the sky directly over the whole visible area. The art stayed the same, and Cloud Hop was fixed.

But several other games used the same helper: Archery, Bubble Shooter, Knife Toss, Penalty Kick, Home Run Derby, Zigzag Stairs, Spiral Drop and Timber Chop all had the same problem. Instead of fixing them one by one, I decided to fix the shared helper.

I assumed the amount of copying was the problem. So I changed the helper to copy a single edge row into a small canvas and stretch that. The canvas was no longer copied onto itself. Measured in the simulator, the results looked great: seven of those eight games ran at 60fps and Timber Chop at 52fps. Compared with Cloud Hop's 11 to 14fps, it looked fully solved.

I shipped that change.

11fps again on a real iPad

After the deploy I opened Dash Run on a real iPad and it was still at 11fps. The canvas on that device was 2732Γ—1784 pixels. The approach that had no problem in the simulator was just as slow on the real device.

That was when I understood that the problem was not how much I copied but the fact that I read the already-drawn canvas back at all. Copying one row into a small canvas still reads pixels from the finished canvas. I added a temporary switch to turn the stretching off or change how it read, and with stretching off the same device ran at 60fps. The simulator uses the Mac's graphics chip, which hid this read cost.

So I removed the shared stretching helper and made each game paint its background directly out to the edges of the visible area. Clouds and enemies are drawn out to that edge as well. No code that reads the drawn canvas back remained.

// simplified example: paint the sky over the whole visible area, never read the canvas back
ctx.fillStyle = sky;
ctx.fillRect(view.left, view.top, view.right - view.left, view.bottom - view.top);

I changed ten games this way. On the real iPad, Dash Run went from 11fps to 120fps. Timber Chop, Archery and Bubble Shooter also reached 120fps after the same change.

Code that reads an already-drawn canvas back is very slow in Safari. Even if the simulator is fast, don't trust it; check on a real device. This time I trusted the simulator's numbers, shipped, and ran into the same problem a second time.

The next post explains how to build this fps counter yourself. It barely touches the game code and goes all the way to reading the number in the iPad simulator.

You can play the game below.

References

Games from this post

Cloud HopBounce from cloud to cloud and climb as high as you can. Gray clouds fall apart, and storm clouds must be stomped from above! Play

More posts

Add to Home Screen

  1. Tap the Share button at the bottom of the screen (top on iPad). It looks like a square with an arrow pointing up.
  2. Scroll down and tap "Add to Home Screen".
  3. Tap "Add" at the top right. The HTML6 Games icon appears on your home screen.

This works in Safari. Opened from the home screen, it runs without the address bar, like an app, and even offline.