1Game Design Fundamentals
▶
Before a single line of code is written, every successful browser game begins as an idea that has been carefully thought through and documented. Game design fundamentals are the principles that transform a vague concept — "I want to make a game where you dodge things" — into a concrete blueprint that guides every coding decision that follows. Skipping this stage is one of the most common mistakes new developers make; they dive straight into drawing on a canvas or handling keyboard events, only to discover midway through that they aren't sure what winning looks like, or that their rules contradict each other, or that the project has grown far beyond what they can finish. Investing time in design before coding is not a delay — it is the fastest path to a working, enjoyable game.
This topic walks through six interconnected design steps. Each step builds on the previous one, and together they produce a lightweight design document you can refer to throughout development. The examples used throughout this topic center on a classic arcade-style game — a player-controlled spaceship that shoots incoming asteroids — because it is simple enough to illustrate every concept clearly while being complex enough to be realistic.
Defining the Game Objective
The objective is the single most important design decision you will make. It answers the question: what is the player trying to do? A well-formed objective is short, unambiguous, and immediately communicates what success and failure look like. The discipline of forcing yourself to express it in one sentence is not an arbitrary rule — it is a diagnostic tool. If you cannot say it in one sentence, you do not yet understand your own game well enough to build it.
Consider the difference between these two statements:
- Vague: "The player flies a spaceship around and does stuff with asteroids and maybe collects power-ups and tries to get a high score or survive."
- Clear: "The player controls a spaceship and must destroy incoming asteroids by shooting them before any asteroid reaches the bottom of the screen."
The second version immediately tells you what the player controls (a spaceship), what the challenge is (asteroids moving toward the bottom), what the action is (shooting), and what the failure condition is (an asteroid reaches the bottom). From this one sentence you can already infer the entities you need, the win/loss logic, and the primary interaction. That is the power of a precise objective.
A clear objective also acts as a filter throughout development. Every time you think of a new feature — a shield mechanic, a boss enemy, a level system — you ask: does this directly support the objective? If the answer is no, the feature belongs in a "nice-to-have" list, not in your initial build. This prevents scope creep, which is the gradual, unplanned expansion of a project that causes it to never reach a finished state.
Establishing the Rules
Once you know the objective, you define the rules — the specific conditions and constraints that govern how play unfolds. Think of rules as the contract between the game and the player. When rules are consistent and predictable, players can experiment, learn cause and effect, and deliberately form strategies. When rules are inconsistent or unclear, players feel cheated rather than challenged.
Each rule should do one of two things: support the objective (make it achievable) or protect the objective (prevent it from being trivially easy). For the asteroid game, a minimal ruleset might look like this:
| Rule | Type | Purpose |
|---|---|---|
| Asteroids spawn at the top of the screen at random horizontal positions. | Support | Creates the challenge the player must respond to. |
| Asteroids move downward at a constant speed that increases over time. | Protect | Prevents the player from simply waiting and shooting slowly. |
| A bullet that hits an asteroid destroys both the bullet and the asteroid and awards 10 points. | Support | Defines the reward mechanism and the core interaction. |
| If an asteroid reaches the bottom edge of the canvas, the game ends. | Protect | Defines the failure condition tied directly to the objective. |
| The player can fire one bullet at a time; a new bullet cannot be fired until the previous one leaves the screen or hits an asteroid. | Protect | Prevents the player from spamming bullets and trivializing the challenge. |
Notice that this ruleset is small — five rules — and every single rule connects back to the one-sentence objective. A small, focused ruleset has two major advantages: it is faster to implement in code because each rule translates to a relatively isolated block of logic, and it is faster for players to internalize because they do not have to memorize an instruction manual before they can enjoy the game.
When writing rules, watch for contradictions. For example, if one rule says "the game ends when the player is hit by an asteroid" and another says "the player is invincible for three seconds after spawning," you need to make sure these rules do not conflict during the first three seconds of play. Spotting contradictions in plain English is far easier than finding them buried inside hundreds of lines of JavaScript.
Identifying Game Entities
Entities are the distinct objects that exist and act within your game world. Identifying them at the design stage is critical because each entity will later become a JavaScript object or class in your code. The clearer you define an entity's role, properties, and interactions now, the faster and more confidently you can implement it.
For each entity, ask three questions: What does it look like? (its visual representation on the canvas), What does it do? (its behavior over time), and How does it interact with other entities? (the events it triggers when it meets another entity). Here is a breakdown of entities for the asteroid game:
| Entity | Role | Key Properties | Interactions |
|---|---|---|---|
| Player (Spaceship) | Controlled by the player; fires bullets | x, y position; width; height; speed | Moves left/right; spawns bullets; game ends if hit by asteroid |
| Asteroid | Enemy; causes game over if it reaches the bottom | x, y position; radius; speed | Moves downward; destroyed by bullet; triggers game over if it exits canvas |
| Bullet | Projectile fired by player; destroys asteroids | x, y position; speed; active (boolean) | Moves upward; destroyed on contact with asteroid; removed when off-screen |
| Score Display | UI element showing the current score | currentScore (integer) | Updated when an asteroid is destroyed; rendered each frame |
A subtle but important insight in this step: entities that cause the player to lose (asteroids reaching the bottom) and entities that increase the score (asteroids being destroyed by bullets) need particularly careful definition because they directly encode the objective into interactive objects. When you later write collision detection code, you will be translating the interaction column of this table almost word-for-word into conditional logic.
Identifying entities also reveals dependencies. The bullet entity depends on the player entity to know where to spawn. The score display depends on the asteroid entity to know when to increment. Seeing these dependencies before coding helps you organize your code so that data flows in a sensible direction rather than creating tangled, hard-to-debug references.
Sketching the Game Structure
A game is not a single continuous experience — it is a sequence of distinct states, and the structure of your game is the map of those states and how the player moves between them. Most simple browser games share a common set of states:
- Start / Title Screen: The game is loaded but has not yet begun. The player sees a title, instructions, or a prompt to begin.
- Playing: The main gameplay loop is active. Entities are moving, input is being processed, and the score is updating.
- Paused: The game loop is suspended temporarily. Entities freeze in place and input processing is limited to "unpause."
- Game Over: A failure condition has been met. The game loop stops, and the player is shown their final score and an option to restart.
Beyond naming the states, you must explicitly map the transitions — the specific events or conditions that move the game from one state to another. Leaving transitions vague leads to bugs where the game enters an ambiguous in-between state, such as the game loop still running after the game-over screen appears. Here is a transition map for the asteroid game:
| From State | Trigger | To State |
|---|---|---|
| Start | Player presses the Enter key | Playing |
| Playing | Player presses the P key | Paused |
| Paused | Player presses the P key again | Playing |
| Playing | An asteroid reaches the bottom of the canvas | Game Over |
| Game Over | Player presses the R key | Start (entities reset) |
In code, the current game state is typically stored in a single variable — often a string like "playing" or an integer constant — and checked at the top of the game loop and inside event handlers. When you design the state structure explicitly before coding, you know in advance exactly how many branches your event handler needs and what conditions your game loop must check each frame. This makes the code dramatically easier to organize and debug.
Scoping the Project Appropriately
Scope is the boundary around your project that defines what is in and what is out. Proper scoping is the difference between finishing a game and abandoning a half-built prototype. The key principle is to identify the minimum viable game — the smallest set of features that makes the game genuinely playable and enjoyable — and commit to building only that in the first version.
A practical technique is to categorize every feature you can think of into two lists before writing any code:
- Must-Have: Features without which the game does not function or is not recognizable as the intended experience. For the asteroid game: player movement, bullet firing, asteroid spawning and movement, collision detection, score tracking, and game-over detection.
- Nice-to-Have: Features that enhance the experience but are not required for a playable game. For the asteroid game: multiple asteroid sizes, explosion animations, a high-score leaderboard, background music, power-ups, and difficulty levels.
All nice-to-have features go on a backlog list. They are not forgotten — they are deferred. This matters for student developers in particular because the skills being applied — canvas drawing, animation via requestAnimationFrame, and event handling — each introduce their own complexity. A scoped design ensures that the complexity of the game logic and the complexity of the new technical skills do not compound each other into an unmanageable situation. Finish the must-haves first, have a working game, and then selectively pull items from the backlog.
Scope creep most often happens gradually and innocuously. You are coding the bullet mechanism and you think, "it would be really cool if bullets could split into three when they hit a large asteroid." This is a nice-to-have idea, but acting on it immediately means leaving the bullet mechanism half-finished while you design a new splitting mechanic. Write the idea in your backlog and return to the current task. This discipline is a professional habit, not a beginner's constraint.
Planning the Player Interaction Model
The interaction model defines how the player communicates intent to the game — specifically, which physical input events (key presses, mouse clicks, mouse movements) trigger which in-game actions. Designing this explicitly before coding your event listeners prevents a scattered, inconsistent control scheme and ensures that the controls feel natural for the type of game you are making.
The first decision is the choice of input method. This should follow from the objective. A game where precise positioning matters (like a spaceship that moves horizontally) is naturally suited to keyboard arrow keys or A/D keys. A game where the player aims at a target is naturally suited to mouse position. A game where timing a single action matters (like jumping in a platformer) is naturally suited to a single key like the spacebar. Mismatching the input method and the objective creates friction — a mouse-controlled game that requires pixel-perfect quick reactions, for example, will feel clunky regardless of how well the code is written.
Once the input method is chosen, document each specific control and its effect in a control map:
| Input Event | Game State Required | In-Game Action |
|---|---|---|
| ArrowLeft key held down | Playing | Move spaceship left at defined speed |
| ArrowRight key held down | Playing | Move spaceship right at defined speed |
| Spacebar key pressed | Playing | Fire a bullet from the spaceship's position (if no active bullet) |
| Enter key pressed | Start | Transition game state to Playing |
| P key pressed | Playing or Paused | Toggle between Playing and Paused states |
| R key pressed | Game Over | Reset all entities and transition to Start state |
Notice that the "Game State Required" column connects directly to the state structure designed in the previous step. A key press means different things in different states — pressing R during gameplay should do nothing, but pressing it on the game-over screen restarts the game. Without this distinction documented, you will likely discover the ambiguity as a bug rather than designing around it from the start.
Keeping the control scheme simple also has an often-overlooked benefit: it makes your event-handling code cleaner. Every action in the control map becomes a branch in your keydown or keyup event listener. Fewer actions mean fewer branches, which means less code to write, test, and debug. Simple controls are not a sign of an unambitious game — they are a sign of a well-designed one.
Taken together, these six design steps — defining the objective, establishing the rules, identifying entities, sketching the game structure, scoping the project, and planning the interaction model — form a lightweight but powerful design document. You do not need specialized software or formal notation to create it; a plain text file or a few pages of notes is sufficient. What matters is that every major question about your game is answered in plain language before you open a code editor. When you do start coding, you will not be guessing at what to build — you will be translating a clear design into working software, which is a far more manageable and satisfying process.