Echo websocket server using websocketd – as if by magic!

I stumbled across this little gem the other day – websocketd – which turns anything that takes standard-in and standard-out into a websocket server!

To build a dead simple echo server, follow these steps:

  1. follow the download instructions here (don’t forget to add it to your PATH)
  2. create a new console application in Visual Studio (let’s call the console app EchoServer), something like this will suffice:

  1. run this in command line:

and voila!

 

Since we enabled the dev console with the devconsole flag we can now go to http://localhost:8080 in the browser and test out our echo websocket server interactively:

image

pretty sweet, right?

 

One thing to keep in mind though before you consider websocketd for any serious development work, is its implementation details. Based on this paragraph from its project page:

Upon startup, websocketd will start a WebSocket server on a specified port, and listen for connections.

Upon a connection, it will fork the appropriate process, and disconnect the process when the WebSocket connection closes (and vice-versa).

Any message sent from the WebSocket client will be piped to the process’s STDIN stream, followed by a \n newline.

Any text printed by the process to STDOUT shall be sent as a WebSocket message whenever a \n newline is encountered.

in our case this means every time a new connection is establish websocketd will start a new instance of our console app which in practice is unlikely to be what we actually want in a websocket application such as a multiplayer game, and in any production system forking will become really expensive really quickly!

Keep in mind though that it’s still a work-in-progress so the implementation details might change in the future.

 

Links

Having fun with HTML5 – Simple painting app using Canvas

It feels like a little while since I last played around with the <canvas> element, so I spent some time over the weekend and put together a simple painting app using the canvas and here is the end result.

Here’s some screenshots:

image image image

Spray Tool

One of the interesting things with this simple painting app is the spray tool, here’s what I did to achieve the effect:

   1: var _intervalId,    // used to track the current interval ID

   2:     _center;        // the current center to spray

   3:

   4: function getRandomOffset() {

   5:     var randomAngle = Math.random() * 360;

   6:     var randomRadius = Math.random() * radius;

   7:

   8:     return {

   9:         x: Math.cos(randomAngle) * randomRadius,

  10:         y: Math.sin(randomAngle) * randomRadius

  11:     };

  12: }

  13:

  14: this.startDrawing = function (position) {

  15:     _center = position;

  16:

  17:     // spray once every 200 milliseconds

  18:     _intervalId = setInterval(this.spray, 10);

  19: };

  20:

  21: this.finishDrawing = function (position) {

  22:     clearInterval(_intervalId);

  23: };

  24:

  25: this.spray = function () {

  26:     var centerX = _center.X, centerY = _center.Y, i;

  27:

  28:     for (i = 0; i < density; i++) {

  29:         var offset = getRandomOffset();

  30:         var x = centerX + offset.x, y = centerY + offset.y;

  31:

  32:         drawingCxt.fillRect(x, y, 1, 1);

  33:     }

  34: };

The startDrawing function is called when the mouseDown or a subsequent mouseMove event is raised, it in turn sets up an interval event which is triggered every 10 milliseconds and draws one pixel using the currently configured colour within the radius of the mouse pointer.

When the mouseUp event is raised, the interval event handler is unbound.

Saving the Canvas to an Image

One of the cool things you can do with the <canvas> element is the ability to get a data:URL containing a representation of the image as a PNG file, which you can then use as the source for an <img> element so that the user can then save.

The code required to do this is as simple as calling toDataURL() on the <canvas> element and then use the returned value to set the src property on the <img> element.

Demo

You can try out the live demo at http://sketchy.theburningmonk.com or via the shortlink http://lnk.by/fghis.

If you’re interested in the source code, you can get it off github here.

 

Enjoy!

Test your maths skills at the MathDOJO!

Another productive weekend bares fruit for another mini game! This time I’ve put together a dojo-themed mini game whose sole purpose is to test your ability to do simple (well, mostly) arithmetic calculations in your head and your tolerance for my sense of humour (which is not so bad I hope ;-) ).

Screenshots

Here’s some screenshots taken from the game:

image

image

image

image

image

image

image

As you can see, the main game loop is simple: see some dialogue from your opponent, he asks a question, you answer, if you answer correctly within time then your opponent loses health point, when the opponent’s HP is depleted then you’ve defeated him and move onto the next opponent.

