JavaScript Game Development Course for Beginners

Learn to make 2D games with HTML, CSS & plain vanilla JavaScript, no frameworks and no libraries! From sprite animation to state management, in this series of projects you will learn everything you need to make your own 2D animated games! We will go step by step explaining each technique on a small standalone codebase and then we will use everything we learned to make a single final game.

✏️ Course by Frank’s Laboratory.

⭐️ Assets ⭐️
🕹 Project 1: Vanilla JavaScript sprite animation techniques

🕹 Project 2: Parallax backgrounds with JavaScript
Layers Zip:

🕹 Project 3: Enemy movement patterns
Enemies Zip:

🕹 Project 4: Collision animations from a sprite sheet

🕹 Project 5: Point & shoot game
dust cloud:

🕹 Project 6: Enemy variety in JavaScript games

🕹 Project 7: Side-scroller game with mobile support

🕹 Project 8: State management in JavaScript games

🕹 Project 9: Final endless runner game with all the features

🕹 City background layers:
Layer 1:
Layer 2:
Layer 3:
Layer 4:
Layer 5:

🕹 Fire texture:

🕹 Collision animation:

🕹 Lives:

🕹 Forest background layers:
Layer 1:
Layer 2:
Layer 3:
Layer 4:
Layer 5:

🕹 18 Enemies:
Big spider:
Bat 1:
Small spider:
Ghost 1:
Bat 2:
Bat 3:
Ghost 2:
Ghost 3:
Ghost 4:
Walking zombie:
Ground zombie:

🔗 More art assets:

