MilkJS, a Simple HTML5 Framework to Quickly Make Games

I'm currently working on my own HTML5 / Javascript game framework. The goal is to make it ridiculously simple to build a game. This means a simple API, and a short code to make a game.

With it you can build 2D games running at 60fps in any browser (desktop and mobile).

I'm calling this new framework MilkJS, and this is a post to present it to you!

Empty Game

Let's see how to create an empty game with MilkJS.

First we need a basic index.html page to load 2 Javascript files: the framework and the game itself. We also need to add a <div id="game">, that's where the game will be displayed.

<!DOCTYPE html>
<html>

  <head>
    <script src="milk.js"></script>
    <script src="game.js"></script>
  </head>
  
  <body> 
    <div id="game"> </div> 
  </body>

</html>

Then we put the empty game code in the game.js file.

// The main scene, a game can have multiple scene
var play = {
  // This function is called once at the beginning
  // That's where we set up the game, display sprites, etc.  
  init: function() {

  },

  // This function is called 60 times per second    
  // It contains the game's logic  
  loop: function() {

  },

  // Add other functions here if needed
};

// Start a 500 by 500px blue game with the play scene
M.ilk(500, 500, play, '#3498db');

That's it! And no need to use a webserver, just drag and drop the index.html file in any browser and you are good to go.

Simple Game

Now let's see how to make a simple game: the player (a white square) tries to pick a coin (a yellow square) with the arrow keys.

You can of course make more complex games with MilkJS, but for a first example let's keep things simple.

Here's the full source code with lots of comments to explain how things work.

// The main scene 
var play = {
  init: function() {
    // Create 2 sprites: the player and the coin
    // Params: color, x, y, width, height
    this.player = M.rect('#fff', M.w2, M.h2, 50, 50);
    this.coin = M.rect('#ff0', 400, 400, 20, 20);

    // Add a text in the top left corner 
    // The '@' will be automatically replaced by the score
    // Params: text, x, y, color, size
    this.score = M.text('score: @', 20, 20, '#fff', 24);
  },

  loop: function() {
    // Call the hit function when the player touches the coin
    M.overlap(this.player, this.coin, this.hit);

    // Move the player around with the arrow keys
    // Params: horizontal speed, vertiacal speed
    this.player.move(5, 5);

    // Change the scene if we get 5 points
    if (this.score.i == 5) M.scene(win);
  },

  // Function called when the player hits the coin
  hit: function(player, coin) {
    // Emit a bunch of yellow particles
    // Params: color, x, y, size, count, lifetime, speed
    M.particle('#ff0', coin.x, coin.y, 5, 30, 400, 200);

    // Emit a sound of a frequency of 400Hz for 200ms
    M.sound(200, 400);

    // Make the player grow slightly for 200ms
    player.tweenTo(200, { scale: 1.25 }).yoyo();

    // Change the x and y position of the coin
    coin.set(M.rand(0, M.w), M.rand(0, M.h));

    // Increase the score by one and update the label
    this.score.i++;
  },
};

// The win scene
var win = {
  init: function() {
    // Text displayed at the center of the screen
    M.text('YOU WIN!', M.w2, M.h2, '#fff', 40);

    // Make the screen shake for 1000ms
    M.shake(1000, 4);
  },
};

// Create a 500 by 500px game with the play scene
M.ilk(500, 500, play, '#3498db');

Pretty simple to understand, right?

Comparison

Here's a comparison with an other HTML5 framework. With Phaser, the exact same game is made in 60 lines of code. With MilkJS it's just 28 lines once you remove the comments:

var play = {
  init: function() {
    this.player = M.rect('#fff', M.w2, M.h2, 50, 50);
    this.coin = M.rect('#ff0', 400, 400, 20, 20);
    this.score = M.text('score: @', 20, 20, '#fff', 24);
  },
  loop: function() {
    M.overlap(this.player, this.coin, this.hit);
    this.player.move(5, 5);
    if (this.score.i == 5) M.scene(win);
  },
  hit: function(player, coin) {
    M.particle('#ff0', coin.x, coin.y, 5, 30, 400, 200);
    M.sound(200, 400);
    player.tweenTo(200, { scale: 1.25 }).yoyo();
    coin.set(M.rand(0, M.w), M.rand(0, M.h));
    this.score.i++;
  },
};

var win = {
  init: function() {
    M.text('YOU WIN!', M.w/2, M.h/2, '#fff', 40);
    M.shake(4, 1000);
  },
};

M.ilk(500, 500, play, '#3498db');

Phaser is a lot more powerful and flexible, but MilkJS has a much simpler and shorter code. That's why MilkJS is great to quickly prototype game ideas.

Here's how MilkJS manages to be shorter than all other game frameworks:

The API

I've spent weeks (or months?) fine tuning the API, to make things really simple and powerful. Here are two small examples of this.

It's really easy to group objects together:

// Create a group
// Params: x offset, y offset
var g = M.group(M.w2, M.h2);

// Change the group properties (alpha and tween rotation)
g.alpha = 0.9;
g.tweenBy(1000, { angle: 90 }).loop();

// Easily create objects in the group
g.rect('#fff', 0, 0, 50, 50);
g.text('Hello', 0, 50, '#fff', 24);

And you can create levels with tilemaps like this:

 // The level stored in an array
var level = [
  'xxxxxxxxx',
  'x       x',
  'x  ooo  x',
  'x       x',
  'xxxxxxxxx',
]; 

// Create the tilemap
// Params: array, x, y, size of each tile
var map = M.map(level, 45, 45, 40);

// Create and display the tiles
// Params: character, color, attributes
var wall = map.tile('x', '#fff');
var coin = map.tile('o', '#ff0', { scaleX: 0.5 });

// Make each coin rotate
coin.each(function(e) {
    e.tweenBy(500, { angle:90 }).loop();
});

You can see that with a small amount of code you can get great results.

Conclusion

So what do you think? Would you like to make games with this new framework? If so, I will try to fix the last few bugs and put the code on GitHub.

If you want to know when MilkJS will be available and be notified when I write tutorials about it, just join the newsletter below. No spam and unsubscribe at any time.

Get MilkJS news