So far there’re 7 opponents in the game, each with his own set of dialogues and questions become progressively harder, your score is tracked and you can get a big bonus in score if you answer the questions quickly (and correctly of course!) and manage to string together a succession of correct answers! As the opponents become tougher, their questions will yield greater scores too, and not to mention they’ll be harder to beat as they have a higher HP value which means you have more chance to better your all-time top score!

Game Flow

Without going through all the steps of making the game (maybe I’ll do that at a later date), the basic flow of the game goes something like this:

image

The tricky thing is then how do you control this flow easily so that depending on the state you’re in the right action is triggered when the play presses the ENTER key.

In the end I settled for a mechanism where the DOM‘s onkeydown event is handled by this bit of code:

   1: // hook up the onkeydown event to execute the 'next' action whatever it might be

   2: document.onkeydown = function(event) {

   3:     if (event.keyCode == 13) {

   4:         nextAction();

   5:     }

   6: };

The nextAction variable is updated at each step of the game loop so that when the player presses the ENTER key the game does the right thing. The only deviation from this pattern is when I try to get the answer from a player, because there’s a time restriction on answering the questions I needed to set up a timer so that when it times out it’ll trigger the state transition as well.

Play

You can play the game at:

http://mathdojo.theburningmonk.com

or via the shortened link:

http://LNK.by/fgzcu

Enjoy! Do let me know if you have any suggestions, difficulty level, etc. etc.

Creating a sticky note app with HTML5, CSS3 and Javascript

I saw this tutorial the other day, it’s cool but I fancied taking it a step further and make it useful as an app rather than just a fancy way to show some structured data on the screen. Here’s a list of the features which I wanted to add:

  • being able to edit the title and content of the notes
  • being able to save all your notes
  • being able to add new notes
  • being able to delete notes you no longer want
  • randomize the colour of the notes more

HTML and CSS changes

I initially considered using the new contenteditable attribute in HTML5 to make the title and content editable, but settled on using textarea instead as it offers better support for multiple line text and you can easily add placeholder text as well as set a limit on the length of the title.

I added two images as buttons to add a new note and save the state of all the notes and put a a border around the notes to make them stand out a little more.

Here’s the updated HTML:

   1: <body>

   2:     <div id="controls">

   3:         <img id="btnNew" src="images/newnote.png"/>

   4:         <img id="btnSave" src="images/save.png"/>

   5:     </div>

   6:

   7:     <ul id="notes">

   8:     </ul>

   9: </body>

And the CSS to go along with it:

   1: * {

   2:     maring: 0;

   3:     padding: 0;

   4: }

   5:

   6: .hide {

   7:     display: none;

   8: }

   9:

  10: body {

  11:     font-size: 100%;

  12:     margin: 15px;

  13:     background: #666;

  14:     color: #fff;

  15: }

  16:

  17: #controls img {

  18:     width: 28px;

  19:     height: 30px;

  20:     background: rgba(0, 0, 0, 0);

  21: }

  22:

  23: #controls img:hover, #controls img:focus {

  24:     -webkit-transform: scale(1.2);

  25:       -moz-transform: scale(1.2);

  26:       -o-transform: scale(1.2);

  27:       position:relative;

  28: }

  29:

  30: ul, li {

  31:     list-style: none;

  32: }

  33:

  34: ul {

  35:     overflow: hidden;

  36:     padding: 15px;

  37: }

  38:

  39: ul li {

  40:     margin: 15px;

  41:     float: left;

  42:     position: relative;

  43: }

  44:

  45: ul li div {

  46:     text-decoration: none;

  47:     color: #000;

  48:     background: #ffc;

  49:     display: block;

  50:     height: 150px;

  51:     width: 150px;

  52:     padding: 10px;

  53:     -moz-box-shadow: 5px 5px 7px rgba(33, 33, 33, 1);

  54:     -webkit-box-shadow: 5px 5px 7px  rgba(33, 33, 33, .7);

  55:     box-shadow: 5px 5px 7px rgba(33, 33, 33, .7);

  56:     -moz-transition: -moz-transform .15s linear;

  57:     -o-transition: -o-transform .15s linear;

  58:     -webkit-transition: -webkit-transform .15s linear;

  59:     border-style: solid;

  60: }

  61:

  62: ul li div img {

  63:     padding: 1px 3px;

  64:     margin: 10px;

  65:     position: absolute;

  66:     top: 0;

  67:     right: 0;

  68: }

  69:

  70: ul li textarea {

  71:     font-family: 'Chewy', arial, sans-serif;

  72:     background: rgba(0, 0, 0, 0); /* transparent background */

  73:     resize: none;

  74:     padding: 3px;

  75:     border-style: none;

  76: }

  77:

  78: .note-title {

  79:     font-size: 140%;

  80:     font-weight: bold;

  81:     height: 30px;

  82:     width: 70%;

  83: }

  84:

  85: .note-content {

  86:     font-size:120%;

  87:     height: 100px;

  88:     width: 95%;

  89: }

  90:

  91: ul li:nth-child(even) div {

  92:   -o-transform:rotate(4deg);

  93:   -webkit-transform:rotate(4deg);

  94:   -moz-transform:rotate(4deg);

  95:   position:relative;

  96:   top:5px;

  97: }

  98:

  99: ul li:nth-child(3n) div {

 100:   -o-transform:rotate(-3deg);

 101:   -webkit-transform:rotate(-3deg);

 102:   -moz-transform:rotate(-3deg);

 103:   position:relative;

 104:   top:-5px;

 105: }

 106:

 107: ul li:nth-child(5n) div {

 108:   -o-transform:rotate(5deg);

 109:   -webkit-transform:rotate(5deg);

 110:   -moz-transform:rotate(5deg);

 111:   position:relative;

 112:   top:-10px;

 113: }

 114:

 115: ul li div:hover, ul li div:focus {

 116:   -moz-box-shadow:10px 10px 7px rgba(0,0,0,.7);

 117:   -webkit-box-shadow: 10px 10px 7px rgba(0,0,0,.7);

 118:   box-shadow:10px 10px 7px rgba(0,0,0,.7);

 119:   -webkit-transform: scale(1.25);

 120:   -moz-transform: scale(1.25);

 121:   -o-transform: scale(1.25);

 122:   position:relative;

 123:   z-index:5;

 124: }

 125:

 126: /* define 3 different colours for the notes */

 127: ul li div.colour1 {

 128:     background: #ffc;

 129: }

 130: ul li div.colour2 {

 131:     background: #cfc;

 132: }

 133: ul li div.colour3 {

 134:     background: #ccf;

 135: }

There’s a couple of things to note here:

  • the textarea element has a transparent background
  • the textarea element is not resizable
  • the textarea element has a placeholder text

Here’s what the page looks like now:

image

Adding interactions with Javascript

Adding a new note

In the CSS above, you can see that I had defined three CSS class:

   1: /* define 3 different colours for the notes */

   2: ul li div.colour1 {

   3:     background: #ffc;

   4: }

   5: ul li div.colour2 {

   6:     background: #cfc;

   7: }

   8: ul li div.colour3 {

   9:     background: #ccf;

  10: }

when we create a new note, all we need to do to randomize its colour is to add one of these classes to the element randomly.

Here’s the event handler for the ‘new’ image button:

   1: $(document).ready(function() {

   2:     notes = $("#notes"); // get references to the 'notes' list

   3:     ...

   4:     // clicking the 'New Note' button adds a new note to the list

   5:     $("#btnNew").click(function() {

   6:         addNewNote();

   7:     });

   8:     ...

   9: });

  10:

  11: //  adds a new note to the 'notes' list

  12: function addNewNote(class, title, content) {

  13:     // if class is not specified, use a random colour class

  14:     if (!class) {

  15:         class = "colour" + Math.ceil(Math.random() * 3);

  16:     }

  17:

  18:     // add a new note to the end of the list

  19:     notes.append("<li><div class='" + class + "'>" +

  20:                  "<textarea class='note-title' placeholder='Untitled' maxlength='10'/>" +

  21:                  "<textarea class='note-content' placeholder='Your content here'/>" +

  22:                  "<img class='hide' src='images/close.png'/>" +

  23:                  "</div></li>");

  24:

  25:     // get the new note that's just been added and attach the click event handler to its close button

  26:     var newNote = notes.find("li:last");

  27:     newNote.find("img").click(function() {

  28:         newNote.remove();

  29:     });

  30:

  31:     // hook up event handlers to show/hide close button as appropriate

  32:     addNoteEvent(newNote);

  33:

  34:     // if a title is provided then set the title of the new note

  35:     if (title) {

  36:         // get the title textarea element and set its value

  37:         newNote.find("textarea.note-title").val(title);

  38:     }

  39:

  40:     // if a content is provided then set the content of the new note

  41:     if (content) {

  42:         // get the content textarea element and set its value

  43:         newNote.find("textarea.note-content").val(content);

  44:     }

  45: }

  46:

  47: function addNoteEvent(noteElement) {

  48:     noteElement.focus(function () {

  49:         $(this).find(".img").removeClass("hide");

  50:     }).hover(function() {

  51:         $(this).find("img").removeClass("hide");

  52:     }, function () {

  53:         $(this).find("img").addClass("hide");

  54:     });

  55: }

