Gamasutra: The Art & Business of Making Gamesspacer
Making the Move to HTML5, Part 1
View All     RSS
November 1, 2014
arrowPress Releases
November 1, 2014
PR Newswire
View All





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 2 of 3 Next
 

JavaScript for Game Development

Only one general-purpose programming language can be used in the browser: JavaScript. It's also known by its official name: ECMAScript. This is not intended to be a guide or introduction to the language, but we have highlighted some aspects that stood out to us or had unexpected performance implications when approaching JavaScript from a C/C++ background.

Particularly when moving a big project from C/C++ to JavaScript for the first time, developers will likely face some challenges. The JavaScript syntax resembles C, but the language has more in common with functional languages like Lisp; it has closures and first-class functions.

We found closures to be very powerful, making asynchronous programming much easier and clearer. However, subtle errors can occur if developers are not fully aware of their behavior.

For example, quite a common mistake is to create closures inside a loop, referencing variables that change during the execution of that loop. This can lead to confusing behavior since variables referenced by a closure contain the value at the point the closure is executed, not at the point of its creation.

JavaScript has first-class functions, meaning that functions can be passed as parameters, returned from other functions, assigned to variables and stored in dictionaries. Strings can be compiled at runtime into functions, although this is not recommended for security reasons as the string could come from an unknown source and could compromise other parts of the code.

The language has object-oriented features, but there are no classes. JavaScript has constructors and prototype-oriented inheritance. Objects behave like key-value dictionaries, able to optimally store and retrieve anything by name. We found that trying to access values by the wrong name was a common error, usually a typo in the code that stores or retrieves the data.

Objects can be assigned as prototypes of other objects. If a property is not found on an object then the runtime will check the prototype object, and so on. As functions can be stored on objects and objects can share prototypes, the prototype mechanism allows methods and code reuse in a familiar way. Functions also behave as objects, and can be used to store and retrieve properties by name.

JavaScript objects do not have destructors. The JavaScript runtime schedules their destruction once there are no remaining references to them, and JavaScript code does not receive any notification when that happens. We found it non-trivial to find memory leaks in JavaScript code (that is, hanging references to objects which are in turn never garbage collected). Some JavaScript profilers provide object counts on their heap snapshots, which does help, but they require that objects be created from non-literal constructors, using the new operator, in order to differentiate between them. Some profilers also provide reference tracking in their heap snapshots which helps to identify why a given object has not been garbage collected.

As a best practice, we recommend establishing a clear and well-defined policy of object ownership. This makes it easier to point to the code that is responsible for maintaining (and releasing) the reference to a given object.

JavaScript does not have static types, so variables are allowed to have different types over the life of the program and some automatic conversions will happen if different types are used in the same operation. For example, adding numbers to strings will result in a string concatenation of the number converted to a string. This can result in errors that are very hard to find and can have performance implications.

Another subtle point and possible source of bugs are the JavaScript bitwise operations, which convert parameters to signed 32-bit integers with big-endian order and in two's complement format. For example, the following two statements are true:

(0x80 << 24) !== 0x80000000

(0x80 << 24) === -2147483648

Both would be false if unsigned integers were used. Note the use of the triple-equal operator, because it does not perform type conversion if the operands have different types. In contrast, the double-equal operator does perform type conversion and could hide errors in the code.

Although JavaScript variables can have Boolean values, every other basic type can be used on a conditional expression, with the following values being equivalent to false: null, undefined, the empty string, the number zero, the number NaN.

JavaScript does not have block scope; it has a global scope and function scope. Variables can be created anywhere in a function, and will be accessible everywhere, as if they were declared at the top of the function. This will confuse experienced C++ developers who are new to JavaScript.

Exceptions are supported in JavaScript, but the language does not support an exception hierarchy. The throw operator can be used on anything; an object, a number, a string, and it is caught by the first try / catch surrounding the code. There is no way to catch only objects of a specific type.

The language has evolved considerably from its origin and some features are not entirely standardized across browsers. Any recently added feature should be checked for before use. For example, the ability to define getters and setters for object properties with Object.defineProperty has, for us, become one of the most useful features added recently, however we need to provide extra code paths for when support does not exist. In some cases testing for the existence of a function is enough to check for a supported feature, as in the case of Object.defineProperty. In other cases, the code must try to actually use the feature inside a try / catch block and check whether an exception was raised.

We found JavaScript to be a powerful language because of its dynamic nature and functional features, but also a complicated language for big projects. With multiple developers working in parallel on a code base of hundreds of thousands of lines of code, mistakes will happen infuriatingly often without discipline and the right development tools.

At Turbulenz, we try to minimize development mistakes by employing a development process focused on code quality: strict coding standards, frequent code reviews and automatic unit testing. We also routinely use static analysis tools to check our code for common mistakes. So far we have not found analysis tools with the same level of inspection as those available for C and C++, but the following tools have been helpful in that they do catch a fairly large class of bugs, and they do improve over time: Closure Linter, JSHint, and JSLint.

When a new software engineer joins Turbulenz, they receive a copy of JavaScript: The Good Parts by Douglas Crockford. We consider this book a very good introduction to JavaScript, explaining common mistakes and good patterns.

Conversion Tools

Some developers may be interested in the translation tools available for converting other languages to JavaScript. They allow development in one language -- such as one with static typing in order to minimize runtime errors -- which is then converted to JavaScript.

Some of the more popular tools include haXe, Dart and Emscripten. The TypeScript project is a relatively recent addition that extends JavaScript to allow type information to be specified. This is particularly interesting since, as well as providing advantages in terms of error checking and development tools, TypeScript is "backwards compatible" with JavaScript.

