Gamasutra: The Art & Business of Making Gamesspacer
Making the Move to HTML5, Part 1
View All     RSS
February 22, 2019
arrowPress Releases
February 22, 2019
Games Press
View All     RSS

If you enjoy reading this site, you might also want to check out these UBM Tech sites:


Making the Move to HTML5, Part 1

February 7, 2013 Article Start Previous Page 3 of 3

Runtime and Performance

JavaScript runs on a Virtual Machine with tight security controls and a limited set of APIs to interact with the external world. Recent JavaScript engines employ Just-In-Time (or JIT) compilation to generate machine code that executes much faster than the traditional interpreted bytecode of the past. Modern engines evolve at a fast pace, improving performance and reducing memory usage with every release. As one would expect, performance is not up to the level of well-written C / C++ (we found JavaScript to be 4 to 10 times slower depending on the browser) but the gap keeps closing with every new version.

We use jsPerf to evaluate performance of small snippets of code across browsers and find the fastest way to perform a given operation. Unfortunately there is sometimes no single code snippet that is fastest on all browsers (and all of their versions) and so we are forced to compromise, usually based on market share.

Memory allocation will become an issue for big games. What fits well in a console with 512 MB can take over a gigabyte in a browser. Every JavaScript object has a higher overhead compared to the C++ equivalent.

The type of objects used can also affect memory size. JavaScript engines implement numbers as either 32-bit signed integers or 64-bit floating points, and usually both take the same amount of memory (8 bytes).

In order to reduce the memory usage of big arrays of numbers we recommend the use of typed arrays where possible. We made a memory saving of 20 percent in one of our demos just by switching arrays holding 3D vectors to use Float32Array.

Note that typed arrays have generally better performance characteristics than standard Arrays. Most JIT compilers understand how to directly address the underlying memory used by typed arrays, and can generate extremely efficient code to access and operate on them when the data type can be correctly predicted.

As mentioned above, JavaScript uses garbage collection to dispose of objects that are no longer referenced. Garbage collectors employ mark-and-sweep algorithms to detect objects that can safely be destroyed. Some also use a concept of multiple generations to optimize the allocation and deallocation of short-lived objects, and incremental algorithms may be employed to distribute the cost of marking and sweeping. This is also an area of heavy active development.

The total number of objects alive at a given time has a direct effect on the cost of garbage collection. Older VMs will stop the world for seconds during collection if there are millions of active objects. This situation keeps improving and, nowadays, even millions of objects might only stall execution for hundreds of milliseconds, although this still manifests as a perceptible "skip" in a game. Garbage collection is usually triggered by either a lot of object creations in succession, or at fixed periods of time (for example the engine may invoke a full sweep every 10 seconds).

Not surprisingly, we have found it very important for performance to keep the number of objects we create as low as possible. Some of our demos or examples do not create a single object during execution of a frame.

There are several strategies we have found to be useful for reducing object count. Reusing dynamic arrays from function to function and from frame to frame (i.e. using a scratchpad) can be very effective. Also, consider converting Arrays of Objects to flat arrays with interleaved properties. In one of the games on our site, this alone reduced object count by over 75 percent and solved problems with garbage collection pauses.

In some cases, encoding information and commands in custom bytecode can be a way to trade off runtime performance for object count. For example, if storing an SVG path, maintaining a single string that contains instructions for a particular rendering shape and decoding these instructions on the fly will likely use a lot less memory and many fewer objects (although more CPU time) than unpacking the string and storing the instructions as a hierarchy of objects.

The Space Ark title is an example of where we worked very closely with the developer to optimize the game using some of the ideas given here. By drastically reducing the number of dynamically created objects we were able to essentially remove garbage collection pauses during gameplay on modern JavaScript runtimes, and still maintain the visual quality of the original Xbox Live Arcade version, including character animations and particle effects.

Execution of JavaScript code can be assumed to happen in a single thread. The latest browsers do support an API to create an equivalent to multiple JavaScript processes, known as Web Workers (described in a future article) that can only communicate via messages. No direct data sharing between processes is allowed.

Since execution is essentially single threaded, if JavaScript code executes for too long without yielding, the browser may prompt the user to stop the runaway code. If the user agrees, execution of the code will stop immediately and without warning. Even if the user does not stop the code, repetitive dialog boxes warning about long running code are likely to be annoying. Therefore we recommend that long-running work be done in small increments, using the timers and intervals provided by the browser to schedule functions to be executed in the future.

Debugging and Profiling

All browsers now provide a debugging environment embedded as part of the browser itself. The debugger tends to be hidden under a Development Tools menu option or similar.

Debugging features provided usually include:

  • Traversal and inspection of the HTML tree.
  • Recording and inspection of HTTP requests.
  • Console logging, a read-eval-print loop for executing snippets of code.
  • Debugger with support for: Breakpoints, Stack traces, Variable watching

The profilers provided by the browsers often support call-graph capture (based on a form of instrumentation which can add a noticeable overhead to the execution) and heap snapshots (with object counters, objects size and references between them). However, these features and the implementation quality can vary between the browsers.

Debuggers may run in the same process and the same JavaScript context as the code they try to debug, which reduces their stability. We found that all debuggers will eventually crash or fail either when code complexity becomes too high for them, or when they try to show too much information about watched variables. We also suffered from timer callbacks being invoked while stepping through code, which can break certain debuggers and can be very confusing for the developer. However, these tools are extremely valuable and are improving as the browsers are developed.


This article has been an overview of high-end game development for HTML5, including details of the development environment and workflow. In following articles we will talk more about particular features exposed by HTML5 and related standards that are of interest to games. As well as covering specific areas of game development such as Graphics and Audio, we will give tips and recommendations for how to extract the best quality and performance across a wide range of browsers and platforms.

Article Start Previous Page 3 of 3

Related Jobs

DMG Entertainment
DMG Entertainment — Beverly Hills, California, United States

Technical Manager
Mythical Games
Mythical Games — Los Angeles, California, United States

Senior 3d Engineer / Tech Artist
Funcom — Oslo, Norway

Tools Programmer
Funcom — Oslo, Norway

Technical Animator

Loading Comments

loader image