as you can see, this event handler calls the addNewNote function which is responsible for adding a new note to the notes unordered list and sets its class, title or content depending on the arguments. In this particular usage it’ll pick one of the three colour classes randomly and add the class to the new note.

Once a note has been added, I then get a reference to it and create an event handler for its close button so that when its clicked it’ll remove this note from the DOM.

The addNoteEvent function is then charged with adding the focus and hover in/out event handlers to the note to show and hide the close button so that it’s only shown when the note is focused/hovered on.

Saving the notes

I have previously cover the use of HTML5’s local and session storage in this post already, what I’m doing here is very similar but a little more involved.

When the ‘save’ button is clicked, I need to inspect each of the notes and remember the class, title text (remember, you can’t get the text that’s been entered into the textarea by getting the HTML of the element) and content text in a bespoke object, the resulting array of objects is then JSON serialized using Douglas Crockford‘s Json.js library. It this is JSON string that’s saved into the local storage.

On the other end, when the page has been loaded and the DOM event fired (i.e. when JQuery.ready fires) I will need to check if the JSON string is in the local storage, if it is, then deserialize the string back to an object array and add the notes back in in the same order:

   1: var notes;

   2: var count = 0;

   3:

   4: $(document).ready(function() {

   5:     notes = $("#notes"); // get references to the 'notes' list

   6:

   7:     // load notes from local storage if one's available

   8:     var storedNotes = localStorage.getItem("notes");

   9:     if (storedNotes)

  10:     {

  11:         // passes the stored json back into an array of note objects

  12:         var notesArray = JSON.parse(storedNotes);

  13:         count = notesArray.length;

  14:

  15:         for (var i = 0; i < count; i++) {

  16:             var storedNote = notesArray[i];

  17:             addNewNote(storedNote.Class, storedNote.Title, storedNote.Content);

  18:         }

  19:     }

  20:

  21:     ...

  22:

  23:     // clicking the 'Save' button saves the state of the notes board to the local storage                

  24:     $("#btnSave").click(function() {

  25:         var notesArray = new Array();

  26:

  27:         // for each of the notes add a bespoke note object to the array

  28:         notes.find("li > div").each(function (i, e) {

  29:             // save the class attribute of the div, as well as the text for the title and content text areas

  30:             var colourClass = $(e).attr("class");

  31:             var title = $(e).find("textarea.note-title");

  32:             var content = $(e).find("textarea.note-content");

  33:

  34:             notesArray.push({ Index: i, Title: title.val(), Content: content.val(), Class: colourClass });

  35:         });

  36:

  37:         // json encode it

  38:         var jsonStr = JSON.stringify(notesArray);

  39:

  40:         // and save the json string into local storage

  41:         localStorage.setItem("notes", jsonStr);

  42:

  43:         // info the user it's done

  44:         alert("Notes saved");

  45:     });

  46:

  47:     // add a note to the list if there aren't any

  48:     if (count == 0)

  49:     {

  50:         $("#btnNew").click();

  51:     }

  52: });

Demo

You can try out the full demo at:

http://stickynote.theburningmonk.com

or using the short link:

http://LNK.by/fhbba

Enjoy!

UPDATE 27/02/2011:

Thanks to Joey Beechey for pointing out to me in the comment that the demo doesn’t work in Safari and after a bit of digging I found that the problem is with the addNewNote function which has a parameter named ‘class‘ which is a reserved word. I found another reference of this problem here too, which mentions the same problem but with the variable name ‘abstract‘ instead!

I’ve also put the whole thing into a zip file which you can download from here, so feel free to play around with it yourself!

References:

Douglas Crockford’s JSON-js project on github

John Resig’s blog post which includes some handy examples on how to use the JSON-js library

Original tutorial which inspired this blog post

Having fun with HTML5 – Canvas, part 5

In part 4, I put together the foundations for a mini shooting game, with targets moving on the screen and you can ‘hit’ them by clicking inside the targets, etc. etc. I promised an update to make it feel more like a game, so here it is!