⭐️ Contents ⭐️
0:00:00 Intro
0:01:28 Project 1: Vanilla JavaScript sprite animation techniques
0:43:07 Project 2: Parallax backgrounds
1:25:33 Project 3: Enemy movement patterns
2:13:31 Collision detection between rectangles
2:19:23 Collision detection between circles
2:24:14 Project 4: Collision animations from a sprite sheet
2:48:35 Project 5: Point & shoot game
3:50:44 Project 6: Enemy variety
4:45:49 Project 7: Side-scroller game with mobile support
5:54:04 Project 8: State management
7:02:57 Project 9: Final endless runner game with all the features


  1. Hope you have fun with the projects! 😊 If you have any questions or have issues downloading the art assets let me know! Thanks FCC for sharing my course! ❤🙏

  2. They helped! Really.. helped. Although i know some basics on .js, i still find it hard to understand some of what he did with dah dog sprite but as i kept watching and flowing along i came to get what he was talking about. Really great video fr someone like me who wants to work in the game industry, and i also think 2d sprite is a good start up for me. Thanks for this great video Frank, they're awesome.. 😄

  3. At 5:09:55 the gap you see is a background defect (if you merge two backgrounds horizontally in Photoshop you will see the same gap due to the one pixel wide empty column on the left side of the image). So, when you try to compensate for this gap by subtracting the speed value, you shift the second background in such a way that it does not match the first one in the picture. So to get rid of this gap you just have to compensate 1px regardless of speed. And your background will move perfect an any speed. The only thing you have to do – is to compensate the same 1px when sliding background to the start position to avoid background jerking:

    draw(context) {

    context.drawImage(this.image, this.x, this.y, this.width, this.height);



    this.x + this.width – 1, <<—– compensate 1px when merging






    update() {

    this.x -= this.speed;

    if (this.x < 0 – this.width + 1) this.x = 0; <<—— compensate 1px when sliding back to the start position


  4. About keydown / keyup tracking at 4:54:50. You can check key pressing in more compact way:
    keyTrackingList = ["ArrowDown", "ArrowUp", "ArrowLeft", "ArrowRight"];
    if (keyTrackingList.includes(e.key) && !this.keys.includes(e.key))

  5. Man, I'm so stoked about this! I've started making simple arcade games on my own, but it'll be cool to eventually implement 2D scrollers and platformers. Thanks for sharing FCC and FL!

  6. I have an issue regarding the project: 5 point and shoot game. When I created the collisionCanvas, nothing would show up on the screen, and there is no error messages. Also, nothing shows up when I console.log(); anything in the developer tools. I know it's a logic error, but I having trouble finding a solution.

  7. 4:12:40 you're filtering the array of "enemies" with each update, which is an "expensive" operation if you repeat it every 16 ms, all the more it makes no sense to do this with each update, you can filter objects with the same frequency with which they appear, in this case every 500ms inside an if statement where you mark objects as 'marketForDeletion'.

  8. 3:30:54 you can SIMPLIFY color comparison
    FROM: if (object.randomColors[0] === pc[0] && object.randomColors[1] === pc[1] && object.randomColors[2] === ps[2])
    TO: if (JSON.stringify(object.randomColors) === JSON.stringify(pc.slice(0, 3)))
    It looks clearer and less cumbersome.
    And you can do even better if in line 70 you assign the '[…].slice(0, 3)' value rather then '[…]' to the 'pc' variable to shorten the conditional expression
    (JSON.stringify(object.randomColors) === JSON.stringify(pc))
    which is even more readable and clear, and almost two times shorter then
    (object.randomColors[0] === pc[0] && object.randomColors[1] === pc[1] && object.randomColors[2] === ps[2])
    so as result you'll get the next code:

    const pc = […].slice(0, 3);

    ravens.forEach((object) => {

    if (JSON.stringify(object.randomColors) === JSON.stringify(pc)) {

    object.markedForDeletion = true;




  9. 3:24:42 you can SIMPLIFY 'this.color' expression
    FROM: 'this.color = 'rgb(' + this.randomColors[0] + ',' + tihs.randomColors[1] + ',' + this.randomColors[2] + ')'
    TO: 'this.color = 'rgb(' + this.randomColors.join(',') + ')'

  10. About Chance that there will be two identical colors generated next to each other (3:26:10):
    1. Chance of two identical colors: 1/256^6 (256^6 means: 256 to the power of 6)
    2. Chance of at least two identical colors: 1/256^6 + 1/256^9 + 1/256^12 + …. + 1/256^(3*n) – where '"n" is the number of ravels.
    Сalculations are correct assuming a normal distribution of random values returned by the Math.random() method.

  11. About deleting unused objects (which hid behind the left side of the screen 03:07:15). I think the best way to remove them is to remove them in the "update" method of the objects, this way we will significantly reduce the load on the processor, because. in this case, the "filter" method iterates over the raven array only when objects need to be removed, and not every animation cycle. To see the difference – just call console.log() in both cases (console.log should be called in the same place as "filter") and feel the difference.

  12. I have to really admit that at the end I really don't like that you are speeding up your recording, it is really hard to follow along, even if you are making changes to the code and explaining something at the same time…. I don't think that learning Game Developtment should be a rush

  13. project one ,very end when i add the dropdown and dropdown listenin event the code breaks, it does not let me switch state with the drop down. nothing appears at all, please advise

  14. About getting the XY coordinates of the cursor at the click point (2:35:00). You can get the coordinates of the cursor relative to the canvas directly, without any calculations, using the global coordinates of the cursor and the position of the canvas. To do this, you must use the canvas onclick event listener, not the window event listener, and then get the coordinates from the offsetX and offsetY properties of the event object.

  15. The problem of jumping background layers when changing speed is solved very simply (1:24:20).

    Along with the frame rate, you are essentially changing the speed of the game's timeline. In this case, the time in the game is set by the total number of frames. By changing the speed, you recalculate the total number of frames each time. The game jumps to a new position in the timeline, and the background layers jump to new coordinate positions corresponding to the game's position in the timeline. To save the current coordinates of the background layers, you need to save the position of the game on the timeline. In other words, when changing the speed, it is necessary to save the total number of frames. Because the total number of frames depends on the number of the "gameFrame" and on the "gameSpeed" then when changing the speed, it is necessary to change the "gameFrame" inversely proportionally to the change in speed. In the context of the code, this is just one line in the "onchange" event handler function of the slider, where you need to get the new number of the "gameFrame" by simply dividing the total number of frames by the new speed value:

    gameFrame = (gameFrame * gameSpeed) /;

    Important: you must place this line of code at the very beginning of the handler function, where the "gameSpeed" ​​variable remains its value until the speed is changed. I have checked this solution, it works perfectly.

  16. Hello! At 1:00:05 – in the line 27 "… – gameSpeed" is redundant. In the line 25 it's ok, because at the moment when you move the background x1 ahead the background x2, using the x2 value as an offset, x2 keeps the value from the previous iteration, so you have to subtract one more "gameSpeed" value from the x1, because in current iteration the background x2 hasn't made "gameSpeed" step, but when you move the background x2 ahead the background x1, you concatenate it with x1 in the same iteration, specifically when the background x1 has already made a gameSpeed step to the left. This will work correctly only when both if statements are in a row, and after them both expressions for calculating the x coordinate as at 1:08:02, because in this case both x1 and x2 still contain values from the previous iteration.

  17. For everyone who is seeing this, if you want to practice your object oriented programming skills, this tutorial is really really good for that!

  18. Just finished the course from beginning to end. Had a blast the entire way through! This was a really fun way to learn some new stuff in JavaScript. Gonna take what I learned here and build a 2D game for my portfolio! Keep it up my friend and keep making great content! Thanks <3

  19. Hi guys I have a question, since all of this code is client side, does it mean that anyone could see my source code? Is there a way to apply these lessons in cases where users can't see (and copy) the code?

Leave a Reply

© 2023 53GB