This guide walks through all the code behind the Alphabetty Number Game.
The entire game lives in a single file called index.html, and it's
built with three languages that work together: HTML,
CSS, and JavaScript.
You can play the game right here before we dig in:
1. The three languages inside every web page
Every web page you've ever visited is built with the same three languages. Each one has a different job:
- HTML (HyperText Markup Language) defines what's on the page — the text, the buttons, the headings, the images. Think of it as the skeleton.
- CSS (Cascading Style Sheets) defines how it looks — colours, sizes, positions, fonts. Think of it as the clothes and make-up.
- JavaScript (often shortened to JS) defines what it does — what happens when you press a key, how the timer counts down, how the score goes up. Think of it as the brain.
In bigger projects, these three languages usually live in separate files. But for a small project like this, we put them all in one file, which makes it easier to see everything in one place.
2. HTML: building the skeleton
HTML works with tags. A tag is a word wrapped in angle
brackets, like <h1> or <div>. Most
tags come in pairs — an opening tag and a closing tag — with content in
between:
<h1>Alphabetty Number Game</h1>
That creates a big heading with the text "Alphabetty Number Game" inside it.
The / in </h1> means "this is the end of the heading."
The three screens
Our game has three screens, but they're all in the same page. We use
<div> tags (short for "division" — just a box that holds
other things) to group each screen's content:
<!-- The start screen -->
<div id="start-screen" class="screen active">
<h1>Alphabetty<br>Number Game</h1>
<p>Type the letter or number you see!<br>Press SPACE to start.</p>
<div class="high-scores" id="start-scores"></div>
</div>
<!-- The game screen -->
<div id="game-screen" class="screen">
<span id="timer">Time: 60s</span>
<span id="score">Score: 0</span>
<div id="character">A</div>
</div>
<!-- The game-over screen -->
<div id="gameover-screen" class="screen">
<h2>Time's Up!</h2>
<div id="final-score">You scored: 0</div>
...
</div>
Notice a few things:
-
Each screen has an
idlike"start-screen"or"game-screen". An id is a unique name — no two things on the page can share the same id. We use these names later in the JavaScript to find and control each screen. -
They all share the
class="screen". A class is like a group label — it says "these things are all screens, so style them the same way." Unlike ids, many elements can share the same class. -
The start screen also has the class
active. That's how we mark which screen is currently visible. Only one screen isactiveat a time. -
The lines starting with
<!--are comments. The browser ignores them completely — they're notes for humans reading the code.
3. CSS: making it look good
CSS is a list of rules. Each rule says: "find the things that match this pattern, and apply these styles to them." Here's one of ours:
body {
background-color: #FFD700;
color: #FF0000;
font-family: 'Fredoka One', cursive;
height: 100vh;
overflow: hidden;
}
Let's break that down:
bodyis the selector — it picks which part of the page this rule applies to.bodyis the entire visible page.background-color: #FFD700sets the background to a bright gold/yellow. The#FFD700is a colour code — a way of describing exact colours using a mix of red, green, and blue.#FF0000is pure red.font-family: 'Fredoka One', cursivesays "use the Fredoka One font, and if that's not available, use any rounded font." Fredoka One is loaded from Google Fonts at the top of the file.height: 100vhmeans "be exactly as tall as the browser window." Thevhunit stands for "viewport height" — 100vh is the full height of whatever screen you're looking at.overflow: hiddenmeans "if anything spills outside the page, don't show scrollbars — just cut it off."
Showing and hiding screens
This is the most important CSS trick in the game:
.screen {
display: none; /* hidden by default */
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
.screen.active {
display: flex; /* visible when active */
flex-direction: column;
align-items: center;
justify-content: center;
}
The first rule says: every element with the class screen should
be hidden (display: none) and stretched to fill the whole page.
The dot in .screen means "look for this class name."
The second rule, .screen.active, says: if a screen also
has the class active, show it. The display: flex
part, together with align-items: center and
justify-content: center, places the screen's content right in
the middle — both horizontally and vertically. That's how the big letter
ends up dead centre.
.screen.active (two classes)
is more specific than .screen (one class), so its
display: flex overrides the display: none.
Positioning the timer and score
#timer {
position: absolute;
top: 1rem;
left: 1.5rem;
font-size: 2rem;
}
#score {
position: absolute;
top: 1rem;
right: 1.5rem;
font-size: 2rem;
}
The # selector targets an id. position: absolute
means "place me at an exact spot, regardless of where other things are."
The timer goes top-and-left; the score goes
top-and-right. They sit in their corners
like a heads-up display in a video game.
Making the letter huge
#character {
font-size: 20vw;
}
vw stands for "viewport width." 20vw means "20% of
the screen's width." So on a 1000-pixel-wide screen, the letter would be 200
pixels tall. On a phone, it shrinks automatically. This is one simple line of
CSS doing a lot of work — the letter looks great on any screen size without us
having to write special rules for phones, tablets, and desktops.
4. JavaScript: making it do things
HTML and CSS are descriptive — they say what things are and how they look. JavaScript is imperative — it tells the computer what to do, step by step. All the action in our game — the timer ticking, the score going up, the letters changing — happens in JavaScript.
JavaScript code lives inside a <script> tag at the bottom
of the HTML file. We put it at the bottom so that by the time the browser
reads the JavaScript, it has already built all the HTML elements the code
needs to find and change.
Our code is organised into sections, and we'll go through each one.
5. Keeping track of the game with variables
A variable is a named box that holds a piece of information. You can put something in the box, look at what's inside, or replace its contents. Here are the variables our game uses:
let currentScreen = 'start';
let currentChar = '';
let currentScore = 0;
let timeRemaining = 60;
let timerInterval = null;
let initialsBuffer = '';
let enteringInitials = false;
The word let creates a new variable. After the =
is the value we're putting in the box to start with. Let's look at each one:
currentScreenremembers which screen we're on:'start','game', or'gameover'.currentCharholds the letter or number the player needs to type right now.currentScoreis how many they've got right — starts at 0.timeRemainingis how many seconds are left on the clock.timerIntervalis a handle to the running countdown (we'll explain this when we get to the timer).initialsBuffercollects the three letters of the player's initials on the high-score screen.enteringInitialsistrueorfalse— it tells us whether the player is currently typing their initials.
There are also some constants — values that never change while the game runs:
const CHARACTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'.split('');
const GAME_DURATION = 60;
const MAX_HIGH_SCORES = 10;
const works like let, except you can't change
its value later. We write constant names in ALL_CAPS as a convention — a
way of signalling to anyone reading the code "this is a fixed setting, not
something that changes during gameplay."
.split('') do?
The string 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' is just text.
Calling .split('') on it chops it into an array
(a list) of individual characters: ['A', 'B', 'C', ... '9'].
We need a list so we can pick a random item from it later.
currentScreen
in a variable, when we could just check which HTML element has the
active class? Either approach would work. But checking a simple
variable is quicker to read and understand than searching through HTML elements.
It's a trade-off: we have to remember to keep the variable in sync with the
actual screen, but the rest of the code becomes cleaner.
6. Switching between screens
Remember how our CSS hides any screen that doesn't have the active
class? Here's the JavaScript function that swaps which screen is active:
function showScreen(name) {
document.querySelectorAll('.screen').forEach(function(el) {
el.classList.remove('active');
});
document.getElementById(name + '-screen').classList.add('active');
currentScreen = name;
}
A function is a reusable recipe. You give it a name (here,
showScreen) and it does the same steps every time you call it.
This one takes one ingredient — a name like 'start'
or 'game' — and does three things:
- Find every screen using
querySelectorAll('.screen')and remove theactiveclass from all of them. Now they're all hidden. - Find the one we want by building its id (e.g.,
'game' + '-screen'='game-screen') and add theactiveclass to it. Now just that one is visible. - Update the variable
currentScreenso the rest of our code knows where we are.
So when we write showScreen('game') anywhere in our code,
the start screen disappears, the game screen appears, and everything stays
in sync. One function, called from several places, doing the same reliable
job each time.
7. Picking a random letter
function pickNewCharacter() {
var candidates = CHARACTERS.filter(function(c) { return c !== currentChar; });
var randomIndex = Math.floor(Math.random() * candidates.length);
return candidates[randomIndex];
}
function showNewCharacter() {
currentChar = pickNewCharacter();
document.getElementById('character').textContent = currentChar;
}
pickNewCharacter does two things:
-
It makes a new list called
candidatesthat contains every character except the one currently on screen. The.filter()method goes through the list and keeps only the items that pass a test — here, the test is "is this character different fromcurrentChar?" -
It picks a random item from that filtered list.
Math.random()gives a random decimal number between 0 and 1 (like 0.73). Multiplying by the list's length and rounding down withMath.floor()turns that into a whole number we can use as a position in the list.
showNewCharacter calls pickNewCharacter, saves the
result in currentChar, and then puts it on screen by setting
the textContent of the big centre element.
8. The countdown timer
The timer needs to do something once every second — subtract 1 from the
time remaining and update the display. JavaScript has a built-in tool for
this called setInterval:
function startTimer() {
stopTimer();
timeRemaining = GAME_DURATION;
updateTimerDisplay();
timerInterval = setInterval(function() {
timeRemaining -= 1;
updateTimerDisplay();
if (timeRemaining <= 0) {
endGame();
}
}, 1000);
}
setInterval(someFunction, 1000) says: "run this function
every 1000 milliseconds (= 1 second)." Every tick, we subtract 1 from
timeRemaining, update the display, and check if time
has run out. If it has, we call endGame().
setInterval returns a number — a handle — that we store
in timerInterval. We need this handle later so we can tell
the browser to stop the timer:
function stopTimer() {
if (timerInterval !== null) {
clearInterval(timerInterval);
timerInterval = null;
}
}
clearInterval cancels the repeating timer.
We also reset timerInterval to null (which
means "nothing") so we know no timer is running.
stopTimer() at the start of startTimer()?
It's a safety measure. If a timer were somehow already running when we start
a new game, we'd end up with two timers ticking at once — the
clock would count down twice as fast! Stopping any existing timer first
prevents that. This kind of careful thinking — "what if something unexpected
is already happening?" — is an important habit in programming.
9. Listening for key presses
The game needs to respond when the player presses a key. We do this by setting up an event listener — we tell the browser "whenever a key is pressed, run this function":
document.addEventListener('keydown', function(event) {
if (currentScreen === 'start') {
if (event.key === ' ') {
startGame();
}
} else if (currentScreen === 'game') {
if (event.key.length > 1) return;
var pressedKey = event.key.toUpperCase();
if (pressedKey === currentChar) {
currentScore += 1;
updateScoreDisplay();
showNewCharacter();
}
} else if (currentScreen === 'gameover') {
// ... handle initials or restart
}
});
This is the longest function in the game, but it follows a simple pattern. It checks which screen we're on, then decides what to do:
- Start screen: only the space bar starts the game. We check
event.key === ' '(a space character). -
Game screen: first, we skip keys that aren't single
characters (like Shift, Ctrl, or the arrow keys — these all have names
longer than 1 character, which is why we check
event.key.length > 1). Then we compare the pressed key (converted to uppercase with.toUpperCase()) tocurrentChar. If they match, we add a point and show a new character. - Game-over screen: if the player is entering their initials, we collect letter keys and handle Backspace and Enter. Otherwise, we wait for the space bar to restart.
10. Saving high scores
Normally, when you close a web page, all the data disappears. But browsers
have a feature called localStorage that lets a page save small
amounts of data that sticks around — even if you close the browser and come
back later. We use it to store the high-score table.
localStorage can only store text (strings), not complex data
structures. So we need to convert our scores back and forth:
// Saving: turn the list of scores into a text string
localStorage.setItem('alphabettyHighScores', JSON.stringify(scores));
// Loading: turn the text string back into a list
var scores = JSON.parse(localStorage.getItem('alphabettyHighScores'));
JSON.stringify takes a JavaScript object or array and converts
it to a text string. JSON.parse does the opposite — it reads
a text string and rebuilds the object. JSON stands for JavaScript Object
Notation, and it's one of the most common ways to store and exchange data
on the web.
Checking for a high score
function isHighScore(newScore) {
var scores = loadHighScores();
if (scores.length < MAX_HIGH_SCORES) return true;
return newScore > scores[scores.length - 1].score;
}
This function asks: "does this score deserve a spot on the table?" There are two ways to qualify:
- The table isn't full yet (fewer than 10 entries) — everyone gets in!
- The table is full, but this score is higher than the lowest one on the table.
Saving and sorting
function saveHighScore(initials, newScore) {
var scores = loadHighScores();
scores.push({
initials: initials,
score: newScore,
date: new Date().toLocaleDateString()
});
scores.sort(function(a, b) { return b.score - a.score; });
scores = scores.slice(0, MAX_HIGH_SCORES);
localStorage.setItem(HIGH_SCORE_KEY, JSON.stringify(scores));
return scores;
}
This function adds the new score to the list, sorts the list from highest
to lowest, trims it to the top 10, and saves it. The .sort()
function uses b.score - a.score to put bigger numbers first —
if the result is positive, b comes before a.
11. How it all fits together
Here's the flow of the entire game, from start to finish:
Let's trace through the code for each step:
-
Page loads. The browser reads the HTML and builds the three
screens (all hidden except the start screen). It reads the CSS and applies
the styles. It runs the JavaScript, which calls the
initfunction. That function loads any saved high scores fromlocalStorageand displays them on the start screen. -
Player presses SPACE. The
keydownevent listener fires. It sees we're on the'start'screen and the key is a space, so it callsstartGame(). -
startGame()resets the score to 0, switches to the game screen, picks a random character, and starts the timer. -
Player types letters. Each key press triggers the event
listener. It compares the pressed key to
currentChar. If they match: score goes up, new character appears. If not: nothing happens. -
Timer ticks. Every second, the
setIntervalfunction subtracts 1 fromtimeRemainingand updates the display. -
Timer hits 0. The interval function calls
endGame(), which stops the timer, shows the final score, and either prompts for initials (if it's a high score) or shows the score table directly. - Player presses SPACE again. Back to step 2 — the whole cycle repeats.
That's the whole game. About 300 lines of code — and most of that is CSS styling and HTML structure. The actual game logic is maybe 100 lines of JavaScript. Simple games don't need complicated code.
12. Things to try yourself
The best way to learn is to change things and see what happens. Here are some ideas — start easy and work up:
-
Change the colours. Find
#FFD700(yellow) and#FF0000(red) in the CSS and replace them. Try#00FF00(green) or#4444FF(blue). What happens if you make the text and background the same colour? -
Change the time limit. Find
GAME_DURATION = 60and change it to 30 for a faster game, or 120 for a longer one. -
Letters only. Change the
CHARACTERSstring to'ABCDEFGHIJKLMNOPQRSTUVWXYZ'— no numbers. What about numbers only? -
Add a sound. Can you play a short beep when the player
gets one right? (Hint: search for "JavaScript play sound" — the
Audioobject is simpler than you might think.) - Wrong key feedback. Right now, pressing the wrong key does nothing. Could you briefly flash the screen or change the letter's colour to show the mistake?
- Difficulty levels. What if the timer started at 30 seconds for hard mode and 90 seconds for easy mode? You'd need a way for the player to choose before the game starts.