Adding a background

The black background was more than a little dull so I had a look around for some freely available images, and to apply them I had two simple choices:

  • draw the image onto the main canvas on each frame
  • use the image as background to the canvas

I opted for the latter as it seems more efficient, it does mean that I need to change the way I’m clearing the canvas for each frame though. In part 1 of this series I mentioned that by setting the width or height of the canvas element you can erase the contents and reset all the properties of its drawing context, which seems to fit the bill of what I’m trying to do here.

The clear function is therefore changed to:

   1: // clear the canvas page

   2: function clear() {

   3:     // reset the canvas by resizing the canvas element

   4:     canvasElement.width = canvasElement.width;

   5: }

Limit the number of Shots

To make the game more challenging (having an unlimited number of shots is just too easy), I decided to add a limit on the number of shots you can have and after you’ve used up all of them it’s effectively game over.

To show how many shots you have left, I want to show a number of bullets in the top left corner and depending on the number of shots you have left the bullets are shown differently:

  • Five shots or less and you will see a bullet for each
  • More than five shots and you will see a bullet followed by the words x N where N is the number of shots left
  • If no restriction on the number of shots (for time attack mode for instance), you see an infinity sign next to a bullet

To facilitate these requirements, I need to be able to track the number of shots left and decrement its value every time you click inside the canvas element. Thankfully, the structure is there already, just need some small modifications:

   1: // first declare a new global variable

   2: /* GLOBAL VARIABLES */

   3: var WIDTH,              // width of the canvas area

   4:     HEIGHT,             // height of the canvas area

   5:     ...

   6:     bullets,            // how many shots left

   7:     ...

   8:

   9: // then add some more steps to the mousedown handler

  10: $("#canvas").mousedown(function (mouseEvent) {

  11:     // ignore if no more bullets left!

  12:     if (bullets <= 0) {

  13:         return;

  14:     }

  15:

  16:     // hit test and add message, etc.

  17:     ...

  18:

  19:     // deduct the number of shots left if a cap is defined

  20:     if (bullets != undefined) {

  21:         bullets--;

  22:     }

  23: });

Then I need to take care of the business of actually showing them on the screen, luckily it wasn’t too hard to create a basic bullet looking image and an infinity symbol. To make it easier to use them over and over in my code, I added two <img> elements to the HTML (along with a third which I will explain later) but they’ll be hidden:

   1: <div class="hide">

   2:     <img id="target" src="images/target.png"/>

   3:     <img id="bullet_target" src="images/bullet_target.png"/>

   4:     <img id="bullet" src="images/bullet.png"/>

   5:     <img id="infinity" src="images/infinity.png"/>

   6: </div>

Back to the Javascript code, there needs to be an extra step in the draw function to add the bullets to the scene. To encapsulate that logic I pulled it out into its own function:

   1: // redraw the target boards on the canvas

   2: function draw() {

   3:     // as before

   4:     ...

   5:

   6:     // then the bullets

   7:     drawBullets();

   8: }

   9:

  10: // draw the bullets onto the top left hand corner

  11: function drawBullets() {

  12:     // don't proceed any further if no more bullets left

  13:     if (bullets <= 0) {

  14:         return;

  15:     }

  16:

  17:     // define basica X and Y offsets

  18:     var offsetX = 10, offsetY = 10;

  19:

  20:     // draw the first bullet

  21:     context.drawImage(bulletElement, offsetX, offsetY);

  22:     offsetX += bulletElement.width;

  23:

  24:     if (bullets == undefined) {

  25:         // draw the infinity sign next to the bullet with some padding

  26:         context.drawImage(infinityElement, offsetX + 5, offsetY + 5);

  27:     } else if (bullets > 5) {

  28:         context.textBaseline = "top";

  29:         context.textAlign = "start";

  30:         context.fillStyle = "orange";

  31:         context.font = "bold 40px arial";

  32:

  33:         // draw a count next to the bullet, e.g. "x10"

  34:         context.fillText("x"+bullets, offsetX + 5, offsetY);

  35:     }

  36:     else {

  37:         // otherwise, draw the remaining bullets

  38:         for (var i = 1 ; i < bullets; i++) {

  39:             context.drawImage(bulletElement, offsetX, offsetY);

  40:             offsetX += bulletElement.width;

  41:         }

  42:     }

  43: }

