Fire phone represents an incredible opportunity for HTML5 developers to create new experiences. Developers now have direct access to the hardware sensors that power Fire’s Dynamic Perspective, which opens up all kinds of new ways to let users interact with your web app content. Just like native devs, Fire phone’s four dedicated cameras offer x, y and z data that you can access directly to determine where the player’s head is looking at the screen, and shift the perspective to reveal new angles on what would have otherwise been a flat web page.
Over the course of this tutorial, we will walk through how to create a simple splash screen for a game in Phaser (a popular open source game framework). We’ll also cover how to take advantage of the ability to run web apps next to native ones on the Fire phone and how to add Dynamic Perspective to create a parallax layering effect which gives the scene more of an immersive experience. Before we get started, let’s take a quick look at the splash screen we’ll be creating:
Here you can see a standard splash screen for a game where the main player will float up and down, giving the scene some sense of motion. The start text also blinks to let the player know what action they should take.
To start, download my Phaser Project Template from https://github.com/gamecook/phaser-project-template. You will need to have the following setup on your computer:
This project will give you everything you need to run our project and has step-by-step instructions on how to get everything configured to run locally. Once you have everything set up, rename the Phaser Project Template to DynamicPerspectivePhaserDemo, then navigate into it via the command line. You’ll want to run the following command to get everything configured:
> npm install
Once that is done running, you should be able to launch the project by typing in the following:
> grunt
This will start a local server and open up your browser to http://localhost:8080.
Now we have everything up and running to build our Phaser splash screen. Just download the artwork from here. You’ll need to create an assets folder inside of the deploy directory of the Phaser Project Template. Once that is created, put all of the artwork you just downloaded into it.
One note: if you are using WebStorm, you will want to exclude the deploy/js folder from the Directories setting so you don’t have any performance issues as our grunt script automatically rebuilds the JS code for us.
Step 1. Let’s create a clean slate to work from by opening our main.js file in the src/game folder of the Phaser Project Template. Once you have it open, simply delete all the boilerplate code in that file.
Step 2. Next we are going to create our new state object and Phaser game instance from scratch by typing out the following:
var state = { preload: function () { }, create: function () { }, update: function(){ } } // Create new game instance var game = new Phaser.Game( 800, 480, Phaser.AUTO, 'game', state )
Step 3. Once you have all the artwork in your assets folder, we can add it to our preload function:
preload: function () { this.load.image("mask", "/assets/background-mask.png"); this.load.image("background", "/assets/background-image.png"); this.load.image("title", "/assets/title.png"); this.load.image("start", "/assets/start-text-touch.png"); this.load.spritesheet("player", "/assets/player-sprites.png", 385, 412); }
Here you can see we are simply loading everything up as an image with the exception of the player, which will be animated. That means we can use the dedicated sprite sheet loader.
Step 4. In our create() function, let’s setup the scaleMode to give us full screen on different device resolutions:
create: function () { this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL; this.scale.pageAlignHorizontally = true; this.scale.pageAlignVertically = true; this.scale.setScreenSize(true); ...
Step 5. We can start blocking out how the scene will be laid out. Inside of our create() method, add the following:
// Calculate center point for laying scene out var centerOffset = {x: (this.world.width - 679) * .5, y: (this.world.height - 466) *.5}; // Create background image this.background = this.add.image(centerOffset.x, centerOffset.y, "background"); // Save bg position this.bgPos = this.background.position.clone(); } // end create
This will calculate where we will center the background image. We’ll also save out the value for the background in order to use it as a reference point later on.
Step 6. If we refresh the browser, we’ll see our first glimpse at how the splash screen will look:
Step 7. Let’s get back into our code now and add in the player as well as the title and other elements that will help complete the scene. In our create() function. Below the background code we just added, put the following code:
create: function () { ... // Create player this.player = this.add.sprite(this.bgPos.x + 20, this.bgPos.y + 20, 'player'); this.player.animations.add('fly', [0, 1, 2, 3], 10, true); this.player.animations.play("fly"); this.playerStartPos = this.player.position.clone();
Here you can see we are setting up our player as well as creating and setting a fly animation. We also save out our player’s start position, which we will use later on when we start modifying it based on the Dynamic Perspective code we’ll add for Fire.
Step 8. Now we can wrap this up with the last bit of layout code:
// Add mask, title and start images this.mask = this.add.image(this.background.position.x-50, this.background.position.y + 280, "mask"); this.title = this.add.image(this.bgPos.x + 370, this.bgPos.y+295, "title"); this.start = this.add.image(this.bgPos.x + 405, this.bgPos.y+385, "start"); } // end create
Here we add the background mask, the title and the start text.
Step 9. If you refresh the browser you will now see our scene:
While this is really easy to setup, it looks a little boring. Let’s add some animation to it.
Step 10. In our update() function, add the following code:
// Make player float up and down this.player.y = (this.playerStartPos.y / 2) + 8 * Math.cos(this.time.now / 200) + 20; // make start text blink this.start.visible = Math.round(this.time.now/500) % 2 ? true : false;
This code will make the player float up and down as well as make the start text blink. You can test this by refreshing the browser again.
At this point, we have our entire splash screen laid out. Let’s talk about how to set up the Web App Tester on Fire phone to get our splash screen ready for adding the Dynamic Perspective APIs.
Just like you can currently do on the Kindle Fire, you’ll need to download the Web App Tester on the Fire phone for this next part of the tutorial. You can get the latest build from the Amazon Appstore here.
Step 11. Once you have the Web App Tester installed, you’ll want to look up your computer’s IP address.
Step 12. When you have the IP address, you can enter that into the URL field and save it.
Step 13. Now you can pull up the Amazon WebView to make sure everything is working. Once it’s loaded, you should see the splash screen running at full screen on the device. One thing to note is that we are not locking the rotation in this simple demo, so make sure you are holding the phone in landscape. Here is the splash screen being rendered at full 720P resolution on the Fire phone:
As you can see, the process for installing and testing hosted web apps on Fire phone is straight forward. We’ll be using the Web App Tester and Fire phone for the remainder of our tutorial to make sure we can properly test the Dynamic Perspective APIs.
The good news is that the Dynamic Perspective APIs are built into the Amazon WebView we’ll be testing within the Web App Tester. That means that you don’t have to add in any extra JavaScript libraries to start using Dynamic Perspective in your own HTML5 apps and games, it’s ready for you right out of the box. Let’s get started:
Step 14. We are going to want to make some global variables to store the motion tracking data points we’ll be using in this example. At the very top of our project above where we declared our state object, add the following:
var dpX = 0; var dpY = 0; var faceDetected = false; var scale = 2;
Here we are going to store the x, y values for the Dynamic Perspective x and 7 axis, whether facial detection is working, and a scale factor to help us manipulate the raw data into something more manageable.
Step 15. Now at the very end of our create() function add the head tracking event listener:
addEventListener('amazonheadtracking', this.handleAmazonHeadTracking);
As you can see, we’ll be using a standard event listener to implement the Dynamic Perspective APIs in our web app.
Step 16. After our create() function, add the following:
handleAmazonHeadTracking: function(event){ dpX = event.x_mm/scale; dpY = event.y_mm/scale; faceDetected = event.isFaceDetected; },
This will save the head tracking position values into our global variables that we set up previously. You’ll also see that we are dividing it by our scale to reduce the value a little so we don’t see large x, y values shifts since the original artwork is being scaled up to 1280 x 720 from 854 x 480. Remember that the data returned is relative to the size of the phone’s renderable screen area - not your game - if it’s up-scaled like we are doing here. You may want to modify these factors by the games scale factor instead. Here I am simply dividing them in half.
Step 17. Now let’s calculate the new position we’ll use to apply to each of our layers. Add the following to the top of our update() function:
var newPositionY = Phaser.Math.clamp(dpX, -30, 30); var newPositionX = Phaser.Math.clamp(dpY, -30, 30); this.background.x = this.bgPos.x + newPositionX/5; this.background.y = this.bgPos.y + newPositionY/5;
Here we are using a method called clamp() which is part of the Phaer.Math lib. We supply our Dynamic Perspective x and y values along with a limit so it stays within the specified range. One thing to note is that since this splash screen is designed to run in landscape we need to swap the Head Tracking x and y values since they don’t change based on the phone’s orientation.
Now if you run the game on Fire phone and look at the device as well as move it around, you’ll see the background now subtly shifts in response to your head movement.
Step 18. Let’s add the same effect to our player. You’ll want to replace the current this.player.y line of code where we calculate the floating animation with the following:
this.player.x = this.playerStartPos.x + newPositionX; this.player.y = (this.playerStartPos.y / 2) + 8 * Math.cos(this.time.now / 200) + (20 + newPositionY);
Now we are modifying the player’s x position and adding the newPoistionY to the end of where we calculate the up and down value to create some additional movement along the Y position. You may also notice that we are not dividing the new x and y values by 5 like we did in the background. This allows us to give the layers a parallax effect so that the background will move at a slower rate.
Step 19. Run the scene again on Fire phone and you’ll see the final effect.
If you have previously published a web app to the Amazon Appstore, you can follow the same process. For those who are new to this, there are 3 easy steps after you have set up your free developer account and specified that you want to create a new app in the portal:
Step 1: Verifying Your Web App’s URL
You can now validate your web app’s URL right from the developer portal.
Simply put in the URL for your web app or game with the Fire phone code and click the verify button and the tool will let you know if the contents of the URL pass the preliminary submission requirements.
Step 2: Declaring Web App’s Permissions
Once your URL has been verified, you can select your app’s permission. Simply check off the options that apply to your app.
Step 3: Select Compatible Platforms
Then you can define which devices this web app can be published on.
While the Kindle Fire HD, HDX and Fire phone devices offer the best performance for web apps, make sure you test your web app on older devices to ensure the performance is ideal for your users. Intensive web games and anything using WebGL should be distributed on Kindle Fire HD and above.
While you can install web apps in the Amazon Appstore on any Android device that has the store installed, it will default to the native WebView on that device. This means that your app will not have access to the Dynamic Perspective APIs on other Android phones.
Step 4: Certification of Distribution Rights
Finally, certify that have the necessary rights to publish your web app.
As you can see, not only can you easily publish HTML5 apps and games alongside native Android apps through the Amazon Appstore, you also get access to the Fire’s Dynamic Perspective API just like native Android apps do. So be sure to submit your app to the Amazon Appstore to tap into millions of potential new customers.
Related links:
- Jesse Freeman (@jessefreeman)
Welcome to part 3 and the final installment, of my Introduction to Phaser. In the first post, we configured our dev environment as well as the code we’ll need to run Phaser itself. In the second post, we covered preloading, displaying sprites and setting up the game modes. If you missed the two tutorials, please go back and follow those steps before moving on. In this tutorial we are going to cover the following:
Let’s get started.
Right now our game isn’t very fun or challenging. To help with this we’ll add walls with a small gap in the middle for our player to fly through. To get started, we’ll need to create two functions: one to spawn a wall and the other to put two walls together with the gap for the player to fly through.
Step 1. Add the following method to our state object:
spawnWall: function(y, flipped){ }
This function accepts the y position for the wall and if it needs to be flipped vertically. This is important since we will be using the same wall graphic for our top and bottom wall sprites. We’ll need a way to group them together into a single manageable game object.
Step 2. In our create() method, right under where we setup the background TileSprite we should add our wall group:
create: function(){ this.background = this.add.tileSprite(0,0, this.world.width, this.world.height, 'background'); this.walls = this.add.group();
It’s important to create this after our background so it shows up on top. Now we’ll be able to access each of the wall sprites in our game from a single group which we are calling walls. This will make a little more sense later on when we add in collision.
Step 3. Add the following to our spawnWall() method:
spawnWall: function(y, flipped){ var wall = this.walls.create( game.width, y + (flipped ? -OPENING : OPENING) / 2, 'wall' ) }
This adds a new wall sprite to our walls group. The staring x position is set to the width of the game so it starts off-screen and we also use the supplied flipped value to detect if the wall going to be displayed on the top or the bottom. To do this we take advantage of something called a Ternary Operator. Basically the way that this works is we have a condition to the left of the question mark and then the value if true followed by the value if the condition is false. Here is a representation of this:
condition ? true : false;
Step 4. Add the following constant to the top of our script:
var OPENING = 200;
Step 5. Add a few more lines of code to our spawnWall() function:
this.physics.arcade.enableBody(wall); wall.body.allowGravity = false; wall.scored = false; wall.body.immovable = true; wall.body.velocity.x = -SPEED; if(flipped){ wall.scale.y = -1; wall.body.offset.y = -wall.body.height; }
This disables gravity for the wall. Then, we set the y scale to -1 if it’s flipped, or 1 if it is staying the same. We also need to adjust the y offset value of the body if the wall is flipped to account for the scale change we made on the previous line. Finally we set the velocity.x of the wall to our SPEED constant. You’ll notice we make this value negative. That way it moves to the left of the screen giving the impression that the player is flying past it.
Step 6. Add the following line to the end of the spawnWall() function:
return wall;
This will allow us to get a reference to the wall instance being created every time we call the spawnWall() method. We need to test if this works, but we’ll need to add a little bit of debug code first.
Step 7. Add the following line of code to our create method:
this.spawnWall(300);
Step 8. Refresh the game in your browser and now you should see your first wall fly by below the player:
Step 9. To test creating a wall on the top of the screen, simply modify the wallSpawn() function to look like this:
this.spawnWall(300, true);
Step 10. With true being passed into wallSpawn() you can refresh the game to see the new wall on the top of the screen:
At this point we have the foundation in place for us to spawn the walls in our game. We’ll create a method to generate both a top and bottom wall as well as connect it up to a timer.
Step 1. Before moving on, make sure you comment out the spawn code we used to test with in the previous section:
//this.spawnWall(300, true);
Step 2. Now add new method called spawnWalls() with the following:
spawnWalls: function(){ var wallY = this.rnd.integerInRange(game.height *.3, game.height *.7); var botWall = this.spawnWall(wallY); var topWall = this.spawnWall(wallY, true); }
Take note that we added a s to this function’s name since our spawnWall() function only generates a single instance. In the spawnWalls() function we are taking advantage of Phaser’s RandomDataGenerator class. You can access it from the game object by calling this.rnd. This utility has a lot of useful methods for helping create random values in your game. We’ll be taking advantage of integerInRange() which requires a minimum and maximum value to return something in-between the two. Finally to keep the walls within the screen area we calculate the “safe” zone were we would want our walls to spawn by taking a percentage from the top and bottom of the visible area.
Step 3. Now we are ready to start a timer to spawn new walls. In our start() function, add the following code:
this.wallTimer = this.game.time.events.loop(Phaser.Timer.SECOND * SPAWN_RATE, this.spawnWalls, this); this.wallTimer.timer.start();
Step 4. We’ll also need to add a new constant at the top of our script:
var SPAWN_RATE = 1.25;
Step 5. Refresh your browser and you can now fly through an endless supply of walls being randomly generated at a set interval.
Everything is looking good but there is just one minor issue. While you may not see it, the walls that have moved off the screen are still in the game. We need to do a little bit of cleanup to make sure that we destroy any wall that is no longer visible.
Step 1. Add the following code to our update function right after we test if player is out of bounds:
if (this.player.body.bottom >= this.world.bounds.bottom) { this.setGameOver(); } this.walls.forEachAlive(function (wall) { if (wall.x + wall.width < game.world.bounds.left) { wall.kill(); } })
Remember back to when we set up our walls in the group? This forEachAlive loop allows us to easily iterate through all the wall instances inside of that group still active in the game. From there we setup a generic function to test if the instance’s x position is greater than the game world’s bounds.left value so we can destroy it. Calling kill() on an instance completely removes it from the game and from memory as well. Without this function, the game would have a memory leak which would cause performance issues and a poor user experience. Phaser also has some really great pooling classes but for this intro level tutorial we’ll just do this manually. The game is simple enough not to worry about the extra overhead we incur by creating new wall instances from scratch.
Step 2. In the reset() function of our state object add the following line:
this.walls.removeAll();
This ensures we get a clean slate when starting the game over. And while we are on the topic of starting over, you may have noticed that the walls still scroll by when the game is over?
Step 3. Next, we’ll need to stop the walls from moving when we end the game. Add the following to our setGameOver() function:
this.walls.forEachAlive(function (wall) { wall.body.velocity.x = wall.body.velocity.y = 0; }); this.wallTimer.timer.stop();
This function will iterate through each wall instance and set the velocity.x to 0 to stop it from moving. We also stop the wallTimer to make sure nothing else gets created while the game is over.
Step 4. Run the game and make sure that the walls stop when game over before continuing on.
Now that we have walls, we can define what happens when a player hits a wall. Luckily for us, collision detection is really easy to implement in Phaser.
Step 1. To get started we’ll need a way to detect the actual collision. To do this, we add one line of code to our update function right after where we test if the player hits the bottom of the screen:
if(!this.gameOver){ if(this.player.body.bottom >= this.world.bounds.bottom){ this.setGameOver(); } this.physics.arcade.collide(this.player, this.walls, this.setGameOver, null, this); }
This tells the physics engine to test for any collisions between two game objects or in this case a single object, the player, and the walls group. The next parameter is the callback the collision check should trigger if an overlap is detected. Finally, we need to pass the game’s scope to the callback parameter.
Step 2. At this point, everything else is already setup for us to build the game over state. Now if you test the game, you’ll see the game ends when the player touches the walls.
Step 3. When a collision occurs you may notice that the player actually bounces back. Add the following to the setGameOver() function to fix that:
this.player.body.velocity.x = 0;
Step 4. Run game and you’ll see the player no longer bounced back because we set the velocity.x to 0 when the game is over.
In this section, we learn how to add a function that increases the score when a player flies through the walls. To get started, we are going to need a way to determine if the player has flown past a wall, and then give them a score for it.
Step 1. We’ll need to add a new function called addScore() to handle the collision callback:
addScore: function (wall) { wall.scored = true; this.score += .5; this.scoreText.setText("SCORE\n" + this.score); }
Here you can see we are getting a reference to a wall instance, adding a score and updating the score text. One thing to keep in mind is that since we have 2 instances, a top and bottom, that make up a single wall we only add half a point to the score. As the player flies past each wall section they will add up to a single value of 1 and the player will not even notice.
Step 2. Now we need to add in the logic to call addScore(). In the forEachAlive loop within the update() function, add the following:
this.walls.forEachAlive(function (wall) { if (wall.x + wall.width < game.world.bounds.left) { wall.kill(); } else if(!wall.scored && wall.x <= state.player.x){ state.addScore(wall); } })
Step 3. Now you can refresh the browser and as you fly through each section of wall you’ll see the score increase by 1.
We now have a functional game. The only thing missing is fun – we need to add some sound effects to round out the playing experience.
Step 1. Before we can access any sound effects we’ll need to load them up. Add this to our preload method:
this.load.audio("jet", "/assets/jet.wav"); this.load.audio("score", "/assets/score.wav"); this.load.audio("hurt", "/assets/hurt.wav");
Step 2. Next we’ll create variables for each of our sound effects so we can access them easier. Add this to our create method:
this.jetSnd = this.add.audio('jet'); this.scoreSnd = this.add.audio('score'); this.hurtSnd = this.add.audio('hurt');
Step 3. Now it’s time to play our sound. In jet() function right after we set the player’s velocity.y to our jet constant add the following:
if(!this.gameOver){ this.player.body.velocity.y = -JET; this.jetSnd.play();
Step 4. Now if you run the game and make the player fly, you should hear the sound. One thing to keep in mind is that each browser supports specific sound codecs. If you have any issues running the sound effects, make sure you are in Chrome or a browser that supports .wav playback.
Step 5. Next we’ll want to let the player know when they score. To enable that, we need to add the following script to our addScore() function:
this.scoreSnd.play();
Step 6. Add this to the end of our setGameOver() method to let the player know when they hit a wall or go out of bounds:
this.hurtSnd.play();
Step 7. Now that we have the rest of our sound effects integrated, we can refresh the browser and test. You should now hear sounds when the player flies up, scores and dies.
At this point the game is basically done, and hopefully you’ve learned how some of the core mechanics of Phaser work.
To recap, we have covered importing assets, creating sprites, managing collision detection and playing sounds. There are a lot of really great resources out there to help you as you dig deeper into the Phaser framework. Make sure you check out the documentation as well as the samples included with the source code. Also, feel free to take my existing codepen project and fork it to create your own variation of it.
One last thing I wanted to highlight is just how easy Amazon’s Web App tools are to use, helping to make publishing an online HTML5 game in the Amazon Appstore simple. I’ll be talking about how to take any HTML5 game and submit to the Amazon Appstore in a future post but the process couldn’t be easier. Here are the steps:
To learn more about the process, check out the documentation on our developer portal and the following reference links:
Related links:
- Jesse Freeman (@jessefreeman)
Welcome to part two of my Introduction to Phaser. In the first part we configured our dev environment as well as the code we’ll need to run Phaser itself. If you missed the first tutorial, please go back and follow those steps before moving on. In this tutorial we are going to cover the following:
Let’s get started.
Phaser has a built in preloader library. Over the course of this tutorial we will be loading in images, sprite sheets and audio files that will be accessible globally within to our game.
Step 1. If you are building your project locally with NodeJS or Apache you’ll need to copy over all of the assets (click here to download) and put them inside of the deploy folder like so:
You do not need to follow this step if you are using CodePen since the same files are being hosted online.
Step 2. Now let’s add the following to the empty preload function we created in our state object. The code bolded below represents what you need to actually add since we have already defined the preload function itself:
preload: function(){ this.load.image("wall", "/assets/wall.png"); this.load.image("background", "/assets/background-texture.png"); }
If you are using CodePen, you will need to preface the urls with http://games.jessefreeman.com/wp-content/public/workshop/phaser/ in order for this to correctly load. As an example, to load in the wall.png you will need the following URL:
http://games.jessefreeman.com/wp-content/public/workshop/phaser/assets/wall.png
Remember to do this for any external asset we preload in our game.
Before we move on, let’s talk about what is happening here. First, you’ll notice that we are calling Phaser’s built in game methods via the this scope. That’s because when our state is loaded up it assumes the same scope as the game object itself. This will allow us to call all of Phaser’s built in methods directly. Next, there are two parts to our load.image call, the unique id of the asset itself and the path to the asset so Phaser knows where to load it from.
Step 3. Now we will need to load in our player sprite sheet. Sprite sheets can be vertical, horizontal or even a grid. The only thing to keep in mind is that each sprite will need to be the exact same width and height in order for this to work correctly. This is what the sprite sheet looks like:
To correctly load this into our game we’ll need to add the following code below where we loaded the first two images:
this.load.spritesheet("player", "/assets/player.png", 48, 48);
This is similar to our load.image request except we have to define the width and height of the sprite. This allows Phaser to have the correct dimensions it needs to cut up our sprites into separate frames for animating later on. Phaser also supports loading texture atlases as well if you prefer to package all of your artwork into a single graphic but we will not be covering that in this tutorial.
Step 4. To test that this is working correctly, go back to your browser, refresh the page and inside of network connection debug tool you should now see that all of the game’s assets are loading correctly.
Now that we have our images, let’s look into how we can display them on the screen.
Adding images and sprites to the display is very easy in Phaser. To get started we are going to create our game’s background then make it scroll before adding in the player.
Step 1. Add the following to the create() function
this.background = this.add.tileSprite(0,0, this.world.width, this.world.height, 'background');
Here we are setting up a new background variable and assign it a TileSprite by calling this.add.tileSprite. The method requires x, y, width, height and label values. Since this texture will take up the entire screen, we are just going to set its x and y value to 0,0 and the width and height to the world’s width and height.
Step 2. Add the following constant to the top of our main.js file:
var SPEED = 200;
This SPEED value will now be a global in our game and help keep everything in sync.
Step 3. Now add the following code right below where we defined our background in the create() method:
this.background = this.add.tileSprite(0,0, this.world.width, this.world.height, 'background'); this.background.autoScroll(-SPEED,0);
Step 4. At this point we have enough to see the background scrolling by based on our SPEED constant. Refresh the browser to see the background scroll.
Step 5. Next, add the following constant, which will represent the gravity in our game:
var GRAVITY = 900;
Step 6. Add the following to the top of the create() function to setup the physics engine:
this.physics.startSystem(Phaser.Physics.ARCADE); this.physics.arcade.gravity.y = GRAVITY;
Step 7. Now add the following to our create() method below our background texture:
this.player = this.add.sprite(0,0,'player'); this.player.animations.add('fly', [0,1,2], 10, true); this.physics.arcade.enableBody(this.player); this.player.body.collideWorldBounds = true;
Here we are creating a sprite instance from the sprite sheet image we previously loaded. Once we set the player sprite up we are going to create our fly animation. We do this by simply calling animations.add on the sprite itself and supply: a label for the animation, the frames, the amount of milliseconds between each frame and if it should loop.
Step 8. Refresh the browser and now you should see the players animate, and it should fall to the bottom of the screen due to gravity:
In most physics engines, a physics body represents the actual object collision area. It’s easy to define a downward pull by adding gravity to the y value of the player’s body object. Also, by setting the player.body.collideWorldBounds to true we tell the physics body that it should be constrained by the boundaries of the game’s world so the player doesn’t fall off the screen.
Now that we have a player and our background, let’s get started adding in a way to play our game. As we build this out, we’ll be taking advantage of the single state of our Phaser game so we’ll need to set up variables to represent the game modes to let us know when the it has started and when it is over. In order to do this we’ll need to create a function that will reset all of our games values first.
Step 1. Add the following function to our state object
update: function(){ }, reset:function(){ this.gameStarted = false; this.gameOver = false; this.score = 0; }
It’s important to note that you need to add a trailing comma to the previous function as you continue to add new functions to the game’s state object so we don’t get an error.
Step 2. Let’s add a call to reset()at the end of our create() function like so:
this.reset();
Now we have a convenient way to reset all of our game values at anytime by calling the reset() function. Not only will this help us get the game ready in the create() function but later on in the when we want to restart the game.
Step 3. Now we need to do a few things in order to build out our intro loop. Add the following to our reset() function:
this.player.body.allowGravity = false; this.player.reset(this.world.width / 4, this.world.centerY); this.player.animations.play('fly');
Here you can see that we are turning off our player’s gravity, resetting his position and setting the animation to fly.
Step 4. Now that our player in the correct position for the intro loop, let’s move the background.autoScroll() out of our create() function and put it into the reset() function with the following modification to the x value:
reset:function(){ this.background.autoScroll(-SPEED * .80 ,0);
Here you’ll notice that we are now to multiplying the SPEED constant by .80 which will slow it down a bit and give us a nice little parallax effect when we add in the walls later on in the tutorial.
Step 5. At this point you can refresh the browser to see the player stays in one place and the background is scrolling a little slower:
Step 6. Now we will need to start building in the logic to differentiate between the attract loop and game play. Let’s add the following to the update() function:
update: function(){ if(this.gameStarted){ }else{ this.player.y = this.world.centerY + (8 * Math.cos(this.time.now/200)); } },
Here we test if the gameStarted value is true, which means the game is running. If it is false we know that we are in the intro loop. During the intro loop we are going to automatically move the player sprite up and down like he is floating until the player starts the game. You can see that we use Math.cos to modify the y position of the player over each frame, which will give us our up and down motion.
Step 7. Refresh the game in the browser and can see the effect.
At this point we have our intro loop but we don’t have a way to let the player know what they should do next. We’ll create some text that will not only tell the player how to start the game but also will be used to display the score when the game is running.
Step 1. Add the following code below where we setup our player sprite in the create() function:
this.player.body.collideWorldBounds = true; this.scoreText = this.add.text( this.world.centerX, this.world.height/5, "", { size: "32px", fill: "#FFF", align: "center" } );
As you can see, Phaser supports canvas text and also embedded web fonts if you have them loaded up into your page. To keep things simple we’ll just use the default font, which is Arial. Here we are setting the x and y position of the text, passing it an empty string to start with and defining an object for how the text should be styled.
Step 2. Now we’ll need to set some text to be displayed. To do this, we’ll set it up in the restart() function then add the following to it:
this.scoreText.setText("TOUCH TO\nSTART GAME");
You can use \n to create line breaks in your text.
Step 3. Refresh browser and you should now see the text rendering correctly:
While we can now see our start text, you’ll notice that it’s off center. We can use a built-in feature of Phaser that allows us to anchor elements.
Step 4. In our create() function, right after we instantiated our scoreText instance, add the following code:
align: "center" } ); this.scoreText.anchor.setTo(0.5, 0.5);
This will center the anchor position of the text object which helps give the impression that it is properly align when displayed.
Step 5. Refresh the browser to see the changes
Now that we have our intro loop complete, let’s look into how to start our game.
In this section we are going to wire up our game to start when the player presses the mouse button and if the game is running, we’ll make our player fly up.
Step 1. We’ll need to create our start function on the state object:
start: function(){ this.player.body.allowGravity = true; this.scoreText.setText("SCORE\n"+this.score); this.gameStarted = true; }
Just like we did before, make sure you remember to insert a comma before you add this function. When called, this will enable the player’s gravity again. Next, we change the display to show the score and set the gameStarted flag to true.
Step 2. Now we need a way of calling the start() function. We’ll be adding a single function that will handle any mouse click. Let’s go ahead and create the jet() function on our state object:
jet: function(){ if(!this.gameStarted){ this.start(); } if(!this.gameOver){ this.player.body.velocity.y = -JET; } }
Here we are testing if the game has not started when the function is called. If the game is in the intro loop mode, meaning that gameStarted is false, we call start() to begin the game. If the game is not over, we also change the velocity of the player’s body. By changing the velocity.y value we immediately send the player flying up. You’ll notice that we are applying a negative value to move up. Now we just need our JET constant.
Step 3. Add a constant for the JET value at the top of our game’s code:
var JET = 420;
Step 4. Now in our create() method, we can bind the mouse click to call our jet() function above where we call this.resent() like so:
this.input.onDown.add(this.jet, this); this.reset();
Phaser handles the mouse input for us by allowing you to easily bind the onDown event to a function, in this case jet(), and supply a scope for the callback. This last bit is critical for maintaining scope in your game as you bind events. By passing this along with the reference to the jet() function we can safely reference properties in the game state like we have been in other places of our code.
Step 5. Now we are ready to refresh the browser and test out flying up and falling down.
Step 6. At this point we are ready to clean up the player animations. Add the following to the update() function, inside where we test if the game has started. We stop the jet animation when the player is falling and start it back up when the player is moving up:
update: function(){ if(this.gameStarted){ if(this.player.body.velocity.y > -20){ this.player.frame = 3; }else{ this.player.animations.play("fly"); }
I usually tie all of my animations to the game object’s physics, in this case testing the value of the player’s velocity and updating the animation to reflect that movement. You’ll note that we set the fall frame number manually to display when the jet is off. It’s not critical to create a one-frame animation if you know the correct sprite ID.
Step 7. Refresh the browser to test out the new animations we added to the player:
We now have everything we need in order to run our game, with the exception of detecting when the game is over.
Step 1. To create the game over logic we are going to need to figure out when the player actually hits the bottom of the screen and goes “out of bounds”. Add the following code just below where we setup our player’s animation in the update() function:
this.player.animations.play("fly"); } if(!this.gameOver){ if(this.player.body.bottom >= this.world.bounds.bottom){ this.setGameOver(); } } }else{ this.player.y = (this.world.height/2) + 8 * Math.cos(this.time.now/ 200);
Here you can see we are testing to make sure that the game is not over. This will keep the setGameOver() function from getting called continuously when the player touches the bottom of the screen. Once we verify that the game is not over, we test to make sure the player’s body is above the bottom of the world bounds. Phaser has several shortcuts for getting values from game objects. Accessing the .bottom property keeps us from having to manually configure the player’s dimensions.
Step 2. Now we need to create our setGameOver() function:
setGameOver: function(){ this.gameOver = true; this.scoreText.setText("FINAL SCORE\n"+ this.score+"\n\nTOUCH TO\nTRY AGAIN"); this.background.autoScroll(0, 0); }
Here you’ll see that we set the gameOver flag to true, change the text to reflect that the game is over and stop the background from scrolling.
Step 3. At this point we have enough logic to actually test that our game can end. Refresh the browser and let the player fall to the bottom of the screen.
Now we need a way to restart out game.
Step 4. Let’s modify our jet() function where we test if the game is over. Add the following else condition to that logic block like so:
if(!this.gameOver){ this.player.body.velocity.y = -JET; }else if(this.time.now > this.timeOver + 400){ this.reset(); }
You’ll notice that we are testing a new variable called timeOver. The basic idea here is that we are creating a simple delay between the time that the game is over, and when the player can click to restart. At the end of most game sessions, the player is frantically clicking away before they die to try and extend their session. Without a delay before restart, the player would accidentally restart the game without seeing their final score. To overcome this, we’re going to test that 400 milliseconds have passed before we actually let the player restart the game, giving them just enough time to see their final score.
Step 5. To make this fully work we’ll want to save the timeOver value when the player dies. Add the following to our the end of our setGameOver() function:
this.timeOver = this.time.now;
By saving out the current game’s time.now value we are able to track how many milliseconds go by before we allow the player to reset the game.
Step 6. Refresh browser and give it a try. You should now be able to fully start, end and restart the game.
Our game is starting to come together. We have sprites on the screen, we are able to control the player to make them fly, we have an intro loop, the game, and a “game over” state to manage. Coming up next we will add in our obstacles, add collision detection, score, points and talk about publishing our game.
- Jesse Freeman (@jessefreeman)
In this tutorial we are going to create the “new hello world” in gaming, a Flappy Bird style game. We’ll be using Phaser, which is a popular HTML5 game framework, and the game we are going to build will feature the main character from my Super Jetroid game along with a few obstacles from my free Space Exploration Art Pack. In the first part of this three part series we are going to cover the following:
Before we dig into the code, you can check out the live demo of the game here.
The next three sections will walk you through three different configuration options: CodePen.io, NodeJS + Grunt and Apache local server.
Simply pick the one you feel the most comfortable with, and once you have your environment configured you can move ahead to setting up Phaser itself. It’s important to note that each of these development options have advantages and disadvantages. Which option you choose depends on your technical comfort level and your past experience building HTML5 content.
Sometimes the hardest part of making an HTML5 game is setting up the development environment. To help make this as easy as possible, you can actually use an online code editor such as CodePen.io in order to run all of the code we’ll cover in this Introduction to Phaser tutorial. The advantage here is that you don’t have install anything on your computer. The disadvantage is that you will not be able to really publish and share your game; you’ll still end up having to host it online somewhere else. This means, you’ll need to rewrite the code to be standalone which I will not cover in this tutorial. Here are instructions for setting that up.
Step 1. You will need to create a new account on CodePen.io.
Step 2. Create a new project.
Step 3. Add the following to the HTML Window
<div id=“game”></div>
This will give us an empty div where we can put our game.
Step 4. Now we’ll need to add the following into the CSS Window
body { background: #333; margin: 0; padding: 0; } #game { margin: 0 auto; max-height: 568px; max-width: 320px; }
This will represent our default CSS for the body of the page and the game div itself.
Step 5. We need to configure an external JS file for the project. We’ll be using a version of Phaser I have hosted on my server. Click on the gear icon to the left of the JS window and paste the following url in the “External JS File” field:
http://games.jessefreeman.com/wp-content/public/workshop/phaser/phaser.min.js
You should have something that looks like this:
Step 6. At this point we are done with the basic configuration, so make sure to minimize the HTML and CSS windows so you have a larger place to work on your code.
One thing to note when using CodePen is that you’ll need to load all of the artwork and sounds from an online server. We’ll cover this later in the tutorial but just keep in mind that you will need to preface the url path to all of the assets we load as part of this tutorial with the following url (which is coming from my personal website):
http://games.jessefreeman.com/wp-content/public/workshop/phaser/
As an aside, you may have issues loading your own artwork into a CodePen project if you are not a pro member and using their hosting solution. To get around this, I have a .htaccess file in the root of my server’s public folder with the following:
<IfModule mod_headers.c> Header set Access-Control-Allow-Origin "*" </IfModule>
This allows you to bypass the cross-domain issues you normally get when loading images from severs on a different domain.
At this point we are ready to move on so you can skip the next two sections on setting up the project locally with NodeJS and Grunt or Apache.
I am a big fan of having an automated build process when I develop HTML5 games. For all of my projects I use NodeJS and Grunt. This automated build process will compile the game’s code into a single JS file for you any time you make a code change and hit save. The workflow also sets up a temporary server to run your game locally and even opens your browser to the correct URL making the entire setup and development process dead simple once it’s fully configured.
To help you get started I have created a Phaser Template Project which you will need to download in addition to NodeJS, Grunt and the project’s dependent modules. The following steps will walk you through this process from scratch. If you already have node and grunt installed, simply skip ahead to step 2.
Step 1. You will need a copy of NodeJS, which works on Mac, PC and Linux. Simply go to http://nodejs.org and get the correct installer for your computer. Once it’s installed you want to open up the command line and type in the following:
> npm
You should see something like the following:
This means that NPM (Node Package Manager) is installed and you can continue on with the rest of the setup process.
Step 2. Next we’ll need to install Grunt on the command line. If you have never used Grunt before, you can learn more about it at http://gruntjs.com. For now, enter the following in the command line:
> npm install -g grunt-cli
Step 3. At this point you have everything you need to support the automated workflow. Next you’ll want to download my Phaser Project Template from https://github.com/gamecook/phaser-project-template and unzip it on your computer where you do your development.
Step 4. Let’s go ahead and rename the Phaser Template Project to phaser-demo.
Step 5. Now we need to install the project’s module dependencies that will allow us to run the Grunt build script. Open up the command line and navigate to the project folder. Once inside, type the following command.
> npm install
You’ll see the following in the terminal window:
If you get an error for any reason, simply re-run the command again.
Step 6. Run the following command to start the grunt task:
> grunt
Once the command is executed your browser should automatically open showing you the default project page and display the current Phaser build version.
Phaser is a very active project, so your build number may be slightly higher then what is displayed above. As of this writing, the current version of Phaser is v2.0.3. As long as you have the Grunt task running, your project will automatically recompile every time you make a change to any JavaScript file inside the src directory. Once the project is recompiled, simply refresh your browser to see the changes. Also, make sure you disable your browser's cache.
This template project has everything you need to build your own Phaser game. You’ll find the game’s source file in the src/game folder called main.js.
Phaser’s source code is inside of the lib folder. You can update Phaser on your own at any point by simply replacing the phaser.min.js file in that folder. Just be careful because newer versions of Phaser may break your game’s code.
Since this project is pre-configured to display the Phaser version number, you will want to open up the main.js file and delete all the code inside of it so that it’s blank before you get started building your new game. Now you can skip ahead to the Setting up Phaser section of the tutorial.
Phaser is just like any other HTML/JS project you have ever worked with. If CodePen and NodeJS/Grunt are not your thing, simply run the game from Apache or any web server you are familiar with. Let’s look at how to setup the Phaser Project Template from the NodeJS configuration setup inside of Apache.
Step 1. Make sure you have Apache installed. I suggest using one of the following depending on your OS:
Step 2. Once Apache is installed and running, you will need to download my Phaser Project Template (https://github.com/gamecook/phaser-project-template) and put it in your Apache’s web root.
Step 3. Now you will want to move main.js and phaser.min.js from src folder into deploy/js folder. Since we are not automatically compiling these two files, we’ll just manually set them up like any regular script tag reference in an HTML file.
Step 4. Open up the index.html file inside of the deploy folder and fix the paths. You will need to load both js files like so:
<script src="js/phaser.min.js"></script> <script src="js/main.js"></script>
Step 5. At this point you are ready to load the project up in the browser. Navigate to your Apache’s localhost and go into the deploy folder. You should see the following:
Note that your url may be different depending on what port your Apache is configured to use. You may also see a different Phaser version number than what is displayed above. As of this writing, Phaser 2.0.3 is the latest build.
Step 6. Now that everything is working, open up the main.js file that should now be located in your deploy/js directory and delete everything inside of it. Then you’ll be ready to start the tutorial in the next section.
The hard part of configuring our environment is now behind us and it’s time to build our first Phaser game.
Step 1. At this point you should have an empty main.js file if you are using the Phaser Project Template or an empty JS window in CodePen.
Step 2. Type out the following object, which we will assign to a state variable:
var state = { preload: function(){ }, create: function(){ }, update: function(){ } }
Phaser uses a notion of “states” to represent each screen of your game. Think of the state as a place to keep all of the logic that belongs in that scene. Phaser will take a state and wire it up to the main game engine to run your code. When creating states, we’ll take advantage of a few functions such as preload(), create() (which is run first when a state is created and done preloading), and update() (which is run before the draw call). While Phaser has other state functions available, we’ll only be focusing on these three main ones for this tutorial.
Step 3. Now it’s time for us to create an instance of the actual Phaser game. Add the following code below where we created our state object:
var game = new Phaser.Game( 320, 568, Phaser.AUTO, 'game', state )
This will represent our Phaser game. Here you can see we are creating an instance of Phaser.Game as well as supplying the width and height. We are going to set the render mode of Phaser to Phaser.AUTO. This will detect what rendering options your browser supports and either run the game with WebGL support or call back to software based 2d Canvas. Finally we define the name of the div where our game should be created as well as pass in a reference of the initial state object.
Step 4. Now you are ready to refresh browser. You should see that we simply have a black box representing where our game will eventually go.
At this point you should now have a stable work environment for building Phaser games as well as the initial game code setup to build upon in the next parts of this tutorial. Coming up next, well talk about preloading, displaying images and building out some basic game mode logic for starting, playing and ending our game.
- Jesse Freeman (@jessefreeman)
For the past several years, HTML5 web app developers have been using the Apache Cordova project to create an incredible array of apps targeting native platforms. Many, such as the Wikipedia Foundation's mobile app, have had tens of millions of downloads across platforms. Cordova is the fundamental tool which enables developers to use open web technologies - HTML5, CSS3 and JavaScript - to create both fundamental and innovative mobile apps. This is why Amazon supports the Cordova project and enables developers to target the Amazon Fire OS platform. We want to enable web developers to take advantage of the advanced capabilities of the Chromium-based Amazon WebView integrated into Kindle Fire tablets running Fire OS.
We've covered the basics of setting up your Amazon Fire OS development environment for Cordova, so for this post, I'm going to create a new web app from scratch, covering all the varied little details you'll need to know to get your app from idea to Appstore. To do this, I created a simple drawing app that shows off both mobile and platform specific capabilities, and used Cordova to wrap it up into a native app ready for publication.
For the sake of simplicity, I developed an app targeting just Amazon devices. Though much of the power of Cordova is the fact that it allows developers to create cross platform apps, it can also be used as a way of quickly creating apps targeted at a single specific platform. This lets me use some more advanced HTML5 capabilities available on the Fire OS platform - in this case the accelerated 2D drawing canvas - and it also lets me cut some corners and use my rusty Java skills to add a few lines of native code to enable some needed functionality. In the future, I'll take this bit of native code and turn it into a plugin that can be used to target a wider range of devices.
I called the app Simple Draw, and it's available on the Amazon Appstore right now, downloadable for free. If you have a Kindle Fire device, go grab the app and try it out. It's a very simple drawing app targeted at young children - you can draw with your finger in various neon colors, clear the screen, or save the drawing as an image, which you can view in your Photos and share with others.
Here's everything you'd need to do to create this app yourself.
First, we'll start with the basics - setting up a new Cordova project and adding in support for Fire OS:
$ cordova create simpledraw com.example.html5.simpledraw SimpleDraw $ cd simpledraw $ cordova platform add amazon-fireos
This will create our new Cordova project directory, pre-fill it with the skeleton folders, and then add in the appropriate Fire OS support files (amazon-fireos). (The first time you run through this process, you'll be prompted to download the AWV SDK found here) Make sure you change the package name to be unique to you or your company, rather than com.example.html5 in the above example.
The resulting directory should look like the following - I expanded a couple important folders to show where we're going to be tinkering.
Now that we've got somewhere to put our app's files, we can go in and clear out the placeholder files and folders that are in the project’s www folder. This is where your app's web files go. The Simple Draw web app above only needs an index.html and a main.js JavaScript file, but other apps could have various text files, images, sound files, etc.
Let's take a quick look at both files.
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Draw</title> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1"> <style> html, body { height: 100%; margin: 0; padding: 0; background: #fff; color: #fff} </style> </head> <body> <script src="main.js"></script> </body> </html>
You'll notice there's not much there. It's really just a placeholder for a full-screen app that uses the HTML5 Canvas, so it doesn't need to be very complicated. It's important to set the viewport meta tag correctly, so my app knows that it's meant to be used on a handheld device, and to then call the JavaScript file at the end to make sure the DOM is loaded by the time I want to start manipulating it.
Next is the JavaScript, which is a bit longer, but is relatively straight forward and doesn't rely on any external libraries. The script creates a new full-screen canvas element, then listens for touch events that are used to draw on and to select from the menu bar at the bottom. There is a colorful gradient to choose your color and a couple icons at the bottom left which are used to clear the screen or save the image to your device's Pictures directory. Everything except that last bit can be done using pure JavaScript.
var canvas = document.createElement('canvas'); canvas.style.position = 'absolute'; var context = canvas.getContext("2d"); context.fillStyle = "#000"; context.lineCap = 'round' document.body.appendChild(canvas); var pictureCanvas = document.createElement("canvas"); var pictureContext = pictureCanvas.getContext("2d"); var width; var height; var menuSize = 40; var lineColor = '#fff'; var lineWidth = 6; var isDown = false; var points = []; // listen for events window.addEventListener("resize", reset, false); canvas.addEventListener("touchstart", pointerDown, false); canvas.addEventListener("touchmove", pointerMove, false); canvas.addEventListener("touchend", pointerUp, false); document.body.addEventListener("touchcancel", pointerUp, false); //set up and begin reset(); requestAnimationFrame(animate); // functions function reset(e){ width = window.innerWidth; height = window.innerHeight; canvas.width = width; canvas.height = height; pictureCanvas.width = width; pictureCanvas.height = height - menuSize; context.fillRect(0,0, width, height); drawMenuBar(); lineColor = "#fff"; points = []; } function drawMenuBar(){ // color gradient var grad = context.createLinearGradient(menuSize * 2, height - menuSize, width, height); grad.addColorStop(0, 'black'); grad.addColorStop(1 / 8, 'red'); grad.addColorStop(2 / 8, 'orange'); grad.addColorStop(3 / 8, 'yellow'); grad.addColorStop(4 / 8, 'green') grad.addColorStop(5 / 8, 'aqua'); grad.addColorStop(6 / 8, 'blue'); grad.addColorStop(7 / 8, 'purple'); grad.addColorStop(1, 'white'); context.fillStyle = grad; context.fillRect(menuSize * 2, height - menuSize, width, height); // icons var refreshIcon = new Image(); refreshIcon.onload = function(){ context.drawImage(refreshIcon, -2 , height - menuSize); }
refreshIcon.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAACg0lEQVRYCe2XMa8pQRTHx/MSjRBRENEpCBEJEoUQvgO1UqGlFirxaURoVSqh0IioFKIRiYRCKObt2XdnMruse8bam1vsSTZzdpzzPz9nszOzDkIIVa5fa39+LdkXmA1o9gnZHbQ7aLYDZvP/mhWA/FQqReLxOAmFQqrcfr8nq9WKLJfLT8irCzUs1lKXx+Oh3W6XbrdbamTwW6fToRArqy/Ey4FBYrVapYfDwYjrYR5iIUcoKuPLAbbb7QcA7ATkvgGJB2w0Gg8s8BhbrRZNJpPU7XarF/gw9+zxg4YkJA4wk8nQ+/2uAez3+9TlchkWhN8gRjTQAC0JSBzgZDIR69Bms4kuArGigdZHAXO5nKhPB4OBTAE1FnJEA00k5Pcd7PV6ojaNRqNYcR4Xi8U0GqD5McDZbMbFF4sFVvghDnKZgSYGEHWaCYfDitZ/m8/nzJUexVxR85UQCtDv93ON4/HIfVlHzBU1X+mgAN8RflZUhBI1n8WyORTgbrdj8SSbzXJf1hFzRc1XOihAZd3iGul0mihvMb/HOspbTCCXmajJ5ozGb98m2XXQ6XTSWq2m0bV0HVT+GdXvJJFIRAMAMeyCNe50OvFt0PKdBAqLe/F4POYwDIqNlUqFLXUU/B/biwEATiLX65UadS+RSNDz+cwBL5cL95lj2WmGdahUKj3tntfrpZvNhnGoo/70Y/l5kEHqR4fDQUejkQZOvPnRE7UeDu7r9brIo/HX67WpbxLHV0FlMGfBYJAUi0VSKBRIuVxWv/KUzpLb7UYCgQBR3uq3CnwMUF/d5/ORfD6vQg+HQzKdTvUhqHvLAFHVEUGorQ6hY1mIDWi2tXYH7Q6a7YDZ/H90B3qb+wyF3wAAAABJRU5ErkJggg=="; var downloadIcon = new Image(); downloadIcon.onload = function(){ context.drawImage(downloadIcon, menuSize, height - menuSize); } downloadIcon.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAABCklEQVRYCe2X0QmDUAxFtegA4hCKfy4huIvgCG4hiLMIziHuoP4LqQFfwVLa2puWR0ngka/7OJ4EVNdxHNqOtXWxlmwHU0B0QmpQDaIG0LzIDlZV5RDR4aBgJi8CaC77RldA1KoaVIOoATSvO6gGUQNoXndQDaIG0Lz1O8gPyD/up06SJLQsy/aN+l61bXvq/juec3AmnOc5rev6krDve/J9//eADFqW5VPAcRwpDEMEjrOfGTS5pmkeQs7zTHEco3A4oOd51HXdAZJHn2WZBBwOyCaDIKBhGG6QRVFIwckAMmQURTRNE9V1LQknB8iQaZoSj9zsp0R390u2ZmdZ/yZRQHRx1ODfG7wCaGXbMjKT0dAAAAAASUVORK5CYII="; //icons border context.strokeStyle = "#fff"; context.lineWidth = 2; context.beginPath(); context.moveTo(menuSize, height-menuSize); context.lineTo(menuSize, height); context.moveTo(menuSize * 2, height-menuSize); context.lineTo(menuSize * 2, height); context.stroke(); } function saveImage(){ if(confirm('Save Image?')){ pictureContext.drawImage(canvas, 0, 0); var dataUrl = pictureCanvas.toDataURL("image/png"); if(typeof SimpleDraw !== 'undefined'){ SimpleDraw.savePNG(dataUrl); } } } function eraseImage(){ if(confirm('Erase Image?')){ reset(); } } function drawLine(){ if(points.length > 1){ context.strokeStyle = lineColor; context.lineWidth = lineWidth; context.lineCap = 'round' context.lineJoin = 'round'; context.shadowBlur = lineWidth/2; context.shadowColor = lineColor context.beginPath(); context.moveTo(points[0].x, points[0].y); for (var i = 1; i < points.length; i++) { context.lineTo(points[i].x, points[i].y); } context.stroke(); } } // events function animate(time) { drawLine(); requestAnimationFrame(animate); } function pointerUp() { isDown = false; points = []; } function pointerDown(e) { e.preventDefault(); var point = {}; point.x = e.targetTouches[0].pageX; point.y = e.targetTouches[0].pageY; if(point.y > height - menuSize){ if(point.x < menuSize){ eraseImage(); return; } if(point.x > menuSize && point.x < menuSize * 2){ saveImage(); return; } if(point.x > menuSize * 2){ var data = context.getImageData(point.x, point.y, 1, 1).data; lineColor = 'rgb(' + data[0] + ',' + data[1] + ',' + data[2] + ')'; } } else { isDown = true; points.push(point); } } function pointerMove(e) { e.preventDefault(); var point = {}; point.x = e.targetTouches[0].pageX; point.y = e.targetTouches[0].pageY; if(isDown && point.y < height - menuSize){ points.push(point); } }
A few notes about the Javascript:
So we're all set with the main functionality of the app, and can quickly test the script by building/running the application on the device.
Please note: Cordova apps for Fire OS don't currently support emulation because of the integrated nature of Amazon WebView.
Plug your Kindle Fire into your computer via a USB cable and perform:
$ cordova run
On first run, this will compile the application using the Android Developer Toolkit, then push the app's debug version to your Kindle Fire so you can test.
Hey, it works! Well, almost - it doesn't actually save the images, so we'll have to wire that up next.
When I first started testing out the functionality of the app, I assumed I'd be able to use the Cordova FileSystem API plugin to save the resulting image to the local file system. So I added the plugin via the command line (see the Cordova docs on how to do this), and used it to save the canvas image data as files. But I couldn't see them from the Photo gallery! That's not useful for this particular application - so I uninstalled that plugin, and decided to add that bit of native functionality myself.
The problem is that Fire OS (and other Android systems) keeps a separate database of images that needs to be updated in order for the images to appear in the photo browser. So in addition to saving the file, I needed to update the system to recognize the fact that the file is an image and to update the database. To do this, I needed to add a few lines of Java code to add in a custom JavaScript Interface in Cordova's native app wrapper code, found deep within the platforms folder here:
/simpledraw/platforms/amazon-fireos/src/com/example/html5/simpledraw/SimpleDraw.java
If you've done any sort of Java development, you'll recognize what that long path is - it's the standard Java directory system based on the name of the package. I had to do some digging on the web to find the right APIs to wire together, but in the end it was only a few lines of Java. I would have preferred to stay within JavaScript, but having the option to dive into native code if I needed to was very useful.
Here's the resulting SimpleDraw.java CordovaActivity class:
package com.example.html5.simpledraw; import org.apache.cordova.*; import java.text.SimpleDateFormat; import java.util.Date; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.provider.MediaStore; import android.util.Base64; import android.view.Gravity; import android.webkit.JavascriptInterface; import android.widget.Toast; public class SimpleDraw extends CordovaActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); super.init(); //super.getFactory().enableDeveloperToolsUnix(this.getPackageName() + ".devtools"); this.appView.addJavascriptInterface(new JSObject(), "SimpleDraw"); super.loadUrl(Config.getStartUrl()); } public class JSObject { @JavascriptInterface public void savePNG(final String dataUrl) { runOnUiThread(new Runnable() { @Override public void run() { SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); String fileName = "drawing-" + df.format(new Date()) + ".png"; byte bytes[] = Base64.decode(dataUrl.substring(dataUrl.indexOf(",") + 1), Base64.DEFAULT); Bitmap bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); MediaStore.Images.Media.insertImage(getContentResolver(), bmp, fileName, fileName); Toast toast = Toast.makeText(getApplicationContext(), "Image saved!", Toast.LENGTH_SHORT); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); } }); } } }
Notes about the native code:
adb forward tcp:9222 localabstract:com.example.html5.simpledraw.devtools
In order for the app to be able to save files to the device's local storage, it needs to have the right permissions. This is done by editing the AndroidManifest.xml file found in the /platforms/amazon-fireos directory.
In the example below, you can see that the WRITE_EXTERNAL_STORAGE permission has been added to the list of user-permissions, allowing the app to save images to the file system. Additionally, the android:screenOrientation flag has been set in the application element to lock the screen to landscape mode only.
The resulting file looks like this:
<?xml version='1.0' encoding='utf-8'?> <manifest android:hardwareAccelerated="true" android:versionCode="1" android:versionName="0.0.1" android:windowSoftInputMode="adjustPan" package="com.lab126.html5.simpledraw" xmlns:android="http://schemas.android.com/apk/res/android"> <supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:resizeable="true" android:smallScreens="true" android:xlargeScreens="true" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application android:hardwareAccelerated="true" android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale" android:label="@string/app_name" android:name="SimpleDraw" android:screenOrientation="landscape" android:theme="@android:style/Theme.Black.NoTitleBar"> ... more xml configuration here ... </manifest>
Now when you test out the app and click the download arrow, the image will be saved and you'll get a native Toast message pop up confirming the process.
If you check in the Photos app, you'll be able to see your image. Hooray! So the app now works, but we're still a few steps away from publishing it to the Amazon Appstore.
The Cordova config.xml settings file can be found in your main project folder. Enter in your contact details, the name and description of your app, and then add in Fullscreen and BackgroundColor preference tags (see below) which will make your app fill the entire screen, and prevent any sort of color flash as it loads.
Here's what the config.xml file looks like:
<?xml version='1.0' encoding='utf-8'?> <widget id="com.example.html5.simpledraw" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"> <name>SimpleDraw</name> <description> A sample drawing app using Cordova for Amazon Fire OS </description> <author email="beattier@amazon.com" href="http://www.amazon.com"> Russell Beattie, HTML5 Platform Technical Evangelist </author> <content src="index.html" /> <access origin="*" /> <preference name="Fullscreen" value="true" /> <preference name="BackgroundColor" value="0xff000000"/> </widget>
Next we'll need to replace the different sized app icons that our project used before we are ready to publish. These images are found in the /platforms/amazon-fireos/res directory, and will need to be created and copied over manually. I used a drawing program to whip up a quick app icon and then exported it as a giant 512px square image (you'll use this later when submitting your app to the Appstore). I then shrunk the images as needed and saved them as icon.png files in the following folders:
/res/drawable = 96px
/res/drawable-hdpi = 72px
/res/drawable-ldpi = 36px
/res/drawable-mdpi = 48px
/res/drawable-xhdpi = 96px
Now when you build and run your project, the generic Cordova icon will be replaced by your app's custom icon.
We're almost there! One last step before we're ready to publish the app is to create a signed -release version of the app's .apk file. Up until now, the Cordova build process has created -debug versions of the app for testing, but now we'll set up the files needed to sign the apk when the --release flag is passed to the build command.
First, you'll need to either create or locate your Java keystore. If you haven't created one yet, definitely check out the Android documentation which summarizes the keystore process quite well. However the following command will get you started:
$ keytool -genkey -v -keystore my-release-key.keystore -alias myapps -keyalg RSA -keysize 2048 -validity 10000
This will prompt you for all the information you need to enter and create a new keystore file for you to use to self-sign your app. Put the keystore somewhere safe and accessible from your Cordova project, and don't forget the password!
Next, you need to create a new text file called ant.properties in the base of the amazon-fireos directory: /platforms/amazon-fireos/ant.properties. In it, add two lines pointing at the appropriate directory:
key.store=/Users/example/keystore/my-release-key.keystore key.alias=myapps
Now you can create a release version of your app to submit to the Amazon Appstore by just using the Cordova command line tool. Call the build command and add a --release flag like this:
$ cordova build --release
This time it will go through the normal build cycle, and then stop to ask you for your password to your keystore and alias. Once it is complete, there will be a brand new signed apk ending in -release.apk saved in the platform's ant-build directory. For example /platforms/amazon-fireos/ant-build/SimpleDraw-release.apk .
Now that you have your apk ready to go, you can submit it to the Amazon Appstore! You'll need to gather some screenshots, that big icon image from earlier, and fill out some details describing your app on the site, but within a short time the submission process will be complete.
To start the process, head to the Amazon Developer Portal and click on the "Submit Android App" button (you'll need an Amazon developer account of course – that’s free). That will guide you through the process - for more information about the type of information you need to answer, definitely check out our helpful submission documentation as well.
Hopefully this will save you some time when developing your own Cordova app for Amazon Fire OS. Though it may seem like a lot of steps the first time through, it's really only a few extra files that need to be created or changed, and the end result is a fun new app created using familiar, yet powerful, web technologies. Good luck with your app - and definitely contact us if you have any questions or concerns!
Amazon WebView (AWV) is a Chromium-derived web runtime exclusive to Fire OS. AWV makes better performing and more powerful apps possible by providing support for a faster JavaScript engine (V8), remote debugging, and hardware optimizations for Kindle Fire devices including an accelerated 2D Canvas. Enabling your Cordova project to support AWV gives you access to HTML5 features not supported by Android’s built in WebView such as: CSS Calc, Form Validation, getUserMedia, IndexedDB, Web Workers, WebSockets and WebGL.
For developers who are not familiar with Cordova, it is an open source solution that provides tools and templates that wrap web code and assets, allowing you to publish your web code and assets as if they were native apps. Cordova is also customizable via its plugin architecture which allows developers to extend the framework in order to access OS level APIs like the built-in contact list, as well as physical hardware on the device itself like the camera. Cordova also makes it possible for two way communication from the web app wrapper to the device’s native language.
To ensure that all core Cordova plugins will be natively supported, Amazon worked with the Apache community when adding Cordova support for the Amazon Fire OS platform. Here is how to set it up on your local computer, enable Amazon Web View and create a project from scratch.
You need to make sure you have all the required tools and libraries needed to compile and package an Android application. Download and install the following (please note these links will take you to third-party sites):
You'll need to have Java installed, and the Android SDK from developer.android.com/sdk which includes the Android Developer Toolkit (adt). You may be presented with a choice of where to install the SDK, otherwise move the downloaded adt-bundle tree to wherever you store development tools.
For Cordova command-line tools to work, you need to include the Android SDK's tools and platform-tools directories in your PATH environment:
To modify the PATH environment on Mac, Linux, etc.:
export PATH= $ {PATH}:/Development/adt-bundle/sdk/platform-tools:/Development/adt-bundle/sdk/tools
You will also need to enable Java, Ant and/or Node from the command line. Open a command prompt and type "java", then "ant" then "node". Reinstall, or append to the PATH whichever fail to run.
This will expose the tools in newly opened terminal windows. Otherwise run this to make them available in the current session:
$ source ~/.bash_profile
To modify the PATH environment on Windows 7:
;C:\Development\adt-bundle\sdk\platform-tools;C:\Development\adt-bundle\sdk\tools
You will also need to enable Java, Ant and/or Node from the command line. Open a command prompt and type "java", then "ant" then "node". Reinstall, or append to the PATH whichever fail to run:
;%JAVA_HOME%\bin;%ANT_HOME%\bin
Make sure you are able to invoke npm (Node Package Manager) on your command line; it's added as a part of Node.js. (You can install Node.js from the nodejs.org homepage and type NPM in the command line to validate the installation.)
To install Cordova, open a command prompt/terminal and use:
$ npm install -g cordova
On Unix-like systems, you may need to append "sudo" to ensure it is installed correctly. See Cordova's documentation for the Command-Line Interface for more details.
Create project. Open a command line/terminal in a directory where you'd like to have your project stored and run the following commands to create a project and add the files needed to build for Amazon Fire OS:
$ cordova create hello com.example.hello HelloWorld
Add Amazon Fire OS platform. Change into the newly created project directory and add the files needed for the amazon-fireos platform target.
$ cd hello $ cordova platform add amazon-fireos
The first time you try to add the amazon-fireos platform you will be instructed add the Amazon WebView SDK. Download and extract the Amazon WebView SDK zip file from the Amazon Developer Portal.
Copy awv_interface.jar from the unzipped folder into the amazon-fireos directory found in Cordova's global working directory. Note: You'll need to create the libs/ folder manually.
On Mac/Linux:
$ mkdir ~/.cordova/lib/amazon-fireos/cordova/3.4.0/framework/libs $ cp awv_interface.jar ~/.cordova/lib/amazon-fireos/cordova/3.4.0/framework/libs/
On Windows:
> mkdir %USERPROFILE%\.cordova\lib\amazon-fireos\cordova\3.4.0\framework\libs > cp awv_interface.jar %USERPROFILE%\.cordova\lib\amazon-fireos\cordova\3.4.0\framework\libs
Then you will need to remove and re-add the platform:
$ cordova platform rm amazon-fireos $ cordova platform add amazon-fireos
Add HTML5 assets. Your web app's assets - HTML, CSS, JavaScript, images, etc. - should be placed in the project's www folder. You can use the sample placeholder files installed as a template, or you can replace them completely with your web apps's files.
Add support for Android devices if needed. If your app is going to be used on non-Fire OS devices running Android, you can provide a fallback to the stock Android WebView engine by adding the awv_android_factory.jar file found in the AWV SDK bundle into your Cordova project under the platform libraries folder. For example:
./hello/platforms/amazon-fireos/libs/awv_android_factory.jar
Adding the code above will allow you to submit the app to the Amazon Appstore and target non-Kindle Fire devices as well as all Kindle Fire devices.
Build and deploy your project. Make sure that USB debugging is enabled on your device as described on the Android Developer Site, and use a mini USB cable to plug it into your system. (Note: Currently, testing via an emulator is not supported for Amazon WebView based apps.)
Then you can build and deploy the app to the device with the following single command:
$ cordova run
With Amazon Web View support enabled for Cordova, you can build higher performance apps with tools like WebGL, CSS Calc and Web Workers, debug your app remotely, and see performance gains through a faster JavaScript engine and access to accelerated hardware on Kindle Fire devices.
To learn more, follow these links (some of which will take you to third-party sites):
We are excited to announce that developers can publish their web apps to the Amazon Appstore without a manifest. As we continue to streamline the web app submission process our goal is to make submitting hosted web apps just as easy as submitting Android ones. Now all you need to do is supply your hosted web app’s url, instead of uploading an APK, and the rest of the process is exactly how you would expect. Fill in the app’s description, upload screenshots and define your app’s permissions which allow you to quickly publish web content to the Amazon Appstore. Let’s take a look at how this works.
Our web app submission process revolves around being able to take any online website and by submitting its URL it will be published like a native app to the Amazon Appstore. For customers who have websites that support responsive design, work well on mobile, and are looking for a way to distribute their site next to native apps, this is the best solution out there for getting hosted content in the Amazon Appstore. Additionally, they can monetize their website by setting a base list price or by using Amazon’s In-App Purchasing APIs. If this is your first time submitting a web app, you simply provide a url where you would normally upload a native APK.
Let’s take a look at how you can get your own web app ready for submission in four easy steps.
You can now validate your web app’s URL right from the developer portal.
Simply put your URL in, click the verify button and the tool will let you know if the contents at the URL pass the preliminary submission requirements. We check for the following things:
1. Is the URL properly formated
2. Make sure that a URL does not return a 4XX or 5XX http status
3. URL has valid security certificate
4. Redirects don’t lead to an eventual 4XX or 5XX code
Just keep in mind that users will need to have an Internet connection to access your site once it’s submitted.
Once your URL has has been verified, you can select your app’s permission. Simply check off the options that apply to your app.
Then you can define on which devices this web app can be published.
While the Kindle Fire HD and HDX devices offer the best performance for web apps, make sure you test your web app on older devices to ensure the performance is ideal for your users. Intensive web games and anything using WebGL should be distributed on Kindle Fire HD and above.
One other thing to keep in mind is that while you can install web apps in the Amazon Appstore on any Android device that has the store installed, it will default to the native WebView on that device. This means you will not have access to the optimized Amazon WebView and will see a drop in performance. Make sure to test out your URL in the web app Tester and select Android Web View to see what it will run like on native Android devices.
Finally, the last thing you need to do is check off Distribution Rights.
This ensures that you own the domain and the rights to publish the app. We will do our own verification to make sure you are in fact the owner of this domain and are not trying to publish another site to the store.
As you can see, the process of submitting a web app to the Amazon Appstore couldn’t be easier. Now that the entire process is done through the developer portal it should take you only a few minutes to expand your web app’s user base by making it available to millions of Amazon Appstore customers in nearly 200 countries. Better yet, you now have the ability to charge for your web app or add in-app purchases to create new opportunities to monetize your online content on mobile devices.
Amazon Developer Portal -
- Jesse Freeman (@jessefreeman)
In a world quickly moving toward mobile device adoption, there is a growing pressure for web developers to learn new languages in order to adapt to the ever-changing landscape of content delivery. For the past 16+ years, web has been the king of mass distribution. But now as app stores on mobile devices are driving huge monetization opportunities, how do web developers stay competitive in this new “post PC world”? The key is to understand how you can maximize your web app’s potential in the distribution vs. monetization model.
As developers look to create new content, be it a web app or native app, they should be thinking about the following model:
The distribution vs monetization model.
The concept is that the larger your distribution base, the better your chances of monetization are. Along the far side of the x-axis is the native mobile and tablet space, which is fragmented around several different platforms, and the individual platform distribution potential is much smaller. On the flip side, since all mobile devices and computers have web browsers, an online web app’s potential reach is staggering.
The reality of this however has been that even with the smaller distribution of mobile, developers are seeing much higher opportunities to monetize on those devices. On the web, we have seen more difficulty to monetize that content without the help of built-in systems like IAP (in app purchase) or integrated checkout, which are generally available on native devices through their app stores. The ideal solution would be to actually target both demographics, and the only platform we have seen that is capable of effectively doing that is HTML5.
When most developers hear “scaling a web app” they instinctually think about the backend or server side of things. But over the past year as responsive design has come into its own, we are finally seeing websites that can not only run on desktop browsers but elegantly reconfigure themselves to accommodate all the different viewports users are visiting with.
The most common responsive design resolution breakpoints.
The forethought that goes into building a truly responsive design that flows correctly from desktop to mobile phones is no small task but the opportunity for capturing the largest possible distribution is worth it. Gone are the days of splitting off your web traffic between a mobile only site and a desktop site because the cost of maintaining both grow exponentially. But what about still reaching native app stores?
Some of the current solutions on the market for publishing HTML5 content next to native apps have revolved around the PhoneGap/Cordova model. These allow the developer to package the web app and submit it to a native app store. But there is one glaring downside to this kind of distribution; you lose the ability to maintain a single codebase. In an ideal world, you would want to simply redistribute your online app in a mobile store and keep the two in sync. This is some of the thinking behind our HTML5 web app resources for Amazon Appstore.
The last thing a developer would want to do is fork a project and end up maintaining multiple code bases to support each platform it is distributed on. So why not just keep your content online where it will get the largest potential for distribution and still submit it to an app store that offers you an entirely new way to monetize it? This is a fairly new publishing model that has been growing more and more popular over the past few years. It offers the best of both worlds since you maintain a single place were your web content can live and you gain the added benefit of being able to distribute your app in a native store. With that distribution comes the potential of increased monetization by simply charging for the app, using IAP or continuing with your current ad based model.
The best part is that you can experiment with this new type of distribution today in the Amazon Appstore with our HTML5 Web App Tester. Simply download the app, point it to your site’s URL and test out how your web app works. From there it’s easy to submit to the Amazon Appstore and begin reaching a whole new audience.
Creating an application using Amazon's Mobile App Distribution Program is a great way for developers who have hosted web apps to create downloadable apps available on the Amazon Appstore. Web app developers benefit from the increased discoverability that the store provides when users are searching for new apps, as well as being reminded to use the web app by the icon on their device's home screen afterwards. In addition, all of a user’s linked devices that use the Amazon Appstore will have the icon waiting in their Cloud list of apps as well.
And because web apps are hosted online, developers have increased flexibility to re-use existing assets and make changes or fixes to the app quickly and easily, without having to re-create a new app that has to be re-installed by the end user. But what happens if the user wants to use the app offline? Obviously, if the app relies on live server-side content - such as streaming videos or airline reservations - then obviously it's not going to work. But if the web app is self-contained and doesn't need to talk to a server for its core functionality - like most games - then being able to take the app offline is something that should be an option for users.
Happily, enabling your web app to be used offline can be done using HTML5's built-in Application Cache with only a few small changes to your code. Below I'll outline the steps you need to take to create a basic offline web app. It's surprisingly easy to set up, but beware! Application Cache has a well deserved reputation for being difficult for developers to get a handle on.
The first thing you need to do is create an HTML5 manifest text file with a list of every asset file your web app requires - HTML, JavaScript, CSS, images, icons, fonts, etc. The manifest file can have any name you choose, but we'll use app.appcache to be clear and to avoid overlap with other types of manifest files.
Here's the content of a basic manifest file:
CACHE MANIFEST # Version 1.0 CACHE: main.js main.css logo.png NETWORK: *
- The first line needs to be CACHE MANIFEST.
- The second line in this example is just a comment, but is useful to make changes to your web app by simply incrementing the version number. Note: Only changes to the manifest file will invalidate the cache.
- The third line begins the CACHE: section where you list out the asset files used by your web app, either relative to the location of the manifest file, an absolute path or complete URL. Note: DO NOT list app.appcache in your manifest.
- The NETWORK: section has a wildcard which permits the browser to download files that are not listed in the CACHE: section. Note: Without the NETWORK: section, the browser will ONLY re-request files listed in the CACHE: section after the initial page load.
You need to also make sure your web server serves the correct MIME type for the manifest file. For Apache, it looks like this:
AddType text/cache-manifest .appcache
You also need to makes sure the manifest file is not being cached on the server. If the HTTP Cache-Control header for the manifest file doesn't update, or a 304 Not Modified is return, then the web engine won't be able to see if the manifest file has been changed or not, which is the only way to invalidate the offline cache.
You then need to add an attribute to the tag of every HTML page you serve pointing at the manifest file, like this:
<html manifest="app.appcache">
Finally, you need to make sure your app updates itself if the manifest changes - the easiest way to do this is to add this bit of JavaScript to your main HTML:
<script> window.applicationCache.addEventListener('updateready', function(e){ window.location.reload(); }); </script>
Your web app should now be offline enabled! If you have Python installed, you can test this by setting up a local server to see what's happening both on the server and in the browser.
beattier@amazon.com:~/html5demos/offline$ python -m SimpleHTTPServer Serving HTTP on 0.0.0.0 port 8000 ... 1.0.0.127 - - [21/Jan/2014 13:42:52] "GET / HTTP/1.1" 200 - 1.0.0.127 - - [21/Jan/2014 13:42:52] "GET /app.appcache HTTP/1.1" 200 - 1.0.0.127 - - [21/Jan/2014 13:42:52] "GET /main.css HTTP/1.1" 200 - 1.0.0.127 - - [21/Jan/2014 13:42:52] "GET /main.js HTTP/1.1" 200 - 1.0.0.127 - - [21/Jan/2014 13:42:52] "GET /logo.png HTTP/1.1" 200 - ...
If you request the page again, you'll see that *only* the manifest is requested.
1.0.0.127 - - [21/Jan/2014 13:43:12] "GET /app.appcache HTTP/1.1" 304 -
By modifying the manifest file and reloading, you'll see that all the files listed will be re-downloaded again.
You can also connect the Amazon Web App Tester to see the client side of the process as well by using Remote Debugging. (See our previous overview of setting up the Tester here.) In the screenshot above, I've connected to a Kindle Fire HDX and loaded a demo offline web app stored on Github. By looking at the Resources tab and drilling down into the Application Cache folder, I can see the assets that are cached locally, and a log of the Application Cache events.
This is just a basic way to setup an offline web app. There are more options that you can add to your manifest file, more events you can track in JavaScript and more functionality you can use to make your web app's offline experience much more seamless to the end user. Check out the links below for more information.
Conceptually, it's important to understand that once you've enabled a manifest, your web app is now offline first and forever. Let's repeat that for clarity: OFFLINE FIRST AND FOREVER.
OFFLINE FIRST means:
- Your web app's files will then always be loaded from the offline cache first, and then a request will be made to the server for the manifest file to see if there have been any updates.
- The browser will not automatically refresh if the manifest has changed. It will in fact download the files from the server, but it will wait until the next time the page is requested to use them. This is why the script in step 4 above to detect a manifest change and immediately refresh the page is important.
FOREVER means:
- The only thing that can invalidate the offline cache and trigger a re-download of files is a change in the contents of the manifest file - not just the timestamp.
- There is no programmatic way to invalidate the offline cache from the browser. Even changing or deleting the manifest attribute in the tag will not invalidate the cache.
- Until the browser requests a manifest and receives a 404 or 410 from the server, it will continue to consider the web app as being offline and use the last downloaded version, rather than updating from the server.
The info above should be able to get you started with offline web apps. Once you've added in the manifest, your web app will be available offline the next time your users load up your app. Fair warning: This can be a tricky feature to implement - especially if you misconfigure or forget some of the steps above. Getting the browser to let go of the manifest and refresh your code can be incredibly frustrating. I think the upside is worth it though, as enabling your web app to be used anywhere a native app can be used is incredibly satisfying.
To get more information about Application Cache, I encourage you to check out these great articles which dive into the topic in even more detail.
· http://diveintohtml5.info/offline.html
· http://www.html5rocks.com/en/tutorials/appcache/beginner/
· http://www.html5rocks.com/en/mobile/workingoffthegrid/
· http://alistapart.com/article/application-cache-is-a-douchebag
In future posts, I'll expand on offline apps by looking at topics such as offline data storage and efficient caching strategies.
-Russ (@RussB)
Note: Effective 08-26-2015 Free App of the Day (FAD) has been replaced with Amazon Underground.
In August of last year we enabled HTML5 developers to publish hosted web apps next to native Android apps in the Amazon Appstore, making HTML5 web apps discoverable to millions of Amazon customers. Starting today, developers can set a list price for their web apps, giving them another way to monetize web content to Amazon Appstore customers in nearly 200 countries across the globe.
Prior to this update, web apps published to the Amazon Appstore were automatically set to free. In the latest update developers can now set a list price in specific currencies based on the local markets they’re publishing their web apps to. This is great news for developers looking to publish their first web app to the Amazon Appstore, and the feature is now retroactive to all web apps that have already been published. That means existing developers simply need to log into their Developer Account to see an updated “Availability & Pricing” tab with the option to charge for the app.
Now with the ability to set a price for web apps in the Amazon Appstore, HTML5 developers can take part in our Free App of the Day (FAD) promotion. For developers not familiar with this program, qualifying apps gain greater exposure that could drive significant traffic to their apps. Opting into the FAD promotion and being selected enables us to work directly with the publisher to be featured in some of our most visible marketing placements. This includes placements on mobile devices, Kindle Fire, and the Amazon Gold Box Best Deals page, which are complemented by social media exposure including Facebook and Twitter announcements. These placements and the exposure they provide can drive significant traffic to the featured apps and allow the developers to grow their install base quickly.
To learn more about qualifying for the Free App of the Day program, check out this post on how to get approved, and make sure you opt to have your web app considered for the program during the app submission process.
To republish an existing app as paid, simply edit the ‘Availability & Pricing’ tab and enter a list price, which is already defaulted to free. The same list price changes can also be made to any submission set to “IN PROGRESS” that is waiting to be approved for release.
This is just one new feature we’ve added to help make publishing HTML5 Web Apps to the Kindle Fire best in class. For more information, check out the HTML5 Web Apps detail page
-Jesse Freeman (@JesseFreeman)
The Amazon Mobile App Distribution Program enables developers to create Kindle Fire apps using existing HTML5 mobile web apps. It’s also a good way for web developers to start creating mobile apps using the skills and knowledge they already have.
Back in December we covered how to get your existing web apps onto actual devices with a Webinar on Submitting HTML5 Web Apps to the Amazon Appstore, and companion blog post focusing on setting up the Amazon Web App Tester to debug and test your apps.
We wanted to follow up with some more details to help you get the most out of the tester, which is a key part of the HTML5 web app creation process on Kindle Fire tablets. The Web App Tester has a variety of powerful features which can be used to make development faster and easier. Below are a few ways to best take advantage of all that great functionality.
What exactly is Amazon's Web App Tester? It's a downloadable app which lets you test your web app in a production-like environment on your Kindle Fire or Android device, without first submitting it to our store. The tester contains the same web engine and libraries that will run your web app when it is wrapped into a downloadable app. The tester however, has an interface that lets you enter in your own custom URLs, and most importantly, enables remote debugging for development and testing using your desktop computer.
Because the Web App Tester is based on the same technologies as the final wrapped app, you should be able to better assess your app's performance and functionality - and more quickly work through any problems you might encounter - on the device itself, rather than via an emulator or simulator, which may not be as accurate.
Additionally, libraries that are pre-loaded into the final downloadable app, such as the In App Purchasing API for JavaScript, are also built into the Tester so you can debug your IAPs before you launch. (Look for a more detailed post about enabling IAP for your web apps coming soon).
The first thing you'll encounter when using the Web App Tester is a place to enter a URL for testing. This is great for easy ad-hoc testing, but if you have complicated URLs to enter, or have multiple URLs that you need to manage, you can create a JSON file of URLs and put it into the root folder of your Kindle Fire. The file has to be named amazon.testerurls.json and placed in the /mnt/sdcard/ folder on your device. Here's how the list of URLs should be formatted:
{ "urls":[ "http://m.imdb.com", "http://m.amazon.com", "https://read.amazon.com"] }
The easiest way to get it to the correct spot on your device is via the command line using the Android Debug Bridge (ADB), which comes as part of the Android SDK. Assuming you've set up the SDK correctly, you only need to connect your device using USB, open a command line, change to the directory where your JSON file is located and run this command:
$ adb push amazon.testerurls.json /mnt/sdcard/
If your device and your computer share the same network, you can enable Web Developer Tools for debugging over WiFi. If you're on a corporate network, or want to test aspects of your app including offline functionality or WLAN speeds, you can connect via USB using ADB.
Helpfully, the Web App Tester gives you all the details you need to enable Remote Debugging once you start the app. Simply click the full screen handle at the bottom or side of the screen, and swipe down. There you'll see options to enable remote debugging using ADB or via your network.
Once you choose, a dialog box pops up with instructions and the exact URL to enter into your Chromium based browser (for debugging). For example, here are the instructions for remote debugging via WiFi:
Close that dialog, enter the URLof your web app to test and you'll be all set to debug via a desktop computer.
Once the Dev Tools page is open in your desktop browser, remote testing and debugging of your mobile web app should become as familiar as doing development for desktop browsers. Here are some things to watch for.
On Device Tools. First, note that all the normal functionality you'd find in Dev Tools is live, but running on the device itself. In fact the entire UI is a static HTML app served from the device, which then communicates back via Web Sockets. Viewing the network speed, recording the timeline activity or profiling is all happening with the device's hardware.
Reload Shortcut. When doing development for a desktop browser, you may be in the habit of clicking the reload icon in the browser to refresh the page. Rather than exiting out of your app on the device, and then re-starting, you can simply type Command-R/Control-R inside the remote Dev Tools window to refresh the contents of the page on the device itself.
Live Inspection. Just like on a desktop browser, you can use the inspect icon to help pinpoint elements on the screen within the HTML5 markup of your app - rather than clicking the screen, just activate the inspect icon, and then touch the screen to find that element within the Dev Tools. It goes in reverse as well, notice that as you use your mouse to hover over the markup in Dev Tools, the corresponding elements light up on the device.
FPS Meter. In the Dev Tools, you can use the settings icon to turn on the FPS meter, which displays in the top corner of your device. This will let you get a live view of how fast your app is refreshing, without having to add in additional libraries.
On Device Debugging and Console. You can step through JavaScript code just as you would normally. Additionally, the console is also live, with the JavaScript engine running on the device. This allows you to use console tricks such as $0 to refer to the selected element, navigate using document.location.href, or even pop up an alert() window if needed.
Because the Web App Tester's Dev Tools use the same remote debugging protocol as desktop Chromium browsers, they can be accessed not only from Dev Tools, but from text editors, IDEs or via scripting languages such as Python. Here's an example using the chrome-remote-interface Node.js module.
First, install the library using NPM:
npm install chrome-remote-interface
Then create a test.js file with this boilerplate example (modifying the options variable as needed):
var Chrome = require('chrome-remote-interface'); var options = { host: 'localhost', port: '9222' }; Chrome(options, function (chrome) { with (chrome) { on('Network.responseReceived', function (message) { console.log(message); }); on('Page.loadEventFired', function (message) { console.log("----------------------- page loaded "); console.log(message); }); Network.enable(); Page.enable(); Page.reload(); } }).on('error', function (err) { console.log(err); console.error('Cannot connect to Chrome'); )};
Then run the script
node test.js
As you use your app on the device, you'll see events logged in your console as they fire. The protocol and module will also let you send commands to the remote browser, letting you automate testing on the device and recording the results. For more info, check out the Remote Debugging Protocol pages here.
Hopefully some of these tips will come in handy as you're doing development for your web app, if you have any tips of your own or questions about using Dev Tools, definitely get in touch!
-Russ (@RussB)
January 08, 2014
David Isbitski
We want to start the year off by focusing on getting your Web Apps noticed in the Amazon Appstore, so you have the best chance of acquiring new customers. There are a few easy steps you can take to so that your apps, whether web or non-web apps, are more appealing to first time downloaders. This includes ensuring you have quality screenshots and promotional images, detailed product descriptions, an easy way for customers to review or share your app, as well as a way for customers to discover your apps outside of the Amazon Appstore. Amazon also offers unique ways to help your Web Apps get noticed including weekly promotions for the top Web Apps in the Appstore as well as access to Appstore Developer Select.
All Web Apps submitted to the Amazon Appstore will be automatically entered into for Appstore Developer Select, if they meet the program requirements. This program helps get your Web Apps discovered and gives you more opportunities to boost your sales and revenue. As a developer with one or more qualifying apps, you will receive the following benefits:
You can check the status of your eligibility by going to the My Apps section of the Apps and Services Tab in the Developer Console.
You can click on View Report to see which items your App has passed on and which items you need to update in order to become eligible.
Once your app has been enrolled in Appstore Developer Select you will also have the option to view the Ad Campaign that has been set up on your behalf.
The title and description of your app is like the front door of a building. Customers will never know what’s inside until they knock and walk inside. By writing an effective description you can change your app from the spooky abandoned house on the corner to an enticing new shopping mall just waiting to be explored.
The keywords section will be how customers find your app when searching the Amazon Appstore so make sure you chose relevant discoverability terms.
In addition to the App details you can also select a Content Rating for your app letting customers know what to expect.
Be sure to read our previous blog post with tips on effective title and description for your apps here.
Your Web App submission will need to provide screenshots of your app running as well as an optional video. You should create and upload both a 114x114 icon and a 512x512 icon, which will be used for the app list and carousel respectively.
In order to take the screenshots of your app running you will need to use the Web App Tester program on a Kindle device (emulators are not currently supported) and use the Kindle built in screenshot button combination. The button sequence for each Kindle device can be found here.
You will need to next copy the screenshots off of the Kindle device and uploaded to the developer portal.
On windows simply plug in your Kindle device and open File Explorer to Internal Storage > Pictures > Screenshots.
For Mac OSX you will need to use the Android File Transfer Tool and navigate to Pictures > Screenshots.
While you are only required to submit three screenshots, having additional screenshots as well as a promotional image is recommended and will help to increase the chance for downloads. You can also include an optional Video showcasing your Web App.
You can get additional information on taking screenshots of your Web Apps via the official developer documentation here.
Amazon also offers App Badges you can include on your Website and customer communications such as email blasts.
These Badges are available in a variety of languages and will further increase potential download traffic to your app by referring customers who are already interested in your products or have come across your website.
For full details on using App Badges be sure to check out the documentation here.
Building an audience for your apps and connecting with new customers is an important concern for most developers. By following the steps listed here you can ensure a higher chance of getting noticed. The more customers who see your app the greater the chance of increased downloads, more reviews and eventual promotion inside the Amazon Appstore.
Got a great HTML5 Web App in the Amazon Appstore? Looking to get promoted? Why not tell us about it! You can Contact Us directly and let us know.
-Dave (@TheDaveDev)
Starting today all Web App submissions to the Amazon Appstore now support Amazon Device Targeting. While it is possible to detect and optimize your mobile websites server side, you may decide you only want to target your Web Apps to specific devices. Web App submissions to the Amazon Appstore now allow the same device targeting capabilities that Android submissions do. This means you can elect to make your Web Apps available for download on Kindle Fire, Kindle Fire HD, Kindle Fire HDX, general Android (all non-Amazon) devices or any combination of these.
By enabling device targeting within your individual Web Apps you can tailor your app experience to the specific screen size, density, and capabilities of Kindle devices. For example, you may decide to target only devices capable of the Amazon Webview (Kindle Fire 2nd generation and up) giving your Web Apps native-like speeds on Kindle devices.
In the previous version of the developer portal you did not have the option of choosing the device on which devices your Web Apps would be available. By default your Web Apps would run on all Kindle Fire devices as well as Android capable devices.
As of today, a new Device Support section has been added that will allow you to target any or all of Web App capable devices you choose.
To enable Device Targeting in your Web Apps simply navigate to the Apps and Services, My Apps section in the developer portal, click on the title of your Web App, and then select the App File(s) tab.
You can then select the individual devices you want to target. By default we will enable all Web App capable devices so be sure to uncheck any devices you specifically do not want to support. Once you click the Save button you will find a new list of all the devices you have selected to support.
That’s it! By following these few steps you now have more control over which devices your Web Apps will run on, giving you the option to tailor your app experience to the capabilities of each device.
-Dave (@TheDaveDev)
The Amazon Mobile App Distribution program enables you to distribute your existing web apps on Kindle Fire and select Android devices in nearly 200 countries across the globe (including the US, Canada, UK, Germany, Spain, Italy, France, Brazil and Japan).
Almost any mobile friendly website you are hosting today can be run as a Web App. This means all of the HTML5, JavaScript, and CSS code you have built today can be made available as an App in the Amazon Appstore.
In the recent Webinar on Submitting HTML5 Web Apps to the Amazon Appstore, we covered how to get your existing web apps onto actual devices and debug and test them.
You can debug your HTML5 Web App in two different ways.
• You can use your favorite IDE or debugging tools for normal Web App development like you are already doing.
• Or, you can optionally use the Chrome DevTools to debug your web app using a Kindle Fire or an Android device running the Web App Tester Tool.
Note: The Chrome DevTools, the Chrome Browser installation tool, the Android SDK and certain other development tools are provided by third parties, not by Amazon.
The Chrome Developer Tools are freely available from Google and included in all the latest versions of the Chrome Browser. Simply select the Chrome menu at the top-right of your browser window, then select Tools > Developer tools to begin debugging. You can get additional information on installing the tools off the Chromium blog. The Chrome DevTools use the Android Debug Bridge (adb), part of the Android SDK tools, to communicate with the web app from your computer.
Next, we will need to install the Web App Tester Tool from the Amazon Appstore. This tool will allow us to create a bridge to our computer over USB or WiFi.
Simply swipe from the top of the device (if on a Kindle Fire device make sure you tap the full screen handle on the bottom of the screen to open the status bar) and then swipe down from the top of the screen to display the Quick Settings Notification Area. Once there you will have the option to connect over either USB or WiFI.
In this example I have selected WiFi and am given a url I can put into my browser to connect the Chrome Dev Tools to that running app.
Here is a screenshot of a Kindle Fire running my Web App with the Chrome Debug Tools showing the source for my app. This enables me to now select actual <div> tags now in the html code via Chrome on my computer and see those <div> tags being highlighted on my actual device (as in the example below).
By utilizing the free Chrome Developer Tools and the free Web App Tester tool we are now able to debug our Web App on an actual device in real time. This includes everything you would expect from the Chrome Developer Tools, like real time resource monitoring, the ability to view our html source and a JavaScript console.
For a complete walkthrough of the steps described here you can watch the short screencast below as well as checking out the web app debug documentation on our developer portal here.
-Dave (@TheDaveDev)
Today we have a video interview with the executive leadership of Tre Sensa (http://www.tresensa.com/) a NYC based game development and distribution company that optimizes games for the mobile web. In this video, they talk about their business and the importance of HTML5 in their goal to deliver apps to as many screens as possible. They also discuss their experience developing HTML5 apps for Amazon mobile devices as well as a talk about how they came to use Amazon Web Services to support their mobile gaming applications.
We hope you enjoy this video interview with TreSensa.