In this two-part tutorial, you'll learn how to use JavaScript and HTML to create a card matching game powered by the Flickr API. Check out the demo, and read on!
Also available in this series:
- Build a Flickr-Based Pairs Game With JavaScript: Gameplay
- Build a Flickr-Based Pairs Game With JavaScript: Polish
Step 1: Getting a Flickr Account
To be able to use the Flickr API you must be a registered user on flickr.com. From the homepage of Flickr, click the Sign Up link.

Step 2: Obtaining an API Key
Once logged in, you will need to visit the App Garden to get started.
You will want to bookmark this page if you are planning on doing a lot of Flickr development, as it contains a lot of useful information for developers.
Click on the “Create An App” link once you arrive at the App Garden.

Under “Get your API Key”, click the “Request an API Key” link.

Here you will need to choose whether you intend to use the app for commercial or non-commercial purposes. For this game I chose non-commercial.

Next you will be taken to the App Details page. Enter the name of your app and a description of what your app does, accept the agreements, and click the Submit button.

Next, you will be presented with your key and your secret. We will not be using the secret key here as our app does not require authentication. Make a note of your API key, as we will need it for the rest of this tutorial.

Step 3: Diving Into the Flickr API
Flickr has a REST API for developers. We will be using these two methods from the API:
The flickr.photos.search
method will allow us to search for photos, while the flickr.photos.getInfo
method will allow us to get information for a single photo, such as the username (owner) of the photo, the title of the photo, and the URL to the photo's page on Flickr.
If you visit one of the links above, at the bottom of the page there is a link to the API Explorer where you can enter some data and get an example response.

The image above is for the flickr.photos.search
method. Go ahead and click the link now.
There are lots of options, and it may seem overwhelming, but all we are interested in for this tutorial is the “tags” option; I entered “dog” into the tags search box. Choose JSON as the output method, as we will be using JSON in this tutorial.


