Contre Jour

In 2012, Contre Jour was known for its innovative gameplay, charming art style, and captivating soundtrack as an iOS app. This showcase site demonstrates the JavaScript advances and advanced multi-touch features in HTML5 from that era.

View demo

About Contre Jour

“Contre Jour” is a video game that is "blurring the lines between interactive art and games". Made popular on iOS, and created by Maksym (Max) Hryniv, Contre Jour is known world-wide for its innovative gameplay, charming art style, and captivating soundtrack. With the debut of Internet Explorer 10, Microsoft was looking for an opportunity to demonstrate the updated JavaScript engine and advanced multi-touch features they had packed into their new browser.

Contre Jour’s snappy touch controls, unique gameplay, and intensive multimedia requirements presented just the challenge Microsoft was looking for. Teaming up with Max and custom development shop Clarity Consulting, Internet Explorer has brought Contre Jour to the browser using only HTML5 and JavaScript. Completed over six months, bringing Contre Jour to the web pushed the boundaries of what even we thought was possible - proving that HTML5 is ready for the prime time as a viable platform for casual gaming on the web. The following technical tear down provides rich insight into the challenges faced and solutions achieved in bringing Petit to life in the browser. We Hope you enjoy it.

Overview

The goal of the project was to show how Internet Explorer 10 was shrinking the gap between browser based games and native games by bringing Contre Jour to HTML5. We set a goal from the start that we would do this without sacrificing any of Contre Jour’s depth. We knew there were going to be challenges, and large ones at that. Contre Jour is massive, from both a code and media perspective. It boasts over 80,000 lines of Objective-C code, hundreds of image assets, and even more configuration files.

The biggest challenge by far was the conversion of the Objective-C code to JavaScript. The Objective-C code was heavily object oriented and JavaScript’s native support for concepts like encapsulation, polymorphism, and inheritance, is sparse at best. On top of that, differences between the two languages made a direct line-by-line port out of the question. We went through the Objective-C code in detail, evaluated each component, determining how it related to other components, and devised a way to implement each one faithfully in JavaScript.

From the beginning, as always with game development, performance was a must. We were concerned that tracking and moving the many elements across the screen, including the environment, would potentially tax the browser. HTML5 makes drawing simple, but we needed to stay cognizant of the fact that drawing was going to be our most expensive operation with regards to performance. Take a look at the ‘Under the Hood’ section below to see some of the performance tricks we used.

Under the Hood

Ground Physics

image

A defining feature of Contre Jour is how the user interacts and manipulates the surrounding world rather than manipulating the game’s hero character. In Contre Jour, a player shapes the clay-like ground with their finger, which is one of the primary ways to move the game’s hero, named Petit, around the screen.

Coding the ground logic in JavaScript was a big challenge. Mimicking the way it moves and shapes as the user touches it as well as the way it interacts with the games hero required pages of physics soaked logic. To render Contre Jour’s clay-like ground we used a modified JavaScript port of Box2D, which is the same physics engine used in the iOS version. This gave us almost identical functionality for managing physics objects, creating joints, and handling collisions.

The ground is made up of many distinct individual Box2D bodies, and that’s what gives it the ability to be shaped. It appears so smooth when it’s drawn because a quadratic curve is drawn in-between each of its sub-bodies. This gives the ground its claylike feel, but also makes rendering it very expensive.

Particles & Game Details

image

Since the many moving particles that fill the screen are dynamic, we had to pay careful attention to rendering performance. From a coding perspective, grass, dust and flies are sprites that need to be updated and drawn frequently to maintain the game’s lively atmosphere. Immersing the player in a detail rich environment adds surprise and delight to the experience. But, if done incorrectly, these details can end up being more of a distraction rather than adding value.

To address the performance problems that came along with Contre Jour’s complex environment, we kept environment particles on a separate canvas. We also used primitive frame drop logic within the particle engine to ensure a smooth experience. In other words, batches of particles are updated every frame but only drawn occasionally. By doing this we were able to maintain the visually detailed world of Contre Jour without impacting performance.

Snots

image

The ropes (called snots) in the game are actually made up of four separate images (the head, the eye, the eyeball, and the tail) connected together by drawing at least four Bezier curves and two lines. The snots are created by chaining bodies together with joints in the Box2D physics engine.

Once the rope’s bodies are created, we can ask for the location of the rope’s bodies. With those locations, we start a canvas path and append to it quadratic curves based on the bodies locations. When the path is complete, we stroke it a solid black and then perform a fill operation.

Snot Texturing

image

Drawing the ropes turned out to be one of the most complex parts of the project because there is no visually reliable way to apply a texture to geometry through the canvas API, which is commonplace in gaming languages like OpenGL.

Unlike other games, many of Contre Jour’s game elements are drawn procedurally using the Canvas API, rather than using sprite images. For example, the static ropes which are drawn dynamically, also contain a striped pattern that we could not easily apply a texture to. To draw these ropes we needed to follow this series of steps:

  1. Split the rope up into a number of sections based on the rope’s length.
  2. For each section, divide it into two separate pieces - a top piece and a bottom piece.
  3. For both the top and bottom pieces in each section, calculate a separate transform based upon the section’s points and draw the appropriate half of the texture based upon those values.

Multiple Canvas Approach

image

As mentioned earlier, compositing various game elements on multiple HTML5 Canvas elements is critical to achieving good performance in complex browser-based games.

By using this approach, we were able to selectively update and draw different parts of the screen at different rates. Updating and drawing the ground isn’t cheap, so we needed to be selective about when and how we rendered it. Each piece of ground is rendered on its own HTML5 canvas, separate from other game elements. This was necessary because it enabled us to disconnect changes to the ground from other elements in the game by tracking each ground section and detecting whether or not the user had changed it.

Each time the user drags or moves the ground, we flag the affected ground piece, and only that ground’s specific canvas is updated and redrawn. Had we left the ground to a single HTML5 canvas, the performance would have been unacceptable due to the constant rendering of the ground. You can learn more about how to achieve good performance with complex scenes in this Canvas Performance article.

Tip: Not all game elements need to be updated and drawn at the same frequency, and by spreading components of the game across multiple canvases, you can more easily manage drawing a large number of elements at various frequencies during your game loop. Don’t go crazy though, you’ll see a performance impact if you layer too many canvases on top of each other.

More HTML5 Tech

Objective-C to JavaScript

One of our key challenges early on was migrating the deep object hierarchy of Contre Jour from the original iOS code into JavaScript. To help with this we leaned on John Resig’s “Simple JavaScript Inheritance” pattern in several places where we needed inheritance.

This allowed us to use a lot of code from Contre Jour’s Open-C architecture, more than we anticipated. Components in the game that shared common base behaviors, such as the particle systems, were easier to implement because we had an “inheritance” hierarchy that allowed us to avoid writing everything from the ground up each time. Without a pattern like this, it would have been very difficult, if not impossible, to bring the game to HTML5. We would have ended up having to re-write most of the game. Thankfully, using Simple JavaScript Inheritance pattern saved us a lot of time and resources.

CSS3

CSS animations and transitions played a big part in the development of the menus and non-gameplay elements of the game. Most of these CSS transitions occur during screen transitions or game events, such as showing/hiding the pause menu, transitioning between levels, or transitioning between a level and the level picker. In browsers like Internet Explorer 10, every pixel on the screen is hardware accelerated. What does that mean? It means that without any extra code you have the power to create high fidelity, high performant effects. This helped us offload some of the rendering work from the already abused canvas to the DOM and CSS layout engine.

image image

We also used CSS3 Media Queries for scaling the site to support multiple screen resolutions. CSS3 media queries allow developers to attach a condition to affect the scope to which the style applies. For example, we used a media query to apply a scale transform to our outer DIV container to scale down the site for smaller screens.

Tip: Scaling using media queries freed us from the burden of creating and supporting multiple sizes of images. This is an important optimization - maintaining multiple sets of images would have caused additional pain since we had hundreds of images to manage already.

Multi-Touch

One of the few areas in the game where our code branches in different browsers is in the implementation of multi-touch support. Implementing multi-touch was one of the easiest parts of development, thanks to Internet Explorer 10’s built-in support for pointer event listeners. The great news is that they just work. From a developer’s perspective this was great, because it allowed us to focus head’s down on the more challenging parts of the project.

From a coding perspective, we spent some time writing a main touch module that wrapped the browser specific touch support. The module tracks input events, regardless of the browser platform, and bubbles them up to our game engine for processing. In addition, game elements are able to “subscribe” to a given instance of a touch or pointer to receive update notifications through its lifecycle. This made it easy for elements like the ground in the game to respond to touch across browser platforms.

Here is an example of code that has the event listeners hooked up across browsers:

function inferInputModel() {
    if (window.navigator.msPointerEnabled) {
        return 'pointer';
    } else if (window.ontouchstart !== undefined) {
        return 'touch';
    } else {
        return 'unknown';
    }
}

switch (inferInputModel()) {
    case 'pointer':
        element.addEventListener('MSPointerDown', msStart);
        element.addEventListener('MSPointerOut', msStop);
        document.addEventListener('MSPointerUp', msStop);
        document.addEventListener('MSPointerCancel', msStop);
        document.addEventListener('MSHoldVisual', preventDefault);
        break;
    case 'touch':
        element.addEventListener('touchstart', touchStart);
        document.addEventListener('touchend', touchStop);
        element.addEventListener('mousedown', mouseStart);
        element.addEventListener('mouseout', mouseStop);
        document.addEventListener('mouseup', mouseStop);
        break;
    default:
        element.addEventListener('mousedown', mouseStart);
        element.addEventListener('mouseout', mouseStop);
        document.addEventListener('mouseup', mouseStop);
        break;
}

About this demo

Contributors

Last updated

Sep 8, 2015

Categories

Share