We would encourage anyone considering using these tools to make sure they fully understand the implications. Automatically generated JavaScript code may provide suboptimal performance or a substantial size increase over hand-written JavaScript code. What works well in one environment may fail in a different one, requiring multiple code paths. Debugging problems and tracing back to the original offending code may prove difficult, and in some cases fixes may require tweaking the original source code to "trick" the compiler into generating safe JavaScript. Support for specific browser features may not exist, which will also limit what can be achieved.

However, these tools do have some interesting properties (such as the ability to turn C++ code into JavaScript that does essentially no garbage collection), and they are continually evolving and improving. It makes sense to be aware of them as an option, and to monitor their development.

Editors

Editors for static typed languages can provide a lot of functionality that is harder to provide for dynamic languages.

Good editors supporting JavaScript usually provide:

  • Syntax highlighting
  • Auto-completion suggestions for:
    • Default global objects.
    • Well-known external libraries like jQuery.
    • Variables and functions defined within the current function.
    • Global variables and global functions defined within the current file.
  • Refactoring at the current file level.
  • Integration with JSLint or JSHint.

Features such as auto-completion for custom objects, global variables from other files etc. are still lacking for dynamic languages. Some editors are starting to parse and execute all the JavaScript code in the current project in their own JavaScript engines to better judge the availability of variables and properties at the current point in the code. However these editors will frequently generate incorrect or incomplete suggestions. Writing your code in specific ways may help the editor with its suggestions, for example by initializing all the properties in the constructor. In this case the editor may be able to track which constructor was used to build the current object and inspect it for properties to suggest.


Article Start Previous Page 2 of 3 Next

Related Jobs

Twisted Pixel Games
Twisted Pixel Games — Austin, Texas, United States
[10.31.14]

Senior Graphics and Systems Engineer
Twisted Pixel Games
Twisted Pixel Games — Austin, Texas, United States
[10.31.14]

Mid-level Tools and Systems Engineer
Sega Networks Inc.
Sega Networks Inc. — Madison, Wisconsin, United States
[10.31.14]

Mobile Game Engineer
Forio
Forio — San Francisco, California, United States
[10.31.14]

Web Application Developer Team Lead






Comments


Keith Nemitz
profile image
"Unfortunatelly, both the Turbulenz Engine and WebGL only support OS X versions higher than 10.5. Please update your operating system"

The constant push to update software may be good for the economy, but its like superficial consumerism on steroids.

The reason I don't upgrade my Mac is, it allows me to make my games compatible with 10.5 and above. Try any of the demos at www.mousechief.com, to see what I mean. (and the same goes for Windows. They all run on XP or better, and some still run on Win'98.)

David Galeano
profile image
As our technology runs in the browser we are limited by what the browser developers decide to support, and 10.5 is not high on their list of priorities. AFAIK there are no browsers supporting WebGL by default on 10.5.

David Sena
profile image
Nice article. Pretty thorough overview of the technological side of HTML5 games.

Enjoyed particularly the example about the problems with Space Ark and how they were solved. It helps to give a more pratical perspective about the whole matter.

K Gadd
profile image
Good article. I have to disagree with the suggestion to use jsperf microbenchmarks to decide what approach is fastest, though; there are many scenarios where jsperf's crazy design will cause the microbenchmarks to not match reality. The way it's written suppresses some of the optimizations performed by JS runtimes and causes other optimizations to apply to entire benchmark runs, obscuring the actual cost of your code. It also has a rather strange approach to variance where benchmark scores with high variance (due to GC pressure/pauses, for example) are treated as equivalent, making its conclusions entirely false.

In general for performance testing in the browser, right now you really have to settle for timing things in the context where they're actually going to run. This lets you take into account the work the JIT does to optimize your code and better observe secondary effects (like GC pressure, which may not show up at all in jsperf).

Duncan Tebbs
profile image
That's a good point. We have indeed created a lot of benchmarks outside of jsperf.

The examples you gave (GC pauses and looking at the effectiveness of JIT, in particular when using Typed Arrays) are exactly the kind of things we've had to measure with our own benchmark setups.

David Galeano
profile image
Microbenchmarking using jsPerf is just another tool in the box, it is not perfect as you said, but it has its uses. If the results we get using jsPerf are inconclusive or do not correlate with performance gains in the game, then we write our own benchmarks or try different approaches, as my colleague Duncan said.

But of course, micro-optimizations are the last thing we try, our main target is improving algorithms, logic and data structures, and that is what produces the biggest gains.

Steve Fulton
profile image
This is some great stuff! Thanks.

Paul Lenoue
profile image
I just checked out Turbulenz and am impressed! Great use of HTML5. I look forward to the rest of the articles. I just hope they try to make some non-standard casual games in order to stand out from the rest.

Lennard Feddersen
profile image
Never gets past "Loading..." on my iPad.

David Galeano
profile image
Which game did you try? It is possible that it run out of memory. We have similar issues on some Android devices and we are working on reducing the amount of resources used for tablets and mobile.

Did you try the samples at http://biz.turbulenz.com/samples ?

Lennard Feddersen
profile image
Hi David, Save The Day and Polycraft. I ran Polycraft on my desktop just fine.

David Galeano
profile image
OK, thanks, we'll look into it. Until recently WebGL was not an option on mobiles and tablets, it is still in beta actually, so we were focused more on desktop support.

Kevin Long
profile image
Really nice start to this series of articles. I look forward to taking some of these ideas into my games and HTML5 classes.
Thanks.
Kevin


none
 
Comment: