Free Download PDF Books, HTML5 Games Most Wanted – Build the Best HTML5 Games

HTML5 Games Most Wanted – Build the Best HTML5 Games

HTML HTML5 ReadOnline

Summary of Contents

  • Page 1

    actionURI(http://www.freepdf-books.com ):http://freepdf-books.com

  • Page 2

    For your convenience Apress has placed some of the front matter material after the index. Please use the Bookmarks and Contents at a Glance links to access them. http://cncmanual.com/ actionURI(http://www.freepdf-books.com ):|||||||||||||||||||||||||||||||||||||||||||||||||||||| www.freepdf...

  • Page 3

    Contents at a Glance About the Authors ..................................................................................................... 275,xiii About the Technical Reviewer................................................................................... 277,xv About the Cover Image...

  • Page 4

    INTRODUCTION xvii Introduction HTML5 is a “game changer,” allowing web browsers on such diverse hardware as smartphones, tablets, and personal computers to display the same games, interactive ads, and rich-media applications that were previously only possible as long as the end-user had downl...

  • Page 5

    INTRODUCTION xviii and disadvantages of the HTML5 canvas element, SVG, web sockets, server-sent events, and web fonts, preparing us for understanding the implications of the choices made by the other authors in subsequent chapters. http://cncmanual.com/ actionURI(http://www.freepdf-books.c...

  • Page 6

    1 Chapter 1 The State of Open Web Games In this chapter, I introduce the concept of open web game development using technologies such as HTML5 and JavaScript. I cover the various features that these technologies provide, including such gems as the Gamepad API, which allows you to break away from...

  • Page 7

    Chapter 1 2 Why should you care about open web games? Open web games are by nature games created with open web technologies; as of today, these technologies are HTML5, CSS3, and JavaScript. In this chapter, I will be referring to these technologies under the umbrella term “open web games,” a...

  • Page 8

    The State of Open Web Games 3 and the technology has been created to work on a variety of operating systems and browsers with little to no platform-specific code. What you can be certain of is that if a platform supports HTML5 and the JavaScript APIs that your game requires, then it’s likely t...

  • Page 9

    Chapter 1 4 web gaming technologies, then you should get involved in the W3C Games Community Group (actionURI(http://www.w3.org/community/games/):www.w3.org/community/games/). Access to the world’s biggest audience If anything, this is perhaps one of the most important aspects of the web as a ...

  • Page 10

    The State of Open Web Games 5 HTML5 canvas (often referred to as simply “canvas”) is a JavaScript API and corresponding HTML element that allows for bitmap graphics to be created and edited within the browser. The plus points of canvas are that it’s speedy and that can produce pin-point pi...

  • Page 11

    Chapter 1 6 The limitation of the HTML5 audio element is that its purpose is really to play single audio files, like background music within a game. It isn’t suitable for sound effects, particularly if they are fast paced and if there are many of them to play at once. To solve this, the Audio ...

  • Page 12

    The State of Open Web Games 7 web games today would, at worst, simply stop working as soon as an internet connection failed, and, at best, they would stop sending data to your server and saving player data. When your player refreshes the page that the game is on, they’ll just see a blank page a...

  • Page 13

    Chapter 1 8 the cursor and locking it in place so that it doesn’t hit the edges of the screen. This means that instead of relying on x and y coordinate values for mouse position in related to the top-left corner of the browser, you instead rely on x and y distance values from the position that...

  • Page 14

    The State of Open Web Games 9 With much more on the way These technologies are really just scratching the surface when it comes to creating games on the web using open technologies. Mozilla and other companies are working hard to bring you these APIs and services to help make the web a better pl...

  • Page 15

    Chapter 1 10 Figure 1-1. Screenshot from HTML5 Bejeweled Figure 1-2. Screenshot from HTML5 Angry Birds http://cncmanual.com/ |||||||||||||||||||||||||||||||||||||||||||||||||||||| www.freepdf-books.com http://freepdf-books.com

  • Page 16

    The State of Open Web Games 11 Figure 1-3. Screenshot of the Robots Are People Too web site Figure 1-4. Screenshot from Runfield http://cncmanual.com/ |||||||||||||||||||||||||||||||||||||||||||||||||||||| www.freepdf-books.com http://freepdf-books.com

  • Page 17

    Chapter 1 12 Figure 1-5. Screenshot from Brandon Jones’s TF2 WebGL demo Distribution and monetization options already exist Something that I touched on earlier in this chapter is the ability to distribute your games and make money from them, something that is necessary for the web to become a...

  • Page 18

    The State of Open Web Games 13 The Chrome Web Store uses Google Checkout as the payment provider and they will take 5 percent of each transaction, which is significantly less than the rates on platforms like iOS. Facebook With over 800 million users, it made a lot of sense for Facebook to get in...

  • Page 19

    Chapter 1 14 browsers. If the open nature really is not an option to you, then perhaps open web gaming is not something to consider until you can justify the pros and cons that come with it as a platform. However, all of this doesn’t necessarily mean other platforms are more viable. For exampl...

  • Page 20

    15 Chapter 2 Complexity from Simplicity This chapter will take a look at game design to see how you can make a game fun at its most basic level. A case study of a game called “A to B” will be used to highlight some of the essential theories of design implementation. This chapter will also di...

  • Page 21

    Chapter 2 16 Game design background Unlike many game designers, I have never been obsessed with gaming. As a child, I was not allowed to own a gaming console (PC not included) until I was in high school. However, like most game enthusiasts, I can recall the first moment games became an importan...

  • Page 22

    Complexity from Simplicity 17 When the player presses Go, the ball is launched (at the same angle and velocity every time), and then is subjected to gravity. If the ball hits point B, the player receives points and moves on to the next level. If the ball goes off screen, the ball is returned to i...

  • Page 23

    Chapter 2 18 At its core, A to B is influenced by the Atari classics that I mentioned earlier. At the start, I recognized that since I am new to gaming, I needed to keep it simple. Asteroids was in black and white and the player controlled a triangle, yet somehow it was one of the most compellin...

  • Page 24

    Complexity from Simplicity 19 Once I established the technology, I set up a list of deliverables and deadlines. Planning out a timeline for this is useful for project organization and motivation. Having certain deliverables due at a certain time not only made me stay on schedule, but prevented m...

  • Page 25

    Chapter 2 20 activity, you should inform your players to use the “think aloud” method. This is a common practice in user interface prototyping. You instruct players to verbalize all of the decisions and thoughts that are going through their minds as best they can. This gives you insight into...

  • Page 26

    Complexity from Simplicity 21 Figure 2-4. A to B high-score list Rules of simplicity A to B helped me learn a few valuable lessons for making a simple, yet compelling, game. Focus on the core mechanics. By keeping A to B stripped down and transparent, the player is able to participate directl...

  • Page 27

    Chapter 2 22 not frustrating to learn how to play the game. It is important to immediately provide the tools for or instruction on how to play your game. Players should be able to participate and understand how the mechanics work right off the bat. This does not imply, however, teaching players ...

  • Page 28

    Complexity from Simplicity 23 Why A to B works Whether or not you care for the simple graphics, game play in A to B just works. This can be attributed to much of the theory that I read surrounding game development. First and foremost was Jesse Schell’s Art of Game Design: A Book of Lenses (Mor...

  • Page 29

    Chapter 2 24 Additionally, Processing.js lacks power. In the original version, you can draw thousands of objects to the screen without sacrificing frame rate. With the JavaScript port, however, there is a significant disparity between the two. This is largely due to the constraints of using the ...

  • Page 30

    Complexity from Simplicity 25 In regards to the JavaScript version of Processing, the following is a rundown of the strengths and weaknesses of Processing.js: Strengths Excellent graphics library Don’t need to learn JavaScript Great forum and community Cross-browser compatibility Can ...

  • Page 31

    Chapter 2 26 Basic Processing syntax If you are familiar with Processing or Java, this bit will look familiar to you. void setup(){ size(400,200); } void draw(){ background(0); fill(255,0,0); ellipse(mouseX,mouseY,50,50); } This is a very basic example of the Processing syntax. This code will ...

  • Page 32

    Complexity from Simplicity 27 In-line processing For people like me, you may want more access to the code than this. Processing.js allows you to write your Processing code directly in the script tag. The processing.js file will automatically convert what you write into Processing. The following i...

  • Page 33

    Chapter 2 28 size(400,200); } void draw() { background(0); fill(250); ellipse(mouseX,mouseY,ellipseSize,ellipseSize); ellipseSiz...

  • Page 34

    Complexity from Simplicity 29 Importing and loading images For anyone out there that wants to make a game, it is quite likely that you will want to have some sort of images involved. In order to incorporate them in your Processing.js code, it is handled a bit differently than with building web s...

  • Page 35

    Chapter 2 30 $.post("highscorelist.php",{score: theScore, name: playerName},function(data){ result = data; } As long as you can do some simple PHP communication, you can easily implement any sort of data storage that you can envision. Summary By now you have hopefully witnessed the pow...

  • Page 36

    31 Chapter 3 How to Make Multi-Platform HTML5 Games from Scratch Introduction Today, applications like Twitter, or games like Angry Birds, are available on many platforms. The number of different instances of an application can become huge. Such an application can target the number of platforms...

  • Page 37

    Chapter 3 32 Figure 3-1. Various platform directions It’s almost unfeasible to develop so many instances and maintain them yourself. You want to fix bugs and add features once—and only once. Hence, what’s required is a common language to describe an application for multiple instances—an...

  • Page 38

    How to Make Multi-Platform HTML5 Games from Scratch 33 Throughout this chapter, we will examine the case study of Same Game Gravity (a game available on six actionURI(http://github.com/gre/chess-game):platforms: iPad, iPhone, Android, web, Chrome Store, actionURI(http://github.com/gre/chess-game...

  • Page 39

    Chapter 3 34 stylesheets, and JavaScript scripts (to learn more about all available elements, see the Other Resources section). The most recently released version of the HTML specification is HTML4, but for several years the fifth version has been a work in progress and its release is imminent. ...

  • Page 40

    How to Make Multi-Platform HTML5 Games from Scratch 35 <script type="text/javascript" src="game.js"></script> </head> <body> <div id="pages"> <section id="menu" class="current"></section> <...

  • Page 41

    Chapter 3 36 CSS, a descriptive stylesheet language CSS (Cascading Style Sheets) allows us to easily describe web application styles through a list of rules. CSS is often neglected or misunderstood in game development, but CSS—especially in its third version—is a very powerful tool that you ...

  • Page 42

    How to Make Multi-Platform HTML5 Games from Scratch 37 Some significant CSS properties If you want to learn some basic CSS properties, we recommend you follow some of the resources listed at the end of this chapter. For now, we’ll concentrate on some of the more advanced and indispensable featu...

  • Page 43

    Chapter 3 38 Transitions CSS Transitions are a way to easily add animations to a web page. Basically, it allows us to perform an animation between two given states. CSS properties will change CSS values, which will be animated smoothly over a specified duration. To use CSS Transitions, like in a...

  • Page 44

    How to Make Multi-Platform HTML5 Games from Scratch 39 #myel:hover { color: green; padding: 10px; /* transition-* are getted by inherence */ } CSS Transitions allows us to make degradable games because CSS interpreters are nice; if they meet unknown properties, they simply ignore them and ...

  • Page 45

    Chapter 3 40 (Ctrl+A). Moreover, on mobile and tablet, you usually want your application (your game) to look like a real native application. Imagine playing a real-time game and accidentally triggering the text select mobile user interface! It will rudely interrupt the game, most likely leading ...

  • Page 46

    How to Make Multi-Platform HTML5 Games from Scratch 41 Stylesheet languages above CSS SASS (or SCSS) and LESS are both stylesheet languages that simplify writing and maintaining CSS source codes. They are compiled into regular CSS. We will use SASS in our examples (in fact, we’re using the “...

  • Page 47

    Chapter 3 42 top: 0 left: 0 width: 100% height: 100% +box-sizing(border-box) #pages > section z-index: -1 opacity: 0 +translate3d(-100%, 0, 0) +transition-property((transform, opacity)) +transition-duration((1s, 0s)) +transition-delay((0s, 2s)) &.current z-ind...

  • Page 48

    How to Make Multi-Platform HTML5 Games from Scratch 43 This means that only the current page is in the viewport of the browser (and take the full space with the styles we defined), other pages are outside, so are effectively invisible. We define a 1-second transition duration to perform a smooth...

  • Page 49

    Chapter 3 44 continueGame: function(){ /* ... */ }, newGame: function(){ /* ... */ scene('newgame'); }, game: function(){ /* ... */ scene('game'); }, help: function(){ /* ... */ scene('help'); } } }(); Listing 3-8. Starting the Router Entry Point of the Game Controller $(docum...

  • Page 50

    How to Make Multi-Platform HTML5 Games from Scratch 45 rook | knight | bishop | queen | king | pawn indicates the type of the piece current means the piece on the square is selected by the user playable means the square is playable for the selected piece That’s everything to make the user i...

  • Page 51

    Chapter 3 46 #chessboard > div &.current, &.current.lighter background-color: #beb results in the following CSS: #chessboard > div.current, #chessboard > div.current.lighter { background-color: #bbeebb; } Game logic We will now implement the game logic in the game.js J...

  • Page 52

    How to Make Multi-Platform HTML5 Games from Scratch 47 } }(); // ... }(window)); Inside the first closure, we have defined a ns argument, which refers to the window variable because we pass it in argument at the end. This is how you can redefine an object access name. This ns argument is ...

  • Page 53

    Chapter 3 48 var c = String.fromCharCode(i+97); self.pieces.push( piece('white', firstRowTypes[i], c+'1') ); self.pieces.push( piece('white', 'pawn', c+'2') ); self.pieces.push( piece('black', 'pawn', c+'7') ); self.pieces.push( piece('black', fi...

  • Page 54

    How to Make Multi-Platform HTML5 Games from Scratch 49 We want to be able to save a game, as shown in Listing 3-12, so that we can retrieve it between interruptions. Listing 3-12. Saving Game ns.Storage = function() { var gameKey = 'chessdemo_game'; return { saveGame: function(game) { ...

  • Page 55

    Chapter 3 50 The result Figures 3-3 and 3-4 show how the chess game should appear. Figure 3-3. Menu display Figure 3-4. chessboard 4http://cncmanual.com/ |||||||||||||||||||||||||||||||||||||||||||||||||||||| www.freepdf-books.com http://freepdf-books.com

  • Page 56

    How to Make Multi-Platform HTML5 Games from Scratch 51 Mobile frameworks jQuery and Zepto jQuery is an awesome, widely-used JavaScript library that makes working with the DOM easier and offers an abstraction to support all browsers. Zepto is an alternative to jQuery that only targets WebKit brow...

  • Page 57

    Chapter 3 52 Configuration of our chess game Listing 3-14 shows the config.xml we used to make our chess game: Listing 3-14. config.xml <?xml version="1.0" encoding="UTF-8"?> <widget actionURI(http://www.w3.org/ns/widgets):xmlns="http://www.w3.org/ns/widgets&quo...

  • Page 58

    How to Make Multi-Platform HTML5 Games from Scratch 53 Moreover, we need tools to make game development and maintenance easier. In fact, this tool quickly becomes essential to quickly spread (you can almost even say “compile”) the changes we made on a single source to different instances of ...

  • Page 59

    Chapter 3 54 background.jpg: the background of the game icon.png: the icon of the game Chewy.ttf: the font used The following is an extract of an Android/Makefile: SRC_DIR = ../src DIST_DIR = assets RESOURCES = images Chewy.ttf background.jpg icon.png VIEWS = index.html=game.html:"{vers...

  • Page 60

    How to Make Multi-Platform HTML5 Games from Scratch 55 To do this, we’ll inject translations in the DOM on page load with JavaScript. Our convention to identify a translation key is to have a i18n-{key} class on the DOM tag to replace the translation where {key} is the key of the translation (...

  • Page 61

    Chapter 3 56 updateDOM(); }, get: function(key) { var dict = dictionaries[lang] || dictionaries[defaultLang]; return ""+dict[key]; }, updateDOM: updateDOM } }(); $(document).ready(i18n.init); Pure DOM vs. canvas–based game There are mainly two...

  • Page 62

    How to Make Multi-Platform HTML5 Games from Scratch 57 ctx.beginPath(); ctx.arc(.5, .5, .4*size, 0, Math.PI*2, true); ctx.fill(); // fill a disc with gradient ctx.restore(); // restore the last ctx state saved } actionURI(http://www.w3.org/TR/html5/the-canvas-element.html):TipactionURI(ht...

  • Page 63

    Chapter 3 58 an acceptable fallback: the application should work even if some effects don’t occur (for instance, not supporting CSS Transitions will simply disable the transition animation). CSS transforms are very powerful and efficient (rotation, scale, skew, etc.) and are hard to make in ca...

  • Page 64

    How to Make Multi-Platform HTML5 Games from Scratch 59 Other resources The following web sites provide additional useful information on HTML5, the latest web technologies, and advanced JavaScript: actionURI(http://www.w3.org:):www.w3.org: Web specifications are available at The World Wide Web C...

  • Page 65

    61 Chapter 4 Creating, Saving, and Loading Tracks This chapter will explain a few of the basics on structuring games in JavaScript as well as game play in general. We will also cover a few JavaScript tips and tricks. Who we are, what we will build, and why We are two of the five creators of Mar...

  • Page 66

    Chapter 4 62 Figure 4-1. Screenshot of application The application and the sense behind it The goal of Marble Run is to build the longest marble run in the world. Every player can build his own little marble run, also called a “track.” After saving this track, it will be appended to the exi...

  • Page 67

    Creating, Saving, and Loading Tracks 63 Figure 4-2. Screenshot of Marble Run You see, Marble Run is very similar to what we plan to do in this chapter—and that’s not by accident. We get a lot of questions about the way we do this and that in Marble Run. So, our little application makes a gr...

  • Page 68

    Chapter 4 64 Why levels are important We want to take a closer look at the different types of games and how they are built up. There are basically two kinds of game plays when it comes to browser games. The ones build on certain levels, created by the maker, such as all the jump-and-run games, ...

  • Page 69

    Creating, Saving, and Loading Tracks 65 Figure 4-3. Screenshot of a creative track on marblerun.at Split the big thing into small components Now it’s time to get back to our little app. The first step with every application is to make a basic plan of how it’s going to be structured. Everyth...

  • Page 70

    Chapter 4 66 For the styling of the application, we need some CSS. So within the create-save-load-tracks folder, we create another folder named stylesheets. Now let’s add an empty style.css file to that folder for our CSS. But as you probably have already figured out, that is not enough. When ...

  • Page 71

    Creating, Saving, and Loading Tracks 67 <input id="track-name" type="text" placeholder="Track Name"/> </form> </section> The second section element holds a form with a lot of buttons, with each button representing a brick. Later these buttons w...

  • Page 72

    Chapter 4 68 Adding a little style When you open the HTML file in a browser, you will notice that things look a little shaken up. What we need is a basic CSS stylesheet to get all the parts of our application into the right places. To accomplish this, we link to our previously created style.css f...

  • Page 73

    Creating, Saving, and Loading Tracks 69 margin-right: 50px; float: left; } section#bricks-container { width: 100px; margin-right: 50px; float: left; } section#tracks-container { width: 300px; float: left; } button#save, input#track-name { float: right; } section#tracks-conta...

  • Page 74

    Chapter 4 70 Download (jQuery); button. Found it? Click it! In case your web browser doesn’t ask you where to save this page, right-click in the page and select “Save page.” Save the page as jquery.js into our javascripts folder. There are many ways to structure an application in JavaScrip...

  • Page 75

    Creating, Saving, and Loading Tracks 71 the context is more the API of the canvas to be used with JavaScript. We will explain how to get the context of a canvas element later. In the last block, we declare four global variables. The store and grid are two instances of store.js and grid.js, the t...

  • Page 76

    Chapter 4 72 means we have to be very aware of the order in which the elements are drawn onto the canvas. Once you draw something on it, you cannot draw underneath it. First things first. In this case, we want to draw the grid before we draw any bricks. This is why we decided to have everything...

  • Page 77

    Creating, Saving, and Loading Tracks 73 Now, for the draw function. function draw() { clearCanvas(); context.translate(0.5, 0.5); grid.draw(context); } First we clear the canvas. The command context.translate(0.5, 0.5); moves the drawing context half a pixel to the right and bottom. We ...

  • Page 78

    Chapter 4 74 for (var row = 0; row < numberOfRows; row++) { context.moveTo(0, row * this.cellSize); context.lineTo(gridWidth, row * this.cellSize); } context.stroke(); } Wow! That is a really big chunk of code. So, what do we do here? For the rectangle around the grid, we simpl...

  • Page 79

    Creating, Saving, and Loading Tracks 75 Figure 4-5. Screenshot of the application so far The bricks Now we get to a very interesting part: the first brick. First, we will have to create a new class in a new file. Since we are going to have a few different brick types, we might want to add a new...

  • Page 80

    Chapter 4 76 Figure 4-6. Folder structure Square The first brick that we go over is a simple rectangle. var Square = function() { this.row = 0; this.column = 0; this.type = "Square"; this.rotation = 0; } We initialize the class with a row and a column attribute. We also need ...

  • Page 81

    Creating, Saving, and Loading Tracks 77 To get our brick to the right position, we will translate the context to this point. context.translate(x, y) moves the coordinate system of the context by x and y. That means once you translated the context by (10, 10) you would start to draw at (10, 10), ...

  • Page 82

    Chapter 4 78 context.restore(); } The first difference is the radius in the draw function. We need to calculate it so we can draw a proper circle. The radius is half the brick size. After calculating, we do the same context.save(), context.translate(), and context.restore() magic we know from t...

  • Page 83

    Creating, Saving, and Loading Tracks 79 The first special part is as follows: context.translate(BRICK_SIZE / 2, BRICK_SIZE / 2); context.rotate(this.rotation * Math.PI / 180); context.translate(- BRICK_SIZE / 2, - BRICK_SIZE / 2); What do we do here? We know that every brick saves its rotation ...

  • Page 84

    Chapter 4 80 context.beginPath(); context.fillColor = 0; context.moveTo(0, 0); context.lineTo(BRICK_SIZE, BRICK_SIZE); context.lineTo(0, BRICK_SIZE); context.closePath(); context.fill(); context.restore(); } Drawing the triangle is also pretty easy. We move our imaginary penc...

  • Page 85

    Creating, Saving, and Loading Tracks 81 var mouseY = event.offsetY || event.layerY; var column = Math.floor(mouseX / BRICK_SIZE); var row = Math.floor(mouseY / BRICK_SIZE); } First we read the X and Y position relative to the top-left corner of the canvas element. Every mouse click trigge...

  • Page 86

    Chapter 4 82 Grid.prototype.addBrick = function(brick, context) { this.bricks.push(brick); brick.draw(context); } It adds the brick to its container of bricks. And after that, we just have to draw the brick onto the grid. We don’t have to trigger the big draw function in the application.j...

  • Page 87

    Creating, Saving, and Loading Tracks 83 function setBrick(buttonID) { if (currentButton) { currentButton.removeAttr("disabled"); } currentButton = $("#" + buttonID); currentButton.attr("disabled", "disabled"); switch (buttonID) { ca...

  • Page 88

    Chapter 4 84 var brick = new selectedBrickClass(); brick.column = column; brick.row = row; grid.addBrick(brick, context); } And that was all you had to do for that! Note We also added a safety drop out of the function. If the user didn’t select a brick type before clicking on the gr...

  • Page 89

    Creating, Saving, and Loading Tracks 85 Grid.prototype.getBrickAt = function(column, row) { for (var i = 0; i < this.bricks.length; i++) { if (this.bricks[i].column === column && this.bricks[i].row === row) { return this.bricks[i]; } } return null; } Very simple! ...

  • Page 90

    Chapter 4 86 Saving tracks Let’s walk through this step-by-step. First, we create a Store class. We add another new file named store.js to our includes and start out with something like this: var Store = function() { this.tracks = []; } The tracks array will resemble our database. That mean...

  • Page 91

    Creating, Saving, and Loading Tracks 87 information. So, what do we need for re-creation later? Obviously the position on the grid, so let’s store the column and row properties in our values object. The brick.type property is also very important; without it we don’t know what type of brick w...

  • Page 92

    Chapter 4 88 Once we have the JSON representing our track, we use the counter function to JSON.stringify, which is JSON.parse. This function will convert the argument string into a handy JavaScript object that we can work with. In our case, we get an array with all the brick value objects. This a...

  • Page 93

    Creating, Saving, and Loading Tracks 89 initUI(); draw(); }); The next step is adding functionality to the Save button. So let’s skip to the iniUI() function and add another click callback, as follows: $("#save-track").click(function(event) { event.preventDefault(); store.s...

  • Page 94

    Chapter 4 90 This might look a little complicated, but it’s actually very simple. First, we use jQuery to create an empty <p> tag and an <a> tag. We can leave the href of the <a> empty, but still set a href attribute. Why? That way the browser still marks it as a link. But we...

  • Page 95

    Creating, Saving, and Loading Tracks 91 You can get the complete code for this project from GitHub; just head over to actionURI(https://github.com/stravid/create-save-load-tracks):https://github.com/stravid/create-save-load-tracksactionURI(https://github.com/stravid/create-save-load-tracks): and...

  • Page 96

    93 Chapter 5 3D CSS Tutorial Harness the power of 3D CSS transformations to make a lightning-fast iPad game with creative JavaScript Introduction HTML5 canvas is, of course, brilliant. But it has to be said, its performance on iPads (and most other devices) leaves much to be desired. The GPU-acc...

  • Page 97

    Chapter 5 94 Our game has three main visual components: the puffer fish, the background layers, and the particles that occur when the fish explode (see Figure 5-1). Every graphical object is a DOM element—in fact, they’re all just divs with image backgrounds and I’m animating them by adju...

  • Page 98

    3D CSS Tutorial 95 Game variables The following shows the game variables. // DOM elements var container = document.createElement('div'), layer1 = document.createElement('div'), layer2 = document.createElement('div'), // screen size variables SCREEN_WIDTH = 1024, SCREEN_HEIGHT = 768, HALF...

  • Page 99

    Chapter 5 96 layer2.style.background = 'url(img/parallaxFront.png) transparent'; document.body.appendChild(layer2); container.className = "container"; container.style.webkitPerspective= "400"; container.style.webkitPerspectiveOrigin= HALF_WIDTH+"px "+HALF_HEIGHT...

  • Page 100

    3D CSS Tutorial 97 Game loop overview The following is the main game loop. The comments should explain enough about what’s going on. We're adding more fish, updating the parallax layers, iterating through all the fish, and updating them all, then finally calling emitter.update(), which looks a...

  • Page 101

    Chapter 5 98 Our Fish object handles the position, update, and appearance of our fish. The constructor parameters specify its 3D position, the image URL, and the image size. We’re creating an HTML div element and setting its CSS properties so it has a fixed size, absolute position, and a backg...

  • Page 102

    3D CSS Tutorial 99 styleStr, sx = Math.sin(counter*0.4)*0.04 + this.size, sy = Math.sin(Math.PI + counter*0.4)*0.04 + this.size; dom.style.webkitTransform = "translate3d("+this.posX+"px, "+this.posY+"px, "+this.posZ+"px) scale("+sx+","+sy+&q...

  • Page 103

    Chapter 5 100 this.velY += this.gravity; // and the velocity to the position this.posX += this.velX; this.posY += this.velY; this.posZ += this.velZ; //rotate pos and vel around the centre counter++; this.rotate(2); }; As well as x, y, and z position, we also have an x, y, and z v...

  • Page 104

    3D CSS Tutorial 101 Cross-browser considerations For the sake of simplicity, we’re blatantly ignoring cross-browser issues, and only worrying about iOS and webkit. But is it possible to make this work across all browsers? Modern browsers pretty much all support these transforms, but they all ...

  • Page 105

    Chapter 5 102 fishes.push(fish); // then add touch and mouseover events to the fish. fish.domElement.addEventListener("mouseover", fishMouseOver, true); fish.domElement.addEventListener("touchstart", fishTouched, true); container.appendChild(fish.domElement); } Se...

  • Page 106

    3D CSS Tutorial 103 Exploding the fish The following are the listeners that are called when you touch or mouse-over a fish. In either case, we need to find the fish object for the DOM element that fired the event. Note that the touchstart event has an array of touch objects; it may well be that ...

  • Page 107

    Chapter 5 104 Particles Figure 5-3. Image of exploding fish particles We don’t have room in this tutorial to look at the particle system in detail, but rest assured that it’s a very similar system to the way that we manage the fish. The particle emitter has its own update loop, and each par...

  • Page 108

    3D CSS Tutorial 105 The layer behind moves slower than the layer in front; in the following code, look where I’m multiplying counter by five for the back layer’s y position. Remember that counter increments every frame, so our back layer will move down five pixels per frame. The front layer...

  • Page 109

    Chapter 5 106 Conclusion As programmers, playability and responsiveness lies squarely on our shoulders—so making a game playable and responsive are skills that we need to acquire. Anyone can program a character that runs along a platform, but it’s harder to adjust its speed, gravity, and con...

  • Page 110

    107 Chapter 6 Particle Systems Introduction Particle systems are used to create many different special effects like fire, rain, and smoke. They are used in virtually all video games, as well as in a lot of movie special effects. But what is a particle? In essence, a particle is something repres...

  • Page 111

    Chapter 6 108 planning to simulate a galaxy, you will need to treat stars, or even clusters of stars as particles. If you are simulating rain, your particles will be individual raindrops. In this chapter, I will show you how to design and implement your own particle effects using HTML5 canvas. ...

  • Page 112

    Particle Systems 109 // The particle distance is 120 pixels from the left and 70 pixels // from the top var position = new Vec2(120, 70), // and in one second the particle moves // 10 pixels to the right and -5 pixels down (5 pixels up). velocity = new Vec2(10, -5), parti...

  • Page 113

    Chapter 6 110 The updated example now looks like this: // 60 times per second window.setInterval (function(){ var td = 1.0/60; particle.position.iadd(particle.velocity.muls(td)); }, 1000/60); Adding checks In JavaScript, numeric errors lead to a special NaN (not a number) value. All ope...

  • Page 114

    Particle Systems 111 There are drawbacks to using notNaN in production code, however. First, it doesn’t work in IE8 or older. Second, it makes your code slow. Luckily, you can simply disable it for use in production. var DEBUG = true; ... if(DEBUG) { notNaN(Vec2.prototype, ‘x’); n...

  • Page 115

    Chapter 6 112 Figure 6-1. Component overview Particles Every particle in the system needs to have at least a position and a velocity. Other properties can be added as needed; and may include, for example, the following: color image alpha age mass The update method of the particle updates i...

  • Page 116

    Particle Systems 113 this.position.iadd(this.velocity.muls(td)); } } Emitters Nothing comes from nothing. Unlike virtual particles in physics, our particles don’t pop into existence spontaneously; they are created by the emitter. The emitter gives particles their initial attributes...

  • Page 117

    Chapter 6 114 Renderer The job of the renderer is to visualize all the particles in a system. There can be different renderers for different targets and rendering techniques. The following are examples of rendering targets: canvas image canvas pixel WebGL HTML Different particles might requi...

  • Page 118

    Particle Systems 115 this.particles = alive; } }; Hello fireworks We have now covered all the basics necessary to create different particle systems. Now it’s time to get started with a simple example. Fireworks are definitely the “hello world” of particle systems. They look gre...

  • Page 119

    Chapter 6 116 Figure 6-3. An individual spark It is important that no component of the color used for the spark is 0. If one component is zero it will not get brighter when additively blended. That’s because 0 * anything === 0 (except if anything is NaN!). Implementing the main loop The main ...

  • Page 120

    Particle Systems 117 Implementing the emitter For our fireworks emitter, we want to pick a random point on the canvas. We then spawn 100 particles at that point and give them random velocities. This will make them fly off into different directions. function emit(system, width, height){ var ...

  • Page 121

    Chapter 6 118 In addition to gravity, we also want to simulate the air the fireworks are in. There are two interesting forces to do this: drag (air resistance) and wind. function dampingf(damping){ return function(particle, td){ particle.velocity.imuls(damping); }; } var drag =...

  • Page 122

    Particle Systems 119 We also need to add some code to the ParticleSystem to prune old particles. We will do this in a very simple way by using a new array for the particles that are still alive. This is not very efficient (it produces quite a bit of garbage), but it is very easy to implement and ...

  • Page 123

    Chapter 6 120 With those changes in place, our fireworks look quite decent. Now it’s your turn to play with the code and make them even better looking. You could, for example, add more colors or shapes to make the fireworks more varied. Fire Another classic particle effect is fire. In this se...

  • Page 124

    Particle Systems 121 Figure 6-5. Individual flame textures Loading the textures In this example, we will need multiple images. In most cases, your application will already have a way of loading resources and you should use that one. For our examples, a very simple function will do. It takes an ...

  • Page 125

    Chapter 6 122 // fireworks example. Again, just because I find it looks better that way. radius = Math.sqrt(Math.random()+0.1)*100; // choose a random texture particle.image = choose(images); // make speed dependent on image size so the small spark will be faster ...

  • Page 126

    Particle Systems 123 ctx.globalAlpha = 0.6; renderCanvasImage(ctx, system.particles, 5); ctx.globalAlpha = 1.0; ctx.globalCompositeOperation = 'source-over'; }, 1000/30); } You can see the complete example in fire.0.html. It does already look like a fire, but...

  • Page 127

    Chapter 6 124 You can see the result of this change in fire.1.html. It is already looking much better. There is still a noticeable popping of the flames as they reach their end of life. We can improve this by slowly fading them out. Fading out of existence To make the particles fade out smoothl...

  • Page 128

    Particle Systems 125 Figure 6-6. Smoke demo in action Creating the textures To create the 64 × 64 textures for the smoke particles, I have simply used some noise with a radial gradient as layer mask. The result is shown in Figure 6-7. Figure 6-7. Individual smoke sprites Implementing the emit...

  • Page 129

    Chapter 6 126 particles faster is no longer needed. I increased the angularVelocity and maxAge a bit to match the behavior of smoke a bit better. The new emitter looks like this: function emit(system, images, x, y){ // emit the particle at the center of the canvas with some random // v...

  • Page 130

    Particle Systems 127 ctx.globalAlpha = 1.0; ctx.globalCompositeOperation = 'source-over'; }, 1000/30); window.setInterval(function() { if(controls.hasFocus) { emit(system, images, controls.mouse.x, controls.mouse.y); } }, 1000/10); } loadI...

  • Page 131

    Chapter 6 128 Please note that performance optimizations depend on the runtime environment. Optimizations that have a positive impact today can become useless, or even harmful, tomorrow. They will also severely impact the maintainability and readability of your code. Single particles If we want ...

  • Page 132

    Particle Systems 129 Figure 6-9. Nesting of particles in a flat array. All the particles are packed into a single array. Removing particles from the middle of an array is very expensive. This is why we will leave dead particles (age > MAX_AGE) in the array and just skip them when drawing. Whil...

  • Page 133

    Chapter 6 130 var requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || wi ndow.mozRequestAnimationFrame || window.msRequestAnimationFrame || function(f){window.setTimeout(f, 5); } Micro optimizations We can gain some additional performance by tweakin...

  • Page 134

    Particle Systems 131 PARTICLES_LENGTH = MAX_PARTICLES * NFIELDS, // compatibility with legacy browsers Float32Array = window.Float32Array || Array, requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || ...

  • Page 135

    Chapter 6 132 if(controls.hasFocus) { emit(controls.mouse.x, controls.mouse.y); } ctx.fillStyle = 'rgba(0, 0, 0, 0.4)'; ctx.fillRect(0, 0, width, height); var imgdata = ctx.getImageData(0, 0, width, height), data = imgdata.data; for(var i = 0; i < PAR...

  • Page 136

    133 Chapter 7 Beginning WebGL WebGL (Web-based Graphics Language) is a very exciting new technology that leverages the power of the Graphics Processing Unit (GPU) within the browser. In this chapter, we will discuss the history and capabilities of WebGL. As a prerequisite to programming games wi...

  • Page 137

    Chapter 7 134 WebGL Origins Version 1.0 of the WebGL specification was completed in March 2011 by the Khronos Group. WebGL is based on OpenGL Embedded Systems (ES) 2.0, which, in turn, is derived from OpenGL. In 1992, version 1.0 of OpenGL was released as a non-proprietary alternative to Silicon...

  • Page 138

    Beginning WebGL 135 Demonstrations There are many wonderful applications that show the power of WebGL. One of the best-known demos was Google Body, which is now at actionURI(http://www.zygotebody.com):www.zygotebody.com. This demo displays an interactive 3D model of human anatomy. Another site ...

  • Page 139

    Chapter 7 136 Ironically, Microsoft can help some old video cards work with WebGL on Windows machines. The ANGLE (Almost Native Graphics Layer Engine) project translates OpenGL ES 2.0 API calls to DirectX 9 API calls. The result is that graphics cards that only support OpenGL 1.5 (OpenGL ES 1.0)...

  • Page 140

    Beginning WebGL 137 Libraries Rather than reinvent functionality, there are several excellent matrix libraries available for use with WebGL. There are also many higher-level graphics engines and APIs available. Matrix libraries Matrix operations—addition, multiplication, inverse, and so forth...

  • Page 141

    Chapter 7 138 Debugging tools Before we begin coding, the reader should set up WebGL-Inspector actionURI(http://benvanik.github.com/):from http://benvanik.github.com/ WebGL-Inspector/. It is currently available for Chrome and Safari and will soon be a Firefox extension as well. WebGL-Inspector l...

  • Page 142

    Beginning WebGL 139 Figure 7-2. Transforms of a translation (left), a rotation (middle), and a scale (right) Composing the scene view To set up a scene, you have to multiply the original coordinates by three separate matrices. These are the Model, View, and Projection (MVP) matrices. Model to wor...

  • Page 143

    Chapter 7 140 Figure 7-4. Diagram of world coordinates transformed to camera view Often, the model and view transforms are combined into one matrix. Projection transform Finally, the projection matrix transforms the camera view to the screen view. There are two ways of doing this—with a persp...

  • Page 144

    Beginning WebGL 141 Figure 7-6. The screen on the right is a wide screen and the aspect ratio is stretched instead of maintained. GLSL Understanding GLSL is essential, as stated at the Khronos WebGL wiki (www.khronos.org/webgl/wiki/ Tutorial#Creating_the_Shaders): “Nothing happens in WebGL wi...

  • Page 145

    Chapter 7 142 In Figure 7-7, we start with raw modelspace vertex positions. The vertex shader then transforms the vertices, usually by multiplying with the MVP matrices. Vertices are assembled into appropriate primitive types, such as points, lines, and triangles. Next the primitives are rasteri...

  • Page 146

    Beginning WebGL 143 Listing 7-4. The Start of demo_1.html <!DOCTYPE html> <html> <head> <style> body{ background: gray; } canvas{ background: white; } </style> <script src="http://code.jquery.com/...

  • Page 147

    Chapter 7 144 } if (!gl) { alert("Error trying to initialise WebGL"); } else { gl.clearColor(0.0, 0.4, 0.0, 1.0); } } To understand the initShaders method, first we need ...

  • Page 148

    Beginning WebGL 145 attachShaders(); gl.useProgram(shaderProgram); } function setupShaders(fragmentShaderSRC, vertexShaderSRC){ fragmentShader = makeShader(fragmentShaderSRC, gl.FRAGMENT_SHADER); vertexShad...

  • Page 149

    Chapter 7 146 Next we pass the sources to the setupShaders function. That function then calls makeShader, which is a helper function to create a shader, set the source, and compile it. This is done by calling the WebGL functions createShader, shaderSource, and compileShader. Once the individual ...

  • Page 150

    Beginning WebGL 147 GLSL types Description varying Vertex shader write, fragment shader read vecN Vector of size 1 × N matN Matrix of size N × N sampler Used for textures So the modelview and projection matrices are uniform throughout the vertex shader, while the position and colors are vertex...

  • Page 151

    Chapter 7 148 Note More information on the RGBA color format can be found on Wikipedia at actionURI(http://en.wikipedia.org/wiki/RGBA_color_space):http://en.wikipedia.org/wiki/RGBA_color_space. Our final function of the example is drawScene, shown in Listing 7-9. Listing 7-9. The drawScene Func...

  • Page 152

    Beginning WebGL 149 Figure 7-8. The output of Listing 7-10, a shaded square This is a lot of work to draw a square. The good news is that advanced concepts and techniques incrementally build off of the same foundation we have just set up. We have deferred showing our shader source until now. List...

  • Page 153

    Chapter 7 150 Our fragment shader is shown in Listing 7-11. Listing 7-11. Our Fragment Shader <script id="shader-fs" type="x-shader/x-fragment"> varying highp vec4 vColor; void main(void) { gl_FragColor = vColor; } </script> You can see in Listing 7-11 ...

  • Page 154

    Beginning WebGL 151 alert(e); } if (!gl) { alert("Error trying to initialise WebGL"); } else { gl.clearColor(0.0, 0.4, 0.0, 1.0); } } fun...

  • Page 155

    Chapter 7 152 $.ajax({ async: false, url: 'shader.vs', success: function (data) { vertexShaderSRC = data.firstChild.textContent; }, dataType: 'xml' ...

  • Page 156

    Beginning WebGL 153 gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0); gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesColorBuffer); gl.vertexAttribPointer(vertexColorAttribute, 4, gl.FLOAT, false, 0, 0); gl.d...

  • Page 157

    Chapter 7 154 Figure 7-9. Wireframe of an octahedron with its eight faces and six vertex points As you can see from Figure 7-9, vertices A, B, C, and D of an octahedron lie on the same plane and form a square. Vertices E and F mirror each other. The line connecting EF intersects the middle of t...

  • Page 158

    Beginning WebGL 155 Listing 7-14. Keyup Handler $(document).keyup(function(evt){ if (evt.keyCode == 80) //'p' { paused =!paused; } }); $(document).ready(function(){ canvas = $(...

  • Page 159

    Chapter 7 156 Using a function like animLoop that calls itself again is essential to animate more than one frame. In our initWebGL function, we need to enable depth testing, as shown in Listing 7-17. Listing 7-17. Enable gl.DEPTH_TEST function initWebGL() { … if ...

  • Page 160

    Beginning WebGL 157 Listing 7-18. Calculating Our Translation and Rotation After Each Frame mat4.translate(mvMatrix, [3*x, y, -12.0 + 5*z]); if(!paused){ x = Math.cos(translationAngle); y = x; z = Math.sin(...

  • Page 161

    Chapter 7 158 -1.0, 0.0, 1.0, 0.0, -height, 0.0, 1.0, 0.0, -1.0, -1.0, 0.0, -1.0, 0.0, -height, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, -1.0, ...

  • Page 162

    Beginning WebGL 159 18, 19, 20, 21, 22, 23 ]; octahedronVertexIndexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, octahedronVertexIndexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uin...

  • Page 163

    Chapter 7 160 A: (-width, 0, -width) B: (width, 0, -width) C: (width, 0, width) D: (-width, 0, width) E: (0, height, 0) F: (0, -height, 0) We have chosen 1 for the width and the square root of 2 (1.41) for the height. Each buffer specifies the itemSize, which is the number of sub-elements per it...

  • Page 164

    Beginning WebGL 161 Listing 7-21. Defining Six Colors Instead of Twenty-four for(var i=0; i < 6; ++i){ var color = colors[i]; unpackedColors = unpackedColors.concat(color); } octahedronVertexColor...

  • Page 165

    Chapter 7 162 Planning Game: A simple dart game. Models: A 2D dartboard/target will be rendered in a circular shape using triangle primitives and a textured image. A 3D “dart,” an elongated tetrahedron, will be rendered. Input: We will use mouse events to throw the dart and an HTML form b...

  • Page 166

    Beginning WebGL 163 dartboardVertexPositionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, dartboardVertexPositionBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); dartboardVertexPositio...

  • Page 167

    Chapter 7 164 NUM_SHADERS = 2, shaderPrograms = [], fragmentShaders = [], vertexShaders = [], vertexPositionAttributes = [], vertexColorAttributes = [], textureCoordAttributes = [], Our shaders are physically sto...

  • Page 168

    Beginning WebGL 165 dataType: 'xml' }); $.ajax({ async: false, url: shaderFilenames[i] + '.vs', success: function (data) { vertexSha...

  • Page 169

    Chapter 7 166 function setMatrixUniforms(i) { gl.uniformMatrix4fv(shaderPrograms[i].pMatrixUniform, false, pMatrix); gl.uniformMatrix4fv(shaderPrograms[i].mvMatrixUniform, false, mvMatrix); } function getVertexAttributes(i){ ...

  • Page 170

    Beginning WebGL 167 } function drawDartboard(){ //draw dartboard gl.useProgram(shaderPrograms[TEXTURE_SHADER]); mat4.identity(mvMatrix); mat4.translate(mvMatrix, [0, 0, dartboard_z]); setMat...

  • Page 171

    Chapter 7 168 Drawing our dart Our dart will be a stretched version of the octahedron we rendered in our previous demo, using only two colors, gold and blue. ... octahedronVertexPositionBuffer = null, octahedronVertexColorBuffer = null, octahedron...

  • Page 172

    Beginning WebGL 169 -dartWidth, 0.0, -dartWidth, 0.0, -dartHeight * dartSkew, 0.0, dartWidth, 0.0, dartWidth, dartWidth, 0.0, -dartWidth, 0.0, -dartHeight * dartSkew, 0.0, ...

  • Page 173

    Chapter 7 170 (octahedronVertexIndices), gl.STATIC_DRAW); octahedronVertexIndexBuffer.itemSize = 1; octahedronVertexIndexBuffer.numItems = 24; } Now it is time for us to draw the dart, as shown in Listing 7-29. Listing 7-29. Drawing the Dart functio...

  • Page 174

    Beginning WebGL 171 positionChange = [], timeChange = 0.0; $(document).ready(function(){ var canvasObj = $("#canvas") canvas = canvasObj.get(0); canvasObj.mousedown(function(e){ po...

  • Page 175

    Chapter 7 172 dart_x = Math.cos(angle) * dartboardDiameter; if(Math.random() < 0.5){ dart_x *= -1; } var velocity = ( Math.sqrt(positionChange[0]*positionChange[0] + positionChange[1]*posit...

  • Page 176

    Beginning WebGL 173 Figure 7-14. Our completed dart game The full source of the program is available online in the file darts.html. The following are some improvements you could make to the game: More precise scoring. Calculating double and triple score areas and separating the two bull’s-ey...

  • Page 177

    175 Chapter 8 CycleBlob: A WebGL Lightcycle Game I started learning JavaScript and WebGL for fun in my free time on evenings and weekends. For most of my career, I’ve programmed large enterprise applications in C++ and, on occasion, I’ve written some data visualizations using desktop OpenGL....

  • Page 178

    Chapter 8 176 teams to better their chances of winning. My version of the game takes place on a closed, 3D surface instead of a flat, walled arena. After two months of full-time work (between jobs), I came up with CycleBlob (actionURI(http://cycleblob.com):http://cycleblob.com), a full, 3D light...

  • Page 179

    CycleBlob: A WebGL Lightcycle Game 177 With some minor exceptions, (until WebGL arrived) interactive 3D content has been largely confined to monolithic applications that the user downloads and runs on a desktop computer. Back in the ’90s, we had VRML, an XML-like language that described 3D mod...

  • Page 180

    Chapter 8 178 Working with this setup is similarly simple. You write the HTML and JavaScript using the editor, save them to your project folder, and double-click the HTML file to open it in the browser to see if it works. This setup is sufficient for the first few attempts at tinkering with web ...

  • Page 181

    CycleBlob: A WebGL Lightcycle Game 179 The latest versions of Chrome and IE contain a full debugger out of the box. It allows setting breakpoints in the JavaScript code, debugging it step-by-step, and examines variables in a watch window. For Firefox, there is an add-on called Firebug actionU...

  • Page 182

    Chapter 8 180 A convenient and efficient way to represent a triangle mesh in a program is using three lists, as follows: Vertices list: Contains the 3D coordinates of all points where triangles meet. A point in a triangle mesh is usually a meeting point of three to six triangles. An edge between ...

  • Page 183

    CycleBlob: A WebGL Lightcycle Game 181 Figure 8-3. The tetrahedron model in 3D has four vertices, four faces, and six edges. Any point on the surface may have a normal, but only one is shown. From these JavaScript arrays, the code can directly construct the typed arrays that are sent to the Web...

  • Page 184

    Chapter 8 182 Models classification The geometric models in CycleBlob can be divided into three categories. Static “artistic” models that are simply moved around in the scene and are not manipulated in any way. The player bike and the bonus ball are such models. In a well-funded game produc...

  • Page 185

    CycleBlob: A WebGL Lightcycle Game 183 Vertices are specified by lines starting with the letter “v” and mesh faces are specified with “f” lines that, as before, specify the vertices’ indices. In this format, the vertices are specified as one-based indices, not zero-based. The full .obj...

  • Page 186

    Chapter 8 184 The bike model in CycleBlob is made of not one, but several distinct meshes—each with a different color. The JSON file of this model contains the lists of vertices and triangles for each of the meshes, along with the definition of its color. One of the meshes has a special “pla...

  • Page 187

    CycleBlob: A WebGL Lightcycle Game 185 total, only six triangles are updated—every frame for each wall—and processing time is kept to a minimum. Geometric processing Downloading a complete model from the server can become an issue if the model is considerably large and download speed is slow...

  • Page 188

    Chapter 8 186 Figure 8-7. A grid rendered using thick lines. The rendered lines have a constant width regardless of the view point. This creates an unaesthetic “thickness” at the edges of the shape, as well as small gaps between adjacent lines. Writing a fragment shader that discards the ...

  • Page 189

    CycleBlob: A WebGL Lightcycle Game 187 The direct approach. Rendering a model that explicitly looks like a grid with holes (see Figure 8-9). This option means that the displayed mesh is a plain, triangle mesh—no tricks or shortcuts. With this option, the edges of the grid are made of normal ...

  • Page 190

    Chapter 8 188 Figure 8-10. The plain quad mesh passes through a pre-processing stage that generates the triangle mesh that’s going to be used for display. Table 8-1. Difference in Data Volume of the Two Grid Models No. of faces No. of vertices Size of model file Input quad mesh 384 386 16.7 ...

  • Page 191

    CycleBlob: A WebGL Lightcycle Game 189 Movement and positioning One of the key characteristics of a computer game is the mechanics of the movement and animation. Coming up with a natural and streamlined look and feel for a game is often as crucial as the basic concept and art design. Indeed, som...

  • Page 192

    Chapter 8 190 Figure 8-11. (a and b) The bike and grid models with their coordinates axis. (c) Drawing both models in the same coordinate system. (d) Drawing with the coordinate base transformation. The coordinate base transformation is a 4 × 4 matrix that we build using the data from the scene ...

  • Page 193

    CycleBlob: A WebGL Lightcycle Game 191 Vector F = (Fx, Fy, Fz): The forward direction of the bike. This is the direction the bike moves in when no turns are taken. It’s a vector tangent to the surface at point P and is also perpendicular to N. (More on how to find this vector to come.) Both N ...

  • Page 194

    Chapter 8 192 Taking a cross product of the vectors induced by two edges of a triangle gives the direction of its normal (shown in Figure 8-12). For a triangle stretched between vertices a, b, and c, the normal would be: N = normalize( (b – a) × (c – a) ) = (b – a) × (c – a) / |(b – ...

  • Page 195

    CycleBlob: A WebGL Lightcycle Game 193 Figure 8-13. Calculating a normal for a vertex (purple) as the average of faces normals (green). If the mesh contains a large variation of triangle sizes, a better approximation is achieved by weighing the average by the area of the triangles. The resulti...

  • Page 196

    Chapter 8 194 The vector F is now exactly perpendicular to N and is situated on the plane created by N and F’. It is better than F’ in that it is tangential to the smooth surface represented by the mesh. Vector R is perpendicular to F and N and, after normalization to the length of 1, is ide...

  • Page 197

    CycleBlob: A WebGL Lightcycle Game 195 Center of view: What part of the scene should be displayed at the exact center of the screen? The obvious answer that comes to mind is that the player’s bike should be there. Although this sounds logical, it may often not be the best choice. The player...

  • Page 198

    Chapter 8 196 up vector is its right-hand side. When the bike moves towards the top of the screen, the up vector is its forward direction. With each turn the player makes in the game, this vector is updated. Taking the camera up vector to always be only the forward direction of the bike would ha...

  • Page 199

    CycleBlob: A WebGL Lightcycle Game 197 Animation engine Smooth animation is a key factor in making a game look good and fun to play. If the animation is choppy or delayed or contains unaesthetic artifacts, the player is going to run out of patience fast, leave, and never return—even if the gam...

  • Page 200

    Chapter 8 198 To clarify this procedure, let’s look at an example. We have indices A and B, which are the vertex indices of the edge the bike is currently on. T is being advanced from 0.95 to 1.1, which means that it passes over vertex B by 0.1 of the distance between A and B and that the bike...

  • Page 201

    CycleBlob: A WebGL Lightcycle Game 199 one of the four neighbors is acceptable. One of the neighbors of B is the vertex we just came from, A, and it can’t be reached again because the bike can’t turn 180 degrees on its axis. Knowing where neighbor A is, we can start going clockwise around B...

  • Page 202

    Chapter 8 200 Defining these sorts of relationships between measurements can become tricky, so it’s useful to establish a common basis for measurement that expresses every measurement. In CycleBlob, the common basis for all length- and size-related measurements is the average distance between t...

  • Page 203

    CycleBlob: A WebGL Lightcycle Game 201 The length of time between updates depends on the hardware being used, the workload of the system and browser at a given time, and many other variables. Setting a constant ∆T assumes all these variables are constant. A value of, say 0.25, makes an implic...

  • Page 204

    Chapter 8 202 Figure 8-16. Possible timelines using setTimout() for frame-rate control. Calling setTimeout() at the end of tick() may cause the complete frame cycle to be longer than the required timeout and cause a frame-rate lower than expected. Another traditionally common option is using se...

  • Page 205

    CycleBlob: A WebGL Lightcycle Game 203 } function tick() { requestAnimationFrame(tick, 16); // do rendering, animation } This approach shifts the responsibility of determining the right frame rate for the game from the programmer to the browser. The browser is almost always more capabl...

  • Page 206

    Chapter 8 204 Of course, a better solution would be to prevent these delays in the first place, but most of the causes for such delays are outside the control of the programmer. One can only hope that as browser technology progresses and WebGL becomes more robustly supported, the guarantees on f...

  • Page 207

    CycleBlob: A WebGL Lightcycle Game 205 A link to the feedback page and a small Facebook “Like” button that is not used often enough by visitors of the site. Finally, a call to webGLStart() that occurs in response to the onload event of the page. This function is called automatically when a...

  • Page 208

    Chapter 8 206 Figure 8-17. The static models of the explosion effect. The spark model is a collection of thin triangles in random lengths and orientation that are all on the same plane. The ring model is a just a round triangle strip. These models are generated in the initialization of the game...

  • Page 209

    CycleBlob: A WebGL Lightcycle Game 207 some 3D content to be displayed. Stopping and restarting the calls to tick() would complicate the logic of the menu system with no consequential performance gains. The tick() function is where most of the real-time game play occurs. The following is an outl...

  • Page 210

    Chapter 8 208 All the efforts invested thus far in loading and generating models, animating them, and lighting them culminate into a single, rendered frame. This function tells WebGL to communicate with the graphics hardware and kick it into action. In most modern machines, this is where the maj...

  • Page 211

    CycleBlob: A WebGL Lightcycle Game 209 References to possible special-effect animation objects that may be occurring for the player. When an object exists here, it receives a call from the global tick() function with the amount of time that elapsed to update its animation state. An optional re...

  • Page 212

    Chapter 8 210 More bonus options. Currently the only bonus in the game is to give the player one more life. Some of the more interesting options I’m considering are bonuses that allow one player to be faster than the others for a period of time, to allow him to break or jump over walls, or even...

  • Page 213

    CycleBlob: A WebGL Lightcycle Game 211 Symbols Definition Q (coordinates) The point to which the bike model is translated to; this point is slightly above P N (vector) The normal vector of the surface where the bike is situated; a vector perpendicular to the surface F (vector) The current forwar...

  • Page 214

    213 Chapter 9 A Real-Time Multiplayer Game Using WebSockets In this chapter, we will walk through the creation of a simple multiplayer game. We will use the HTML5 canvas to display graphics to the user, WebSockets to facilitate communication with our server, and node.js to create the server. We...

  • Page 215

    Chapter 9 214 Philosophy of netcode In a multiplayer game, nothing ruins the experience quite like somebody cheating. Unfortunately, there will always be somebody who tries to cheat at your game. Even more unfortunately, “cheating” in a JavaScript game, such as the one we will soon construct...

  • Page 216

    A Real-Time Multiplayer Game Using WebSockets 215 smooth. This simple idea, simulating the game locally while the real game is being played on the server, is sometimes called “dead reckoning.” Ideally, we can share the code for the game logic between the client side (for responsiveness) and ...

  • Page 217

    Chapter 9 216 With a little bit of clairvoyance, we begin the game engine, game.js, with the list of game properties that we’ll need (see Listing 9-1). These will, in the final product, apply simultaneously to the client and the server. There is certainly virtue in not having two such lists ma...

  • Page 218

    A Real-Time Multiplayer Game Using WebSockets 217 This type of collision detection, where we move the cars regardless of environment and then check for collisions, has some drawbacks. The only serious one that we will consider here is that two cars might pass each other in one time step with no ...

  • Page 219

    Chapter 9 218 // [ Index of first car, Index of second car, X impulse, Y impulse ]. var Impulses = []; for (var i = 0; i < Cars.length; i++) { // Move the cars. X and Y are the coordinates of the center of the car. Cars[i].X += Cars[i].VX; Cars[i].Y += Cars[i].VY; /...

  • Page 220

    A Real-Time Multiplayer Game Using WebSockets 219 // Impulse 1 = -Delta * [ DX, DY ]. // Impulse 2 = Delta * [ DX, DY ]. var DX = Cars[j].X - Cars[i].X; var DY = Cars[j].Y - Cars[i].Y; var Delta = (DX * (Cars[i].VX -...

  • Page 221

    Chapter 9 220 Cars[i].VX *= GP.MaxSpeed / Speed; Cars[i].VY *= GP.MaxSpeed / Speed; } // Friction will act on the cars at all times, eventually bringing them to rest. Cars[i].VX *= GP.FrictionMultiplier; Cars[i].VY *= GP.FrictionMultiplier; } That’s all for the RunGameFrame method. ...

  • Page 222

    A Real-Time Multiplayer Game Using WebSockets 221 </body> </html> Note This HTML file will be changed to point to client-multiplayer.js instead of client-local.js when we revisit the game client in a later section. Now, in client.js, we’ll define a few variables specific to the gam...

  • Page 223

    Chapter 9 222 ); document.addEventListener("keyup", function(E) { if (E.which == 38) KeysPressed &= ~1; // Up. else if (E.which == 37) KeysPressed &= ~2; // Left. else if (E.which == 39) KeysPressed &= ~4; // Right. } ...

  • Page 224

    A Real-Time Multiplayer Game Using WebSockets 223 Now, the only thing missing from this non-multiplayer client is to draw the game. Listing 9-12 is a straightforward exercise in using the canvas. Listing 9-12. DrawGame method function DrawGame() { // Clear the screen GraphicsContext.cl...

  • Page 225

    Chapter 9 224 Figure 9-3. The offline game client created in this section The game server To set up the game server, you will need access to some computer running node.js (actionURI(http://www.nodejs.org):www.nodejs.org). This can be your own computer (there is a Windows port of node.js) or som...

  • Page 226

    A Real-Time Multiplayer Game Using WebSockets 225 Note The WebSocket server add-on modules are sure to change in the future, especially once the WebSocket standard is finalized, but the code we write here will be largely agnostic of the module used. It should be easily adapted to any other modu...

  • Page 227

    Chapter 9 226 Listing 9-16. Setting Up the WebSocket Server // Creates an HTTP server that will respond with a simple blank page when accessed. var HTTPServer = HTTP.createServer( function(Request, Response) { Response.writeHead(200, { "Cont...

  • Page 228

    A Real-Time Multiplayer Game Using WebSockets 227 do { Connection.ID = Math.floor(Math.random() * 100000) } while (Connection.ID in Connections); Connections[Connection.ID] = Connection; Connection.on("message...

  • Page 229

    Chapter 9 228 Size++; return Size; } The event handler for the player disconnecting is very simple. Notice that in Listing 9-18 we directed that event to a function called HandleClientClosure (see Listing 9-19). Listing 9-19. HandleClientClosure method function HandleClientC...

  • Page 230

    A Real-Time Multiplayer Game Using WebSockets 229 try { Message = JSON.parse(Message); } catch (Err) { return; } if (!("Type" in Message && "Data" in Message)) return; // Handle the different types of messages we expect. var C = Connections[I...

  • Page 231

    Chapter 9 230 Notice that we again augmented, for the final time, the list of data stored per car. We’re now keeping track of the user’s handle (username) and it will be displayed on the cars once we revisit the game client. Caution Always perform strict data validation on any input receive...

  • Page 232

    A Real-Time Multiplayer Game Using WebSockets 231 The only remaining piece of server.js is the game loop (see Listing 9-23). We already constructed a game loop in client.js, and this one will be very similar. Listing 9-23. Setting up the Game Loop on the Server // Set up game loop. setInterval(fu...

  • Page 233

    Chapter 9 232 Listing 9-24. New Global Variables for the Client var Socket = null; var GameTimer = null; The first is our WebSocket and the second will be used to stop the game loop if the WebSocket connection is aborted. When the page loads, we’ll ask the player for a handle and then connect ...

  • Page 234

    A Real-Time Multiplayer Game Using WebSockets 233 { // Send a handshake message. // Set up game loop. }; Socket.onmessage = function(E) { // Parse the car data from the server. }; The latter two event handlers require some more detail. When the...

  • Page 235

    Chapter 9 234 Listing 9-28. WebSocket “message” Event Handler Socket.onmessage = function(E) { var Message; // Check that the message is in the format we expect. try { Message = JSON.parse(E.data); } catch (Err) { return; } if (!("...

  • Page 236

    A Real-Time Multiplayer Game Using WebSockets 235 Sending the data is as simple as calling the send() method on the WebSocket object. The WebSocket’s readyState property has the value 1 when it is open and functioning correctly. With those changes, we’re totally online-enabled! One last twea...

  • Page 237

    Chapter 9 236 Figure 9-4. The final game client, with three players online Conclusion WebSockets and the HTML5 canvas open up all sorts of possibilities to developers. These technologies are still brand new, but we should expect to see them really take off in the years ahead. In this chapter, w...

  • Page 238

    A Real-Time Multiplayer Game Using WebSockets 237 Appendix: Setting up node.js Let’s walk through getting node.js up and running. We’ll do it first on Windows and then make some comments about doing it on a UNIX-based system. Windows Download the latest Windows version of node.js from actio...

  • Page 239

    Chapter 9 238 After installing node.js, you will want to install the Node Package Manager (npm), by executing the following command: actionURI(http://npmjs.org/install.sh):curl http://npmjs.org/install.sh | sh and then install the websocket module by running npm: npm install websocket If you ha...

  • Page 240

    239 Chapter 10 Hard-Pressed for a Choice of Technology Browser-based games have been around for as long as there have been browsers, and one can easily retrace the evolution of browsers by following the evolution of browser-based games. At first games were text-based—this was the evolution of ...

  • Page 241

    Chapter 10 240 professionals from Zynga, Facebook, SixToStart, and many other companies serving as judges. My team’s project, FAR7, won the Best Technology category. As the CTO, I would like to discuss the crucial and most complicated stage of development, which is choosing the technology. Why...

  • Page 242

    Hard-Pressed for a Choice of Technology 241 Rendering We should start with the basics, as no game is possible without a graphical representation. In the past, the most widely-used option was simple layers, but since the advent of HTML5, developers have been able to choose between two higher-perfo...

  • Page 243

    Chapter 10 242 Listing 10-1. SVG image source with gradient, animated ellipse, and image <svg version="1.1" width="320" height="320" actionURI(http://www.w3.org/2000/svg):xmlns="http://www.w3.org/2000/svg"> <defs> <r...

  • Page 244

    Hard-Pressed for a Choice of Technology 243 An XML-based language, SVG provides a familiar DOM API on the page for accessing each element. What are the drawbacks of SVG? The main drawback stems from the fourth advantage. The more complex and larger the document, the slower the rendering—as...

  • Page 245

    Chapter 10 244 { if (step == 'in') { w++; h++; } else if (step == 'out') { ...

  • Page 246

    Hard-Pressed for a Choice of Technology 245 ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.setTransform(1, 0, 0, 1, Math.round((canvas.width - w) /...

  • Page 247

    Chapter 10 246 The two-dimensional context has long been supported by every popular browser, with the exception of IE versions below 9.0. Google has created a product called Explorer Canvas, or excanvas, for these older IE versions. Simply by enabling one extra JavaScript file, you get fully fu...

  • Page 248

    Hard-Pressed for a Choice of Technology 247 One of the main challenges of integrating video and audio into browsers is the difference in codec selections in different browsers, which requires us store several copies of content; for example, Ogg Vorbis files for Firefox, Chrome, and Opera, and MP...

  • Page 249

    Chapter 10 248 Connection Most games are sensitive to network latency. We developers are at a crossroads again today, given a choice of several technologies. Let us look at the advantages of each and figure out what to do about obsolete browsers that do not support them. I will provide a little...

  • Page 250

    Hard-Pressed for a Choice of Technology 249 Long polling is the happy medium between simple AJAX and complex HTTP streaming, also known as HTTP server push or Comet (see Figure 10-4). Its key advantage is that the client creates only one connection, which it uses to receive data from the server ...

  • Page 251

    Chapter 10 250 To create multi-line messages, use one \n at the end of each line preceding the last one, and two, at the end of the final line, as shown in Listing 10-6. Listing 10-6. An example of a multi-line message used in JSON transmission data: {\n data: "msg": "hello server...

  • Page 252

    Hard-Pressed for a Choice of Technology 251 Table 10-3. Server-Sent Events: Method of continuously sending data from a server to the browser IE Firefox Safari Chrome Opera Two versions back 7.0 5.0 4.0 12.0 11.0 Previous version 8.0 6.0 5.0 13.0 11.1 Current 7.0 5.1 14.0 11.5 Near future 9.08.0 ...

  • Page 253

    Chapter 10 252 console.log('Server: ' + e.data); }; A study of the protocol’s security in the late November of 2010 revealed that when used with transparent proxy servers, the transmitted data cache can be replaced, and the user will receive a version of the data created by an intruder...

  • Page 254

    Hard-Pressed for a Choice of Technology 253 sent to the server every time because of the way they work. The year 2009 saw the appearance of the Web Storage standard, often referred to as DOM Storage. Web Storage provides a convenient way to store up to 5 megabytes per domain in Firefox, Chrome,...

  • Page 255

    Chapter 10 254 Web fonts Implementation of original fonts on web pages was planned for a long time. In 1995, the HTML2 standard added the tag <font>, which allowed using a font installed on the user’s system, or falling back on a suitable font family if the user did not have any of the r...

  • Page 256

    Hard-Pressed for a Choice of Technology 255 Conclusion We have examined the core stack of technologies used for development of modern browser-based games. It is now apparent that selecting the right combination is no trivial task. Every technology has its pros and cons. To achieve the perfect b...

  • Page 257

    257 IndexA Almost Native Graphics Layer Engine (ANGLE), 139,136 Angry Birds 14,game, 16,9, 11, 68,64, 96,93 Animation engine bike game, 199,197 204,clearInterval()method, 202 199,CycleBlob, 197 main() function, 203,201 mesh pre-processing 200,stage, 198 pre-processing, 201,199 request...

  • Page 258

    INDEX 257,258 Basic game planning (continued) dart drawing. (see Dart drawing) dartboard image textured, 170,167 dartboard variables and buffer initialization, 165,162 drawDartboard 170,function, 167 game, 165,162 165,input, 162 models, 165,162 mouse 173,events, 170–172 parameterized sh...

  • Page 259

    INDEX 258,259 life and death, 121,118–119 main loop implementati 119,on, 116 rendering, 122,119–120 spark 118,designing, 115–116 HTML markup brick type, buttons, 71,67 <canvas> element, 70,66 CSS stylesheet and rules, 72,68 saved 71,tracks, 67 snippets, 70,66 steps, 71,67 Hy...

  • Page 260

    INDEX 259,260 Multiplayer game, WebSockets (continued) 221,bumper.htm, 220 client.js,global variables, 222,221 DrawGame method, 224,223, 236,235 enabled keyboard handlers, 235,234 event handlers, 233,232 final game client, 237,236 game loop on window load, 223,222 handling keyboard 222,...

  • Page 261

    INDEX 260,261 Runfield, 14,9, 16,11 tame the 12,mouse, 7, 13,8 TF2 WebGL, 17,12 uncompiled and open code, 8,3 up-to-date browser, 9,4 W3C, 8,3 WebGL,3D graphics, 10,5 WebRT,OS application, 13,8 WHATWG, 8, 3 write once, use anywhere, 7,2 P PAC-MAN game, 177,175 Particle systems compon...

  • Page 262

    INDEX 261,262 Server-Sent Events (SSE) (continued) simple connection, 250,249 single-line message, 250,249 subscribing 251,message, 250 time-out to 10,5 seconds 251,message, 250 Store retrieving tracks, 91,87–88 save 93,button, 89 saving 90,tracks, 86–87 string-based data 89,structu...

  • Page 263

    INDEX 262,263 movement and positioning 9,4 × 9,4 transformation matrix, 193,191 animation, 196,194 animation engine. (see Animation engine) bike camera relation, 196,194 bike movement, 195,193 camera 196,positioning, 194 center of view and 197,coordinates, 195 coordinate base 192,trans...

  • Page 264

    HTML5 Games Most Wanted Egor Kuryanovich Shy Shalom Russell Goldenberg Mathias Paumgarten David Strauß Seb Lee-Delisle Gaëtan Renaudeau Jonas Wagner Jonathan Bergknoff Brian Danchilla Rob Hawkes http://cncmanual.com/ |||||||||||||||||||||||||||||||||||||||||||||||||||||| www.freepdf-boo...

  • Page 265

    ii HTML5 GAMES MOST WANTED Copyright © 2012 by Egor Kuryanovich, Shy Shalom, Russell Goldenberg, Mathias Paumgarten, David Strauß, Seb Lee-Delisle, Gaëtan Renaudeau, Jonas Wagner, Jonathan Bergknoff, Brian Danchilla, and Rob Hawkes This work is subject to copyright. All rights are reserved by ...

  • Page 266

    CONTENTS iv Contents About the Author ....................................................................................................... 275,xiii About the Technical Reviewer................................................................................... 277,xv About the Cover Image Artis...

  • Page 267

    CONTENTS v There are plenty of good open web games out there ................................................. 14,9 Bejeweled ............................................................................................................... 14,9 14, Angry Birds .........................................

  • Page 268

    CONTENTS vi Embedding a sketch ................................................................................................. 31,26 31, In-line processing...................................................................................................... 32,27 32, Integrating JavaScript .......

  • Page 269

    CONTENTS vii PhoneGap Build........................................................................................................ 56,51 56, Configuration of our chess game .......................................................................... 57,52 57, WebAppBuilder............................

  • Page 270

    CONTENTS viii The Store ..................................................................................................................... 89,85 Saving tracks ............................................................................................................ 90,86 90, Retrieving track...

  • Page 271

    CONTENTS ix Forces ..................................................................................................................... 116,113 116, Renderer................................................................................................................. 117,114 117, System ........

  • Page 272

    CONTENTS x Chapter 7: Beginning WebGL . 136,..................................................................................133 WebGL Origins. 137,...........................................................................................................134 137, How does WebGL work?. 137,.......

  • Page 273

    CONTENTS 274,xi Basic collision detection ...................................................................................... 175,172 175, Scoring ................................................................................................................ 175,172 175, Summary ...............

  • Page 274

    CONTENTS xii The game client, Part 1............................................................................................. 221,220 221, The game server ....................................................................................................... 225,224 225, The game client, Part...

  • Page 275

    xiii About the Authors Egor Kuryanovich is a web developer from Belarus living in Minsk. He goes by the name of Sontan on the web, and his game, Far7, won the Best Technology category of Google’s 2010 Game On competition. Shy Shalom is an Israeli software engineer living in Tel Aviv. While h...

  • Page 276

    ABOUT THE AUTHORS xiv A sought-after speaker, his recent Creative JavaScript / HTML5 workshop series sold out within hours. He co-hosts the Creative Coding Podcast, his blog can be found at seb.ly and he tweets @seb_ly. Gaëtan Renaudeau (aka gre) is a web enthusiast studying towards a master’...

  • Page 277

    xv About the Technical Reviewer Andrew Zack is the CEO of ZTMC, Inc. actionURI(http://www.ztmc.com):(www.ztmc.com), which specializes in search engine optimization (SEO) and internet marketing strategies. His project background includes almost 20 years of site development and project management...

  • Page 278

    INTRODUCTION xvi About the Cover Image Artist Corné van Dooren designed the front cover image for this book. After taking a brief hiatus from friends of ED to create a new design for the Foundation series, Corné worked at combining technological and organic forms with the results now appearing...

Free HTML Books, HTML Book Pdf, HTML Pdf Books, HTML Pdf Books Download, HTML Pdf Books Free Download, Free HTML5 Books, HTML5 Book Pdf, HTML5 Pdf Books, HTML5 Pdf Books Download, HTML5 Pdf Books Free Download, Free ReadOnline Books, ReadOnline Book Pdf, ReadOnline Pdf Books, ReadOnline Pdf Books Download, ReadOnline Pdf Books Free Download,