And these are the results:

image image image

Looks the part, if I might say so myself :-)

Using a Custom Cursor

Though a good approximate, the crosshair cursor just isn’t quite up to the task here, a scope-like cursor is needed here, like the one I created here:

image

(if you’re interested in how I made the cursor, I used Axialis CursorWorkshop which makes the task simple)

All that’s left is to replace the crosshair cursor in CSS, but still keeping it as fallback:

   1: #canvas

   2: {

   3:     cursor: url(../cursors/scope.cur), crosshair;

   4:     background: url(../images/canvas_background.jpg);

   5: }

A similar change needs to be applied to the HTML:

   1: <canvas id="canvas" class="block" width="800" height="700"

   2:         onSelectStart="this.style.cursor='url(cursors/scope.cur), crosshair'; return false;"/>

After that you’ll start seeing the new cursor when you run the game.

Adding a Bullet Target

Thanks to my wife Yinan for giving me the idea of having bonus targets which are harder to hit but give you extra bullets when they’re hit. You have already seen the HTML changes earlier, and probably noticed that I have switched to using an image rather than drawing the regular target boards by hand..

Much of the changes to support a different type of target happened in the Javascript code, namely the Target class, which is now extended by a new BulletTarget class:

   1: // define the Target 'class' to represent an on-screen target

   2: var Target = new Class({

   3:     initialize: function (x, y, radius, dx, dy) {

   4:         // same as before

   5:         ...

   6:

   7:         // hit the target!

   8:         this.hit = function () {

   9:             deleteTarget(_id);

  10:         };

  11:     }

  12: });

  13:

  14: // a target which awards the player with an extra bullet when hit

  15: var BulletTarget = new Class({

  16:     Extends: Target,

  17:     initialize: function (x, y, radius, bonusBullets, dx, dy) {

  18:         var _bonusBullets = bonusBullets;

  19:

  20:         // initialize Target's properties

  21:         this.parent(x, y, radius, dx, dy);

  22:

  23:         this.getBonusBullets = function () {

  24:             return _bonusBullets;

  25:         }

  26:

  27:         // override the draw function to draw the bullet target instead

  28:         this.draw = function () {

  29:             context.drawImage(bulletTargetElement, this.getX(), this.getY(), radius*2, radius*2);

  30:         };

  31:

  32:         // override the hit function to the player some bullets back 

  33:         this.hit = function () {

  34:             bullets += _bonusBullets;

  35:             deleteTarget(this.getId());

  36:         }

  37:     }

  38: });

The logic of removing a target from the targets array is now moved out of the Target class and the code which generates targets during initialization has also changed to generate a bullet target by a configurable chance:

   1: /* GLOBAL VARIABLES */

   2: var WIDTH,              // width of the canvas area

   3:     HEIGHT,             // height of the canvas area

   4:     ...

   5:     targetElement,      // the target img element

   6:     targetRadius = 50,  // the radius of the regular targets

   7:     bulletTargetElement,        // the bullet target img element

   8:     bulletTargetRadius = 30,    // the radius of the targets with bullet bonus

   9:     bulletTargetBonus = 3,      // the number of extra bullets for hitting a bullet target

  10:     bulletTargetChance = 0.1,   // the chance of getting a bullet target

  11:     ...

  12:

  13: // function to delete the target with the specified ID

  14: function deleteTarget(id) {

  15:     for (var i = 0; i < targets.length; i++) {

  16:         var target = targets[i];

  17:

  18:         if (target.getId() == id) {

  19:             targets.splice(i, 1);

  20:             break;

  21:         }

  22:     }

  23: }

  24:

  25: // Add targets to the game

  26: function createTargets() {

  27:     for (var i = 0; i < 10; i++) {

  28:         if (Math.random() < bulletTargetChance) {

  29:             targets[i] = new BulletTarget(0, 0, bulletTargetRadius, bulletTargetBonus);

  30:         } else {

  31:             targets[i] = new Target(0, 0, targetRadius);

  32:         }

  33:     }

  34: }

Demo

The full demo can be found here, hope you like it, I’ll be adding even more features in the near future so come back later if you’re interested in seeing how far this little game can go!

Related posts:

Having fun with HTML5 – Canvas, part 1

Having fun with HTML5 – Canvas, part 2

Having fun with HTML5 – Canvas, part 3

Having fun with HTML5 – Canvas, part 4