
The Arcade card
Make a game someone can actually sit down and play! A platformer, a puzzle, a tiny roguelike, a card game… if a friend can pick it up and have fun, it counts.
Make a game someone can actually sit down and play! A platformer, a puzzle, a tiny roguelike, a card game… if a friend can pick it up and have fun, it counts.
What counts
A game someone else can play. They make choices, and those choices matter: they can win, lose, or chase a better score. Any genre, engine or size is fine. We’d much rather play a tiny game that’s polished and fun than a huge one that’s half there!
Text adventures count, and so do card games, clickers, puzzle games and interactive stories where your choices change the ending. A game engine, a framework or a level editor with nothing to play in it doesn’t count, and neither does a toy you can poke at with no goal at all.
Using an engine like Godot or Unity is totally fine. So is art or music someone else made, as long as you’re allowed to use it and you credit it. Tutorials are a great way to learn, but the game you ship has to be your own: a tutorial’s game with a new sprite on it doesn’t count.
A rhythm game fits here and on the Rhythm card too, and a game that runs on a real Game Boy fits the Retro card. Their guides cover everything specific to those, so check them out if that’s where you’re headed!
Your first ten minutes
Let’s build the core of a tiny game in plain JavaScript, right in your browser. You don’t need to install anything, and it’s easy to put online later.
Rather use an engine? Godot is free and can export to the web, LÖVE is a lovely little framework for Lua, and TIC-80 (free) and PICO-8 (paid) are fantasy consoles with the code, sprite and sound editors all built in. Python people can use Pygame. The steps below are the same ideas in any of them.
1. Make two files. An index.html:
<!DOCTYPE html>
<title>My game</title>
<canvas id="game" width="640" height="480"></canvas>
<script src="game.js"></script>
…and an empty game.js next to it. Open index.html in your browser. You’ll see nothing at all, which is correct!
2. Draw something. Put this in game.js and reload:
const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");
const player = { x: 300, y: 220, size: 32, speed: 240 };
ctx.fillStyle = "hotpink";
ctx.fillRect(player.x, player.y, player.size, player.size);
A pink square! That’s you, for now.
3. Add a game loop. A game redraws itself many times a second. requestAnimationFrame calls your function right before the browser draws the next frame, and dt is how many seconds passed since the last one. Swap the last two lines for this:
function update(dt) {}
function draw() {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "hotpink";
ctx.fillRect(player.x, player.y, player.size, player.size);
}
let last = 0;
function frame(now) {
const dt = Math.min((now - last) / 1000, 0.1);
last = now;
update(dt);
draw();
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
The Math.min stops everything from jumping forward when you switch tabs and come back.
4. Make it move. Keep track of which keys are held down, and fill in update:
const keys = new Set();
addEventListener("keydown", (e) => keys.add(e.code));
addEventListener("keyup", (e) => keys.delete(e.code));
function update(dt) {
if (keys.has("ArrowLeft")) player.x -= player.speed * dt;
if (keys.has("ArrowRight")) player.x += player.speed * dt;
if (keys.has("ArrowUp")) player.y -= player.speed * dt;
if (keys.has("ArrowDown")) player.y += player.speed * dt;
}
Multiplying by dt means you move 240 pixels a second, on a 60 Hz laptop and a 144 Hz gaming monitor alike. Try it! You’ll notice you can walk right off the screen. Fixing that is your first job.
5. Give it a goal. A coin, a score, and a way to tell when two boxes touch:
const coin = { x: 100, y: 100, size: 16 };
let score = 0;
function overlaps(a, b) {
return a.x < b.x + b.size && b.x < a.x + a.size &&
a.y < b.y + b.size && b.y < a.y + a.size;
}
Then add this to the end of update:
if (overlaps(player, coin)) {
score++;
coin.x = Math.random() * (canvas.width - coin.size);
coin.y = Math.random() * (canvas.height - coin.size);
}
…and this to the end of draw:
ctx.fillStyle = "gold";
ctx.fillRect(coin.x, coin.y, coin.size, coin.size);
ctx.fillStyle = "white";
ctx.font = "20px monospace";
ctx.fillText(`Score: ${score}`, 16, 32);
That overlaps check is called AABB collision, and you’ll use it in pretty much every 2D game you ever make.
6. Give it a way to lose. Twenty seconds on the clock, and R to try again:
let timeLeft = 20;
addEventListener("keydown", (e) => {
if (e.code === "KeyR" && timeLeft <= 0) {
score = 0;
timeLeft = 20;
}
});
At the very top of update, so nothing moves once time’s up:
if (timeLeft <= 0) return;
timeLeft -= dt;
And in draw, swap the fillText line for these two:
ctx.fillText(`Score: ${score} Time: ${Math.ceil(timeLeft)}`, 16, 32);
if (timeLeft <= 0) ctx.fillText("Time's up! Press R to play again.", 16, 64);
7. Now stop following along! You’ve got a game: you can play it, score points and lose. It isn’t much fun yet, and what it turns into is all yours. Pick one rule and change it, then see where it takes you:
- What if the coin runs away from you?
- What if every coin makes you faster, or bigger, or harder to steer?
- What if touching something is how you lose, instead of the clock?
- What if you aren’t a square? A slime, a delivery drone, a very small knight…
- What if there’s only one button?
Plenty of great jam games started out exactly like this, with one silly rule someone kept pushing on. If someone makes a game about a raccoon running a corner shop this week, I’m going to play it way more than I should.
Making it feel good
- Juice! Screen shake, particles, a little squash when something lands, a sound on every hit… Watch Juice it or lose it, where two devs take a really plain Breakout clone and turn it into something you can’t put down, one small trick at a time.
- Sound effects. sfxr.me makes retro blips, jumps and explosions in a click, and BeepBox is great for chiptune music.
- Art. Kenney has thousands of free sprites and sounds you can use for anything, and OpenGameArt has loads more (check each licence). If you’re drawing your own pixel art, grabbing a palette from Lospec makes everything look like it belongs together.
- Playtest. Hand it to a friend, don’t explain anything, and just watch. Wherever they get stuck or confused is the next thing to fix!
- Start easy. The first minute should teach the player how your game works. Make it harder after that, not before.
Going further
- Game Programming Patterns (gameprogrammingpatterns.com) is free to read online, by the same person who wrote Crafting Interpreters. The chapters on the game loop and on state are worth it even for tiny games.
- Red Blob Games (redblobgames.com) has the best explanations around of pathfinding, hex grids and generating maps, with interactive diagrams you can play with. Perfect if you’re making a roguelike!
- Text and puzzle games. Twine is made for branching stories, and you can script it as much as you like. PuzzleScript is a tiny language just for block-pushing puzzle games.
- Go tiny. js13k is a yearly jam for web games under 13 KB, and the Lightweight guide has lots of tricks for that.
- Game jams. itch.io/jams always has a few running. A theme and a deadline are a great way to finally finish something!
Before you ship
There’s a few things every project needs before we can approve it:
- It works. It doesn’t have to be finished, but the main thing it does should actually work.
- All of your code is public, on GitHub or somewhere like it.
- Anyone can try it, even someone who doesn’t code, in under two minutes of setup.
- Someone else could rebuild it from your repo and README.
What that looks like depends on where your game lives:
- On the web: a public link that stays up. itch.io is made for this: zip up your game with
index.htmlat the top, and upload it as an HTML5 game. GitHub Pages works great too. Please don’t use Streamlit, and don’t put it behind a login either! Godot and TIC-80 can both export for the web, and pygbag does it for Pygame. - As a desktop game: a GitHub release or an itch.io download with something people can download and run (an
.exe, a.dmg, an.AppImage…). The source code on its own isn’t enough. - If it needs two players: make sure one reviewer on their own can still try it, against a bot or by playing both sides on one keyboard. For online multiplayer, the server needs to be up while we review it.
A gameplay video is an awesome way to show it off, but it can’t be your demo. Put videos on YouTube or Vimeo, or upload an .mp4, rather than Google Drive.
Shipping one
- Demo: a link where we can start playing right away. Tell us the controls! If your game has sound, start with a “click to play” screen, since browsers won’t play audio until someone clicks.
- Screenshot: the middle of a game with stuff happening, rather than the title screen.
- README: how to play, what you made it with, and credits for any art, music or sounds you didn’t make yourself. Feel free to brag about your high score too, and we’ll try to beat it.