Finally, press the “Call Method” button, and an example of the response will be returned. Here's a portion of the sort of data you can expect to receive - the last few lines have been cut out:
{ "photos": { "page": 1, "pages": "49825", "perpage": 100, "total": "4982408", "photo": [ { "id": "7338255166", "owner": "45596890@N00", "secret": "5f145a92b8", "server": "7083", "farm": 8, "title": "Cute Rhea", "ispublic": 1, "isfriend": 0, "isfamily": 0 }, { "id": "7338254718", "owner": "45596890@N00", "secret": "9e1da794a3", "server": "7223", "farm": 8, "title": "Rhea chewing", "ispublic": 1, "isfriend": 0, "isfamily": 0 }, { "id": "7338258850", "owner": "8672236@N04", "secret": "d0a5c4124c", "server": "8027", "farm": 9, "title": "", "ispublic": 1, "isfriend": 0, "isfamily": 0 }, { "id": "7338271122", "owner": "49270434@N08", "secret": "30876cfdf6", "server": "7236", "farm": 8, "title": "286", "ispublic": 1, "isfriend": 0, "isfamily": 0 }, { "id": "7338235972", "owner": "8640124@N02", "secret": "a583d6aa48", "server": "7085", "farm": 8, "title": "Y:4 Day 123: Wind sweeps", "ispublic": 1, "isfriend": 0, "isfamily": 0 }, { "id": "7330075254", "owner": "32081016@N07", "secret": "04cb99e8a7", "server": "7239", "farm": 8, "title": "Mad dogs and Englishmen.....", "ispublic": 1, "isfriend": 0, "isfamily": 0 }, { "id": "7338229968", "owner": "67178219@N06", "secret": "92b4be222a", "server": "7095", "farm": 8, "title": "Le petit chien dans la prairie", "ispublic": 1, "isfriend": 0, "isfamily": 0 }, { "id": "7338185610", "owner": "7315798@N04", "secret": "9a91bd1280", "server": "7224", "farm": 8, "title": "snuggle", "ispublic": 1, "isfriend": 0, "isfamily": 0 }, { "id": "7338111264", "owner": "80044484@N06", "secret": "f795c559e3", "server": "7214", "farm": 8, "title": "DSC_0408", "ispublic": 1, "isfriend": 0, "isfamily": 0 }, { "id": "7338135744", "owner": "80023312@N07", "secret": "d37c015be6", "server": "7234", "farm": 8, "title": "0007_Ridgeback", "ispublic": 1, "isfriend": 0, "isfamily": 0 }, { "id": "7338136008", "owner": "80023312@N07", "secret": "259e50ebaa", "server": "7219", "farm": 8, "title": "0003_Pug", "ispublic": 1, "isfriend": 0, "isfamily": 0 }, { "id": "7338133290", "owner": "80044484@N06", "secret": "a2e954aab0", "server": "7098", "farm": 8, "title": "_DSC0032", "ispublic": 1, "isfriend": 0, "isfamily": 0 }, { "id": "7338142010", "owner": "80023312@N07", "secret": "34809c804f", "server": "7071", "farm": 8, "title": "0009_WireFox", "ispublic": 1, "isfriend": 0, "isfamily": 0 }, { "id": "7338014824", "owner": "36700168@N04", "secret": "2625ab12a4", "server": "8164", "farm": 9, "title": "Nosferatus` dog", "ispublic": 1, "isfriend": 0, "isfamily": 0 }, { "id": "7338092614", "owner": "80023312@N07", "secret": "ee7210e0f1", "server": "8010", "farm": 9, "title": "0002_GreatDane", "ispublic": 1, "isfriend": 0, "isfamily": 0 }, { "id": "7338120200", "owner": "80023312@N07", "secret": "38b5c49f4f", "server": "7245", "farm": 8, "title": "0006_Cavalier", "ispublic": 1, "isfriend": 0, "isfamily": 0 }, { "id": "7338159814", "owner": "7315798@N04", "secret": "327e97cb13", "server": "7104", "farm": 8, "title": "puppy love", "ispublic": 1, "isfriend": 0, "isfamily": 0 },
We will be using the data from the response to construct our URLS to the images. The URLS take this form:
http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}_[mstzb].jpg
To make a usable URL we just replace what is inside the {} with the data we get from above. For example, the first item from the photo
array inside the above JSON is:
{ "id": "7338255166", "owner": "45596890@N00", "secret": "5f145a92b8", "server": "7083", "farm": 8, "title": "Cute Rhea", "ispublic": 1, "isfriend": 0, "isfamily": 0 }
...so the URL we need to construct is this:
http://farm8.static.flickr.com/7083/7338255166_5f145a92b8_q.jpg
The "q" denotes the size of the image: 150x150px.
That link leads to this photo. More info about the URLs can be found in the Flickr Documentation for Photo Source URLS.
Step 4: Setting up the Project
In the source download there is a ZIP file named "StartHere.zip" that contains the files all laid out for you, ready to add some code. You can browse through these files to get and idea of the project's structure. I have also included a "README.txt" that explains the structure in more detail.
We are using the following JavaScript libraries in this project. You can download them separately if you wish.
- jQuery
- jQuery.imgpreload
- jQueryUI - I chose the Cupertino theme
Step 5: document.ready()
Inside the "js" folder you will find a JavaScript file name "flickrgame.js". Add the following code to this file.
$(document).ready(function() { alert("Ready"); });
If you test the "index.html" page now, you should see an alert with the word "Ready".
This code uses jQuery's .ready()
method, which gets fired when the DOM has completely loaded. It is important to call this method when coding your apps, because you need to ensure the DOM has been loaded before you try to manipulate it. For example, if you tried to add a click event handler to a button before the DOM was loaded and ready, it would have no effect.
Step 6: flickrGame
Object Literal
We will be using an object called flickrGame
to keep all of our variables neat and tidy and out of the global namespace.
Enter the following code within the $(document).ready()
you added in the step above, replacing the alert.
$(document).ready(function() { var flickrGame = { } });
Step 7: Add Your API Key
Within the flickrGame
object literal add the following code. Replace the "YOUR API KEY" with the API Key you received when you signed up.
$(document).ready(function() { var flickrGame = { APIKEY: "YOUR API KEY" } });
Step 8: doSearch()
The doSearch()
method will call the Flickr API. Enter the following beneath the flickrGame
object literal definition (but still inside the $document.ready()
function):
function doSearch() { var searchURL = "http://api.flickr.com/services/rest/?method=flickr.photos.search"; searchURL += "&api_key=" + flickrGame.APIKEY; searchURL += "&tags=dog" searchURL += "&per_page=36" searchURL += "&license=5,7"; searchURL += "&format=json"; searchURL += "&jsoncallback=?"; $.getJSON(searchURL, setImages); }
Here we build up our query step by step. We point the searchURL
variable to the flickr.photos.search method of the Flickr API.
As it is a REST API, to call it we just need to construct a URL with the parameters required. So, here we are using the flickr.photos.search
method, and requesting images tagged with "dog". We've set the format to "json" and set "jsoncallback=?", which makes the callback function execute as soon as the data is retrived.
By setting the license parameter tp 5,7
we are asking Flickr to only give us images with an Attribution-ShareAlike licence or with no known copyright license.
We then use jQuery's $.getJSON() method, passing in our searchURL
and a reference to the callback function setImages()
which we will code next.
Step 9: setImages()
In this step we will code the setImages()
function and view the results we get back from Flickr. Enter the following code beneath the doSearch()
function you added in the step above.
function setImages(data) { var tempString = ""; $.each(data.photos.photo, function (i, item) { var imageURL = 'http://farm' + item.farm + '.static.flickr.com/' + item.server + '/' + item.id + '_' + item.secret + '_' + 'q.jpg'; tempString += imageURL+"<br/>"; }); $("#urls").html(tempString); }
Here we initialise a variable tempString
that will hold the URLs we get from Flickr. Within each object, the parameter data
holds the JSON that was returned from the call to the Flickr API, as laid out in Step 3 - so we can see that the individual photo objects are to be found in data.photos.photo
.
We can use jQuery's $.each() method to loop through each JSON photo object that was returned. Inside the loop we construct the imageURL
, again as laid out in Step 3, then append it to tempString
, and finally output tempString
to the div named "urls" by directly modifying the HTML of the div.
The point here is to see what URLs we are getting from our call to the Flickr API. If you are following along with the source code, I added a div with id="urls"
so we could view this in the web page. (I will be removing it in the next step.)
Lastly, to actually view it we need to call our doSearch()
function. Add this right below the setImages()
function.
doSearch();
You can test it here. It may take a second or two before you see this data, depending on your connection speed. Below are the first few URLs returned, in case you are not following along with the source code.
http://farm9.static.flickr.com/8025/7183874333_9b8b43dfe1_q.jpg http://farm9.static.flickr.com/8003/7368215016_3c42485ee9_q.jpg http://farm6.static.flickr.com/5467/7179453807_a4c871311f_q.jpg http://farm9.static.flickr.com/8002/7364322976_4f02a954ea_q.jpg http://farm9.static.flickr.com/8026/7177956063_6d92435602_q.jpg http://farm8.static.flickr.com/7221/7177959671_025cc2381b_q.jpg
Step 10: Storing the Images
We will be storing all the URLs we get back from Flickr in an array, rather than jammed together as a single string. Add the following to the flickrGame
object.
var flickrGame = { APIKEY: "76656089429ab3a6b97d7c899ece839d", imageArray: [] }
(Make sure you add the comma after the APIKEY: "YOUR API KEY"
.)
Now, add the following within the setImages()
function. I removed the code from the previous step, as it was just for demonstrative purposes.
$.each(data.photos.photo, function (i, item) { var imageURL = 'http://farm' + item.farm + '.static.flickr.com/' + item.server + '/' + item.id + '_' + item.secret + '_' + 'q.jpg'; flickrGame.imageArray.push({ imageUrl: imageURL, photoid: item.id }); });
Here we push the imageURL
and item.id
onto the imageArray
using the keys imageUrl
and photoid
respectively. We need the imageUrl
to construct our images, and the photoid
will be used to identify the particular photo. We also need to show an attribution for the photos we use, and by having their id we can link the user to the photo on Flickr.
The need for all of this data will become clearer as we move through the tutorial, so don't worry too much about it for now.
Step 11: infoLoaded()
The infoLoaded()
function will be used for loading the information of the photos. After all of the photo information has been loaded, infoLoaded()
will call a function named preloadImages()
which will preload the images.
Once all of the images have have been preloaded we call drawImages()
, which places the images on screen, and the game begins.
Add the following beneath the setImages()
function.
function infoLoaded(data) { }
The data
parameter will hold the JSON of each particular photo. We will come back to this in a bit; for now we will just use this function to call preloadImages()
directly. Go ahead and add the call now:
function infoLoaded(data) { preloadImages(); }
Step 12: preloadImages()
The preloadImages()
function will be used to create and preload the images for the start of the game, and for each level. We preload them so there is no delay between when we show the images and when the are downloading.
We will come back to the preloading code later. For now, we will just create the first 12 images for the start of the game. We need a new array to hold these 12 images, so add the following to your flickrGame
object:
var flickrGame = { APIKEY: "76656089429ab3a6b97d7c899ece839d", imageArray: [], tempImages:[] }
Make sure you add a comma after imageArray: []
.
The tempImages
array is used to hold 12 images at a time. Our game uses 12 images per level, and the imageArray
holds up to 36 images (depending on how many we set in our search to Flickr). So we will grab 12 images from the imageArray
and store them in tempImages
.
Add the following code beneath the infoLoaded()
function you added in the step above.
function preloadImages(){ flickrGame.tempImages = flickrGame.imageArray.splice(0, 12); for (var i = 0; i < flickrGame.tempImages.length; i++) { for (var j = 0; j < 2; j++) { var tempImage = new Image(); tempImage.src = "cardFront.png"; tempImage.imageSource = flickrGame.tempImages[i].imageUrl; flickrGame.theImages.push(tempImage); } } }
Here we use the javascript method splice()
to pull 12 images out of the imageArray
, and place them into the tempImages
array. We then use a nested for
loop to create 24 images.
For each of these 24 images, we create a new Image
, set its src
to "cardFront.png" (an image you'll see before the card is flipped over), create a new property named imageSource
that contains the URL of the actual image from Flickr to be displayed, and then push it into the array theImages
.
We need to add theImages
to our flickrGame
object, so do that now.
var flickrGame = { APIKEY: "76656089429ab3a6b97d7c899ece839d", imageArray: [], tempImages:[], theImages: [] }
Make sure you add the comma after tempImages[]
.
Lets go ahead and call the drawImages()
function as well; once we've written it, this will render the images on the screen. Add the following after the nested for
loop in the preloadImages()
function.
function preloadImages(){ flickrGame.tempImages = flickrGame.imageArray.splice(0, 12); for (var i = 0; i < flickrGame.tempImages.length; i++) { for (var j = 0; j < 2; j++) { var tempImage = new Image(); tempImage.src = "cardFront.png"; tempImage.imageSource = flickrGame.tempImages[i].imageUrl; flickrGame.theImages.push(tempImage); } } drawImages(); }
Step 13: Shuffling and Rendering
The drawImages()
function actually puts the images on screen. We first shuffle the images in the theImages
array using a function called randSort
and then append them to the #gamediv
section of the HTML.
Enter the following code below the flickrGame
object definition:
function randOrd() { return (Math.round(Math.random()) - 0.5); }
This function is used to shuffle the images. We will call JavaScript's sort()
method on an array and pass in randOrd
as the parameter. This works in basically the same way as outlined in "The Sorting Approach" in this article.
Add the following beneath the preloadImages()
function you added in the step above:
function drawImages() { flickrGame.theImages.sort(randOrd); for (var i = 0; i < flickrGame.theImages.length; i++) { $(flickrGame.theImages[i]).attr("class", "card").appendTo("#gamediv"); } }
We call the sort method on theImages
, passing in our randOrd
function, then loop through theImages
, appending each image to the #gamediv
.
We use the jQuery method .attr
to add a class of "card" to each image. Why? Because when we end a level, we remove the cards from the #gamediv
; this gives us a way to target just the cards and not all images.
Here are links to jQuery's attr() and appendTo() methods so you can learn more about them.
Step 14: Testing our Progress
We should be ready to get some cards on the screen at this point. After the .each()
call in setImages
call the infoLoaded()
function and test the page.
function setImages(data) { $.each(data.photos.photo, function (i, item) { var imageURL = 'http://farm' + item.farm + '.static.flickr.com/' + item.server + '/' + item.id + '_' + item.secret + '_' + 'q.jpg'; flickrGame.imageArray.push({ imageUrl: imageURL, photoid: item.id }); }); infoLoaded(); }
If all goes well you should see 24 images with the Flickr Logo on screen. It may take a moment before they show, because we are calling the Flickr API and loading the images as well.
We will make a small change to the code so we can see the actual images for now. Modify the preloadImages()
function to show the actual image instead of the "cardFront.png":
function preloadImages(){ flickrGame.tempImages = flickrGame.imageArray.splice(0, 12); for (var i = 0; i < flickrGame.tempImages.length; i++) { for (var j = 0; j < 2; j++) { var tempImage = new Image(); tempImage.src = flickrGame.tempImages[i].imageUrl; //"cardFront.png"; tempImage.imageSource = flickrGame.tempImages[i].imageUrl; flickrGame.theImages.push(tempImage); } } drawImages(); }
Step 15: Adding Click Listeners
The addListeners()
function will add a click listener to each of the images that calls a function doFlip()
, which in turn will reveal the image beneath (if the logo is currently displayed).
Add the following beneath the drawImages()
function you added in the step above:
function addListeners() { for (var i = 0; i < flickrGame.theImages.length; i++) { $(flickrGame.theImages[i]).on("click", function (e) { doFlip(e); }); } }
We add the listeners in the drawImages()
function:
function drawImages() { flickrGame.theImages.sort(randOrd); for (var i = 0; i < flickrGame.theImages.length; i++) { $(flickrGame.theImages[i]).attr("class", "card").appendTo("#gamediv"); } addListeners(); }
Step 16: Removing Click Listeners
We need a way to remove the click listeners from the images as well. Add the following beneath the addListeners()
function you added in the step above.
function removeListeners() { for (var i = 0; i < flickrGame.theImages.length; i++) { $(flickrGame.theImages[i]).off("click"); } }
Step 17: doFlip()
The doFlip()
function is called when the user clicks on one of the images. It displays the big images on the left and right of the main gamplay area, and then calls a function checkForMatch()
which checks whether the images match.
Add the following beneath the removeListeners()
function you added in the step above.
function doFlip(e) { var theCard = e.target; $(theCard).attr("src", theCard.imageSource); if ($('#image1').css('backgroundImage') == "none") { $('#image1').css('backgroundImage', 'url(' + theCard.imageSource + ')'); } else { $('#image2').css('backgroundImage', 'url(' + theCard.imageSource + ')'); } if (flickrGame.chosenCards.indexOf(theCard) == -1) { flickrGame.chosenCards.push(theCard); $(theCard).off("click"); } if (flickrGame.chosenCards.length == 2) { removeListeners(); checkForMatch(); } }
We first get a reference to which card was clicked and set its src
attribute to the URL of the actual image (rather than the Flickr logo).
We then check whether the CSS background of the big image on the left (#image1
) is equal to "none", and if it is we set its background image to the same image as the card we clicked on. If it is not equal to "none" then that means a card has been clicked, so we set the background image of the big image on the right.
We check that the chosenCards
array (which we will add in a moment) does not already contain this card, for safe housekeeping, and then push it onto the array. We also remove the click event from the card so they cannot click it again.
If the chosenCards
length is equal to 2 it means the user has chosen two cards, so we call removeListeners()
to remove the click event from the rest of the cards and call the checkForMatch()
function (which we will code in the next step) to see if the selected cards match.
We need to add the chosenCards
array to our flickrGame
object, so do that now:
var flickrGame = { APIKEY: "76656089429ab3a6b97d7c899ece839d", imageArray: [], tempImages:[], theImages: [], chosenCards: [] }
Make sure to add a comma after theImages[]
.
Step 18: checkForMatch()
The checkForMatch()
function checks whether the two clicked cards match. If they do then we "hide" them; if they don't then we add the listeners back to the remaining cards and let the player have another go.
Add the following beneath the doFlip()
function you added in the step above:
function checkForMatch() { if (flickrGame.chosenCards.length == 2) { if ($("#image1").css('background-image') == $("#image2").css('background-image')) { setTimeout(hideCards, 1000); } else { setTimeout(resetImages, 1000); } } }
Here, we check whether the chosenCards
length is equal to 2. If it is, we check whether the two big images are the same by using their background-image
. (We could have used the smaller images instead; as an exercise, see if you can do this.)
If the images do match, we use setTimeout()
to call the hideCards()
function after one second; otherwise we call resetImages()
after one second. By delaying these function calls we add a very brief pause to the game.
We'll code the functions to hide the cards and reset the images next.
Step 19: hideCards()
If the user matched two cards, we hide them both and add the click listeners back to the rest of the cards.
Add the following code beneath the checkForMatch()
function you created in the step above:
function hideCards() { $(flickrGame.chosenCards[0]).animate({ 'opacity': '0' }); $(flickrGame.chosenCards[1]).animate({ 'opacity': '0' }); flickrGame.theImages.splice(flickrGame.theImages.indexOf(flickrGame.chosenCards[0]), 1); flickrGame.theImages.splice(flickrGame.theImages.indexOf(flickrGame.chosenCards[1]), 1); $("#image1").css('background-image', 'none'); $("#image2").css('background-image', 'none'); addListeners(); flickrGame.chosenCards = new Array(); }
We use jQuery's animate() method to tween both cards' opacity to 0, making them appear to fade out. We also remove the images from the theImages
array, reset the big images' backgrounds to "none", add the listeners back to the other images, and reset our chosenCards
array.
Step 20: resetImages()
This function is run if the two images selected do no match. It resets the small images' src
attributes back to "cardFront.png", sets the big images' background to "none", adds the listeners back to the cards, and resets the chosenCards
array.
Add the following beneath the hideCards
function you added in the step above.
function resetImages() { $(flickrGame.chosenCards[0]).attr("src", "cardFront.png"); $(flickrGame.chosenCards[1]).attr("src", "cardFront.png"); $("#image1").css('background-image', 'none'); $("#image2").css('background-image', 'none'); addListeners(); flickrGame.chosenCards = new Array(); }
You can actually play the game now - except that the images are showing. To change that back, alter the preloadImages()
function like so:
function preloadImages(){ flickrGame.tempImages = flickrGame.imageArray.splice(0, 12); for (var i = 0; i < flickrGame.tempImages.length; i++) { for (var j = 0; j < 2; j++) { var tempImage = new Image(); tempImage.src = "cardFront.png"; tempImage.imageSource = flickrGame.tempImages[i].imageUrl; flickrGame.theImages.push(tempImage); } } drawImages(); }
(It is good to change this back and forth for easy testing.)
End of Part 1
That's the end of the first part of this tutorial. So far, you've made a functional HTML5 game; in the next part, you'll add polish, learning how to:
- Let the player choose their own tag, rather than playing with pictures of dogs all the time.
- Obtain and display attribution information for all of the photos used.
- Add multiple rounds and a scoring system to the game.
- Preload the images and display a progress bar for this.
See you then!
Subscribe below and we’ll send you a weekly email summary of all new Game Development tutorials. Never miss out on learning about the next big thing.
Update me weeklyEnvato Tuts+ tutorials are translated into other languages by our community members—you can be involved too!
Translate this post