Gobble, in addition to being a fun game, is a web technology case study with the goal of creating a functioning HTML5 based client side architecture for mobile applications.
Through a concrete application use case, the aim is to select and evaluate a suitable combination of modern JavaScript frameworks and build solid and reusable application architecture also applicable to larger projects.
Gobble also seeks to provide a visually appealing presentation and a fluid user experience by utilizing web technology building blocks, HTML5, JavaScript, and CSS3.
The game
Gobble is a version of the traditional word finding puzzle, where the player tries to identify as many words as possible on the alphabet grid during a limited time. The words can be composed of adjacent letters and the longer the word, the more points it gives. It is a fun game that develops cognitive, observation and language skills.
The game can be played in a single player mode, so that the player points out the found words on the grid, and tries to achieve a maximum score by finding as many words as possible before the time runs out. Alternatively, several players can play together around a single smartphone or a tablet. During the countdown, each player writes words down on a piece of paper. After the countdown ends, words can be highlighted on the screen to demonstrate how they were found and to verify the points they give.
Key features:
- both single player challenge and multiplayer social gaming readily supported with a single game mode
- Finnish and English languages supported for letter frequencies
- supports a wide variety of screens and devices as the layout automatically adjusts to display size, works well in both portrait and landscape displays
- supports both touch and mouse interaction
- available both as a downloadable Android application and as a web game running on modern browsers, including Safari, Firefox and Chrome
How to play
To start playing the game, click the play button. The idle animation ends, a fresh selection of letters is displayed and the clock starts running. During and after the game, you can construct words on the grid by clicking or tapping the cubes, keeping in mind the following rules:
- the next cube has to be adjacent to the previous one (up or down, left or right, or touching diagonally)
- a cube can be used only once per word
- a word has to be at least three letters long to qualify
To accept the word and include it in the found words -list, click the green ✓, to cancel select the red ✗. A word can be created and added to the list only once, even if it there are several ways to construct the word. Added words can be removed with mouse or by swiping to right over the word.
Scoring is based on word length: three and four letter words give one point, five letters gives two points, six letters three, seven gives five, eight gives 10 and nine and above 14 points.
Select settings to change game duration or language. Game duration can be adjusted from 15 seconds to 6 minutes. Language setting changes the frequency of letters appearing on the grid, making it easier to construct words in a particular language. Currently, Finnish and English are supported.
There are different conventions on what words are acceptable. Thus, it is up to the player or players to decide what words they accept – no dictionary support is provided.
The architecture
Drivers
Gobble was created as a study project to build a web technology based client-side architecture for mobile applications. Though the application case is relatively focused, the goal was to create an architecture that also scales to larger projects. Focus has been on client side code but it is seen important that server side interaction over a REST API would be straightforward to implement.
A key task was to evaluate how HTML5 presentation and CSS3 styling could be used to produce a visually appealing and responsive user experience. It was seen necessary to verify that the code and styling is browser independent but not to spend time to ensure wider compatibility or support for old browser versions. For simplicity, development was focused on current WebKit based browser versions for both desktop and mobile as well as on Firefox on desktop. while Internet Explorer and Opera were not dragged along.
There are a number of great open source JavaScript frameworks and components available, and the goal was to utilize ready frameworks as much as possible, while also making sure that the different components play well together.
Further, utilizing a single code base both for single page browser web app and for downloadable mobile applications without extra hassle was also considered important.
Frameworks and libraries
The key decision was to base Gobble on Backbone.js and heavily use Marionette.js. Going through different alternatives left the feeling that Backbone, priding itself on its non-opinionated approach, remains on a relatively high level. It is strong on the data side and makes implementing interaction over a Rest API a breeze, but maybe lacks ready answers on the presentation side. Using Backbone alone would have required an effort to get a functional boilerplate set up and running. Fortunately, Marionette.js nicely addresses the gaps, providing useful functionality and helpful patterns especially in the presentatation area. Without Marionette, Backbone would probably have remained too abstract to my taste.
Used frameworks are listed on the left table. The decision to use Backbone also brought JQuery on board. Underscore.js, a dependency for Backbone, is a nice utility for manipulating collections and data sets. Local storage of user modifiable application settings is implemented on top of Backbone localStorage Adapter.
No UI level framework or widget library, such as Twitter Bootstrap, was used as the UI design is atypical and due to the desire to experiment with CSS styling. A single widget component, NoUISlider, was integrated to Marionette Views to learn how a ready component is used on top of the framework. Of course, a slider is a relatively involved component so a ready implementation was appreciated. On the other hand, a custom implementation of a modal popup was created on top of Marionette Views. A library was needed to capture touch events and JQuery Hammer was selected for the task.
MV and C
Gobble is building on MVC (Model-View-Controller) design pattern popular in many concurrent client side frameworks, including Backbone and Marionette. There are several different flavors of basic MVC pattern, even different letter combinations have been proposed to point out the differences. Here, it was not considered important to follow any particular line of thought. The paradigm was interpreted loosely and it is possible that the convention is misused.
Nevertheless, to classify the approach, it could be said that the design is C heavy: Emphasis has been put to the controller part as an attempt to reduce coupling between the presentation and logic layers. As a result, the role of the View component perhaps has been reduced compared to some alternative arrangements.
Models and Collections
For application data, a clear-cut approach utilizing Backbone Models and Collections to store and manipulate data was adopted. The mapping of application objects to models is obvious: a grid is a collection of cubes with letters, words is a collection of strings representing the constructed words and so on. Conveniently also the game timer, holding the remaining game time is managed as a model, for instance.
Access to models is liberal with no information hiding provided: Key collections are stored under a static namespacing object called Data that can be accessed by other components as they see fit.
As will be discussed, data binding between the presentation layer and models is one way only: Changes in the model get magically updated in the presentation, but changes from the presentation are signaled to state logic that commits the changes to models as needed.
LocalStorage adapter is used to store the current selection for game settings between the sessions. Currently, there is no server side interaction but one of the key benefits of using Backbone is that extending the functionality to utilize a server side REST API is straightforward. The storage adapter simply replaces the sync method that would be used to persist the model to a server over an API.
Views and Presentation
The View component is composed of Marionette Views together with templates and CSS stylesheets, while the actual DOM presentation is created using Underscore templates that correspond to Marionette View objects. A Marionette layout is used to wrap multiple views into a single presentation.
The driving belief behind the design is that the view layer should know about the underlying logic as little as possible. For example, views should not directly manipulate application data stored in models as the view layer is not aware of possible processing steps and side effects when data changes.
Therefore, the role of the view (in addition to presenting the model to the user) is to capture events from the user and mediate them to controlling logic that knows how to deal with them, given the current state of the application. View does know what information the user manipulated, so it also provides the controller references to the new or modified data so that the controller can change or augment the model as needed. Conversely, views are free to create and process presentation level events, such as run animations or expand or hide already loaded DOM elements, if application data or state does not change as a result.
The controlling logic is processing application logic -level events, such as "new word added", rather than UI layer related events such as "mouse clicked". Such a mapping is a responsibility of the view layer that also conveniently provides the mapping from touch and mouse based events.
A design assumption in Backbone seems to be that the element name and parameters of the wrapping DOM element are given in the view object definition and the template only provides the content inside the wrapping element. Rather than dividing HTML content between JavaScript code and HTML template, it would be nice if also the wrapping element and its classes and parameters could be defined in the template. A lot of searching produced no simple solution for the issue and the end result is a harmless but annoying extra empty div element inserted around each element added from a template.
States and Logic
As apparent, the Backbone – Marionette combination provides a solid solution for M and V parts of the MVC. However, for the underlying application logic layer a ready solution was harder the find. The feeling was that a URL router -based 'controller' used by many frameworks would not alone be sufficient for the C role. Not only as Model and Controller can live both on the client and server. A state machine -inspired controller was therefore created for the task.
The definition of what is 'a state' is thought to be related to the events that can be processed and changes that can occur in the application logic rather than what is being presented to the user. For instance, changing game settings such as game duration is not possible during the game play, hinting that several states could be used to model the application. The structure of the game is relatively simple, so three states are sufficient corresponding to pre game, in-game and postgame phases of game play.
With this design, each state handles events, both from the user and other sources (e.g. created by the game timer) that are relevant within the current application state. The state modifies data accordingly and calls views to be displayed as needed. The mapping between the state and view is not necessarily one-to-one, but a state could require different views to be displayed for different purposes.
A state instantiates a Marionette layout object to display a set of views. Alternatively, it can get a handle to the current view when the state is entered if adapting the view is preferred to rendering a new presentation. The implementation provides a state controller object that manages the current state and transitions from one state to another. States can also provide functions to be called when the state is entered or exited to initialize the state and do housekeeping. An application state extends a base State object and provides its own event aggregator based on Marionette Wreqr. The aggregator is used by views and other sources to trigger events in a loosely coupled fashion. States process the events using the event handler functions or simply ignore the events that are not relevant.
Additionally, states allow extending the state so that both the additional events and event handlers can be provided by supplementary modules, making simple object oriented aggregation (I believe is the term) possible. For instance, selecting letter cubes on the grid to form new words is possible during the game (in-game state) but also after the game timer has expired (post-game state). Both states are simply extended with an object providing the events definition and the corresponding event handling logic for letter selection.
Structure and style
In addition to event handling logic implemented in Views, the presentation structure is created by XHTML snippets from the templates and relies heavily on CSS positioning and styling. The UI presentation is divided into few main elements, including the letter grid, current word pane, word list pane, settings pane and so on. Each main building block of the UI uses its own relative positioned div with layers used to organize the panes on top of each other. The style definition is created using Less and precompiled to a CSS stylesheet.
A point was made not to use any image files and so all styling is done with CSS: Shading of the background and cubes was achieved using linear-gradient backgrounds. Box-shadow and text-shadow were used for shadow effects and border-radius for rounded shapes. Animation for appearing and disappearing cubes was created using scale transform combined with transitions, and rotation for the letters is similarly created with CSS transform. Appearing and disappearing of UI pane elements is smoothed with visibility related transition effects.
Screen adaption
It turned out to be an interesting challenge to create the layout so that a single implementation would be able to adapt itself to different screen sizes and orientations. While a standard web page can flow vertically as long as it takes, a particular challenge with a mobile game app is that the display needs to cover the full screen area. The cubes and grid need to be square and the size of these elements needs to be in proportion to the screen height or width. An additional complication comes from the fact that percentage definition of margins refers to screen width, not height, even for horizontal margins, making it impossible to create perfect squares with margins using just percentage based measurements in the stylesheet.
Several upcoming and almost-there CSS features would solve this issue in a relatively simple manner, but none of the alternatives are supported in the target browser environments. Modifying sizes and positions from JavaScript code would have been an option, but would have violated the spirit of the architecture. Another option would have been a dynamically compiled client side Less stylesheet, but the approach was considered to be on the heavy side for the problem at hand and was not attempted.
A modern way to approach the problem is based on viewport percentage units: vh, vw, vmin and vmax, that make it possible to access the height and width of the display from the stylesheet and use CSS calc() to determine the size of the elements such as the cubes and grid. This should give an automatically adjusting CSS layout with little effort. Unfortunately, a bug in Chrome limits the applicability of the approach. Further, the properties and calculations are not supported by Android browser yet.
Alternatively, a flexible box layout could have provided a way to scale the elements and automatically distribute empty space within the elements as the display gets larger. Alas, the latest version of the flex layout spec is not fully supported by Mozilla or mobile Webkit based browsers.
It turned out that media queries can access the screen height and width as well as the aspect ratio, and are well supported in the target environments. Media queries can be used to create a set of layout definitions by conditionally settings sizes and margins of the presentation elements for different screen sizes and aspect ratios. Setting the default font size conditionally using a media query makes it easy to refer to sizes through the rem unit. This obviously works for fonts but can also be used for sizes such as border-radius.
To create an adapting presentation based on media queries, first a sizing model of the layout was created (using MS Excel) that defines the size of the elements in percentages of screen height and width. The excel sheet calculates appropriate element sizes for display size ranges and maps the key element heights and widths to pixel sizes or percentages as appropriate also taking into account margins. It then composes a CSS string containing a set of media queries, each defining the element sizes for a range of screen sizes. The string is then simply copy-pasted into the Less stylesheet.
The result is a "piecewise-linear" screen adaptation, where some elements have fixed sizes while the ones defined as percentages smoothly grow until a new media query rule kicks in just before elements would start overlapping. A bit far-fetched solution perhaps, but it seems to achieve the desired outcome while waiting for the better solutions to realize.
Animation
Continuing with the 'no image files' philosophy, an experiment was done with the HTML5 Canvas element. A time progress bar circulating the grid was implemented using JavaScript to draw on the canvas. The color of the bar gradually changes from blue to red as time runs out. Subpixel rendering, where the browser smoothes the drawing when non-integer pixel values are given, was first utilized, and it resulted in a nicely progressing animation. However, it turned out that different browsers handle the shadow blur a bit differently and in some browsers ended up smudging the canvas badly. As a fix, the canvas was made to update only whole pixels at a time, resulting in a more consistent outcome but a somewhat jerky animation.
Modularization
A key aspect of the desired architecture was the ability to modularize the code into meaningful and manageable components. Require.js was used for creating AMD (Asynchronous Module Definition) modules. Code was divided into modules following the MVC structure: One module and source file for each model, view, state, a couple of utility modules and an application module. This lead to a larger number of files, but it is a personal preference to keep the modules focused and lines of code per file limited.
Use of Require.js seems to require a great deal of care, as the errors that are created when the definitions are incorrect are often vague and root causes difficult to figure out. For example, use of AMD means that the dependencies between the modules cannot be circular, so that module A would require module B that would again require module A. A circular definition just results in the module being undefined. Spending awhile to build the basic setup with configuration files and think through the module structure and dependencies was time well spent. Also, it paid to make sure that AMD compatible versions of component libraries were used instead of trying to shim in non-AMD libraries.
Module dependencies are mostly flowing from application to states to views and models. In a couple of instances, a local require definition was needed to provide access a module that had been introduced before, for example when communicating events back to states from views. Using the top level application object to hang things from would probably not be a good idea here.
Require.js was also used to build a single minified file and to uglify the script contents. Almond AMD loader was utilized in the minified build, so that the file can be loaded directly from the html script tag.
Mobile application
As discussed, the target was to use the same code, HTML, CSS and JavaScript, to create both a web page and a mobile application. A standalone Android application was created using Cordova (Phonegap).
The installation of the development environment with the required Android development tools and Cordova libraries did not go without issues, but once the environment was successfully up and running, the creation of the application was straightforward. Basically, three files: index.html , a minified JavaScript build file, and a stylesheet were copied into the Cordova project folder and the application was ready to be tested. Some tweaking was necessary for the project settings and Android manifest files. Also, icon images in different sizes were to be placed in one of the many alternative folders. It seems that a mighty powerful development machine is needed to meaningfully test the app using the device simulators, though, as the simulation runs painfully slowly.
The process to publish the application and related descriptions to Google Play store was not complicated. The application is available for download here for Android 3.0 and newer.
The game can also be accessed on a web server here using Firefox, Chrome or Safari.
Contact
The intent of this discussion has been to highlight the crucial design choices made during a case study project. The discussion is on a high level on purpose, highlighting architecture and design rather than digging deeper into code. A number of detailed code level examples are already available for for the interested.
If you have any questions, ideas or suggestions, please do not hesitate to contact me